项目的部分
5.1.项目高并发处理方案
1.线程和进程的区别:
答:(1)进程是一个 “执行中的程序”,是系统进行资源分配和调度的一个独立单位;
(2)线程是进程的一个实体,一个进程中拥有多个线程,线程之间共享地址空间和其它资源(所以通信和同步等操作线程比进程更加容易);
(3).线程上下文的切换比进程上下文切换要快很多。
①进程切换时,涉及到当前进程的 CPU 环境的保存和新被调度运行进程的 CPU 环境的设置。
②线程切换仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。
或者所有,任何形式的转载都请联系作者获得授权并注明出处
2.什么是阻塞(Blocking)和非阻塞(Non-Blocking)?
答:阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程占用了临界区资源,那么其他所有需要这个而资源的线程就必须在这个临界区中进行等待。等待会导致线程挂起,这种情况就是阻塞。此时,如果占用资源的线程一直不愿意释放资源,那么其他所有阻塞在这个临界区上的线程都不能工作。
非阻塞的意思与之相反,它强调没有一个线程可以妨碍其他线程执行。所有的线程都会尝试不断前向执行。
3.Java 中线程有几种状态?
答:六种(查看 Java 源码也可以看到是 6 种),并且某个时刻 Java 线程只能处于其中的一个状态。
1).新建(NEW)状态:表示新创建了一个线程对象,而此时线程并没有开始执行。
2).可运行(RUNNABLE)状态:线程对象创建后,其它线程(比如 main 线程)调用了该对象的 start() 方法,才表示线程开始执行。当线程执行时,处于 RUNNBALE 状态,表示线程所需的一切资源都已经准备好了。该状态的线程位于可运行线程池中,等待被线程调度选中,获取 cpu 的使用权。
3).阻塞(BLOCKED)状态:如果线程在执行过程终于到了 synchronized 同步块,就会进入 BLOCKED 阻塞状态,这时线程就会暂停执行,直到获得请求的锁。
4).等待(WAITING)状态:当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。在调用Object.wait方法或Thread.join方法,或者是等待java.util.concurrent库中的Lock或Condition时,就会出现这种情况;
5).计时等待(TIMED_WAITING)状态:Object.wait、Thread.join、Lock.tryLock和Condition.await 等方法有超时参数,还有 Thread.sleep 方法、LockSupport.parkNanos 方法和 LockSupport.parkUntil 方法,这些方法会导致线程进入计时等待状态,如果超时或者出现通知,都会切换会可运行状态;
6).终止(TERMINATED)状态:当线程执行完毕,则进入该状态,表示结束。
注意:从 NEW 状态出发后,线程不能再回到 NEW 状态,同理,处于 TERMINATED 状态的线程也不能再回到RUNNABLE 状态
4.sleep( ) 和 wait( n)、wait( ) 的区别:
答:①sleep 方法:是 Thread 类的静态方法,当前线程将睡眠 n 毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。睡眠不释放锁(如果有的话);
②wait 方法:是 Object 的方法,必须与 synchronized 关键字一起使用,线程进入阻塞状态,当 notify 或者notifyall 被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,释放互斥锁;
5.synchronized 关键字:
答:底层实现:
1). 进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1;
2). 当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。
含义:(monitor 机制)
Synchronized 是在加锁,加对象锁。对象锁是一种重量锁(monitor),synchronized 的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问 synchronized 区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。
6.什么是进程
答:进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间)。
比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。当用户再次点击左边的IE浏览器,又启动了一个进程,操作系统将为新的进程分配新的独立的地址空间。目前操作系统都支持多进程。
8.什么是线程
答:进程是表示自愿分配的基本单位。而线程则是进程中执行运算的最小单位,即执行处理机调度的基本单位。通俗来讲:一个程序有一个进程,而一个进程可以有多个线程。
9.多线程的几种实现方式
答:(1) 继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法将启动一个新线程,并执行run()方法。这种方式实现多线程比较简单,通过自己的类直接继承Thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法。
(2) 实现Runnable接口创建线程
如果自己的类已经继承了两一个类,就无法再继承Thread,因此可以实现一个Runnable接口
(3) 实现Callable接口通过FutureTask包装器来创建Thread线程
(4) 使用ExecutorService、Callable、Future实现有返回结果的线程
ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。
可返回值的任务必须实现Callable接口;无返回值的任务必须实现Runnabel接口。
执行Callable任务后,可以获取一个Future对象,在该对象上调用get()方法就可以获取到Callable任务返回的Object了。(get()方法是阻塞的,线程无返回结果,该方法就一直等待)
10.Vector、SimpleDateFormat是线程安全类吗
答:Vector类的单个方法是原子性的,符合操作不是原子性的
SimpleDateFormat类不是线程安全的
11.哪些集合类是线程安全的
答:①Vector;②Stack;③hashtable;④enumeration;⑤StringBuffer
12.多线程中忙循环是什么
答:忙循环就是程序员用循环让一个线程等待,不像传统方法wait()、sleep()或者yied()它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么做的目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另一个内核运行,这样会重建缓存。为了避免重建缓存和减少等待重建的时间就可以使用它了。
13.什么是线程局部变量
答:ThreadLocal并非是一个线程本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更合适。线程局部变量(ThreadLocal)功能非常简单,就是为每一个使用该变量的线程都提供了一个变量值副本,是java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
14.什么是多线程环境下的伪共存(false sharing)
答:缓存系统中是以缓存行为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存航,就会无意中影响彼此的性能,这就是伪共存
15.同步和异步有何不同,在什么情况下分别使用它们?举例说明
答:如果数据将在线程间共享。例如:正在写的数据以后可能会被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效。
同步交互:指发送一个请求,需要等待返回,然后才能发送下一个请求,有个等待的过程
异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。
区别:一个需要等待,一个不需要等待
16.Thread类中的strat()和run()方法有什么区别?
答:start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会在原来的线程中调用,没有新的线程启动,而调用start()方法会启动一个新的线程。
17.java中Runnable和Callable的区别
答:Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。
他们的主要区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。
18.什么是线程安全?
答:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果都是一样的,而且其他变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。
19.如何在两个线程间共享数据?
答:可以通过共享对象来实现这个目的,或者是使用阻塞队列,或者使用wait()和notify()方法
20.锁池和等待池
答:锁池:假设线程A已经拥有了某个对象的锁,而其它的线程想要调用这个对象的某个Synchronized方法(或者Synchronized代码块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这个线程就进入了该对象的锁池中
等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到该对象的等待池中。
21.什么是FutureTask?
答:在java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能返回,如果运算尚未完成,get()方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnbale接口所以它可以提交给Executor来执行
22.为什么你应该在循环中检查等待条件
答:处于等待状态的线程可能会受到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下推出。因此,当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用之后和等待线程醒来之前这段时间它可能会改变。这就是在循环中使用wait()方法效果更好的原因。
23. java中堆和栈有什么不同?
答:为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量、方法参数和栈调用,一个线程中存储的变量对其他线程是不可见的。而堆是所有线程共享的一片公共内存区域。对象都在堆里创建,为了提升效率,线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时volatile变量就可以发挥作用了,它要求线程从主存中读取变量的值
24.什么是线程池?为什么要使用它?
答:创建线程需要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变成,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,他们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,java API提供了Executor框架让你可以创建不同的线程池。比如单线程吃,数目固定的线程池等
25.有三个线程T1、T2和T3,怎么确保它们按照顺序执行?
答:在多线程中有多重方法让线程按特定的顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成
26.什么是阻塞式方式?
答:阻塞式方式是指程序会一直等待该方法完成期间不做其他事情,ServiceSocket的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到等到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。
5.2 数据传输的安全性解决方案
1.接口与接口之间的调用,如何保证数据安全
答:①通信使用https
②请求签名,防止参数被篡改
③身份确认机制,每次请求都要验证是否合法
④app中使用ssl pinning 防止抓包操作
⑤对所有请求和响应都进行加解密操作
ssl pinning:它是在开发时就将服务端证书(ssl证书)一块打包到客户端里。这样在HTTPS建立时与服务端返回的证书比对一致性,进而识别出中间人攻击后直接在客户端侧中止连接。
在实际的工作中,情况差不多为以下(分两种):
1)公司内部的接口
公司内部的接口,当然是涉及到比较隐秘信息的时候,调用方需要持有一个私钥,调用的时候将传入的参数通过私钥进行的加密,若加密后的内容能够被公钥解密,那么则能够通过。
2)调用第三方的接口
在调用第三方的接口的时候,实现的方式也有两种:
1、利用私钥加密。
2、利用http的请求头的要求数据和格式展开访问控制,如:在调用第三方接口的时候,需要head中的内容必须是:token:xxx,sign:xxx,client_id:xxx,且sign是经过加密的内容。从而达到访问控制。
2.HTTPS的优点
答:尽管HTTPS并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的攻击,但HTTPS仍是现行架构下最安全的解决方案,主要有以下几个好处:
①使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
②HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
③HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。
④谷歌曾在2014年8月份调整搜索引擎算法,并称“比起同等HTTP网站,采用HTTPS加密的网站在搜索结果中的排名将会更高”。
3.HTTPS的缺点
答:虽然说HTTPS有很大的优势,但其相对来说,还是存在不足之处的:
①HTTPS协议握手阶段比较费时,会使页面的加载时间延长近50%,增加10%到20%的耗电;
②HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗,甚至已有的安全措施也会因此而受到影响;
③SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。
④HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。最关键的,SSL证书的信用链体系并不安全,特别是在某些国家可以控制CA根证书的情况下,中间人攻击一样可行。
4.HTTP与HTTPS有什么区别?
答:HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1).https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2).http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3).http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4).http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
5.3 redis作缓存(即门户模块)
1. Redis有哪些数据结构?
答:字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。
如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。
如果你说还玩过Redis Module,像BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。
2. 使用过Redis分布式锁么,它是什么回事?
答:先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!
3. Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
答:使用keys指令可以扫出指定模式的key列表。
对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
4. 使用过Redis做异步队列么,你是怎么用的?
答:一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。
5. 如果有大量的key需要设置同一时间过期,一般需要注意什么?
答:如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。
6. Redis如何做持久化的?
答:bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,优先使用aof来恢复内存的状态,如果没有aof日志,就会使用rdb文件来恢复。
如果再问aof文件过大恢复时间过长怎么办?你告诉面试官,Redis会定期做aof重写,压缩aof文件日志大小。如果面试官不够满意,再拿出杀手锏答案,Redis4.0之后有了混合持久化的功能,将bgsave的全量和aof的增量做了融合处理,这样既保证了恢复的效率又兼顾了数据的安全性。这个功能甚至很多面试官都不知道,他们肯定会对你刮目相看。
如果对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。
7. 使用redis的目的是什么?
答:项目中使用redis一般都是作为缓存来使用的,缓存的目的就是为了减轻数据库的压力提高存取的效率。
8. Redis的同步机制了解么?
答:从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
9. 是否使用过Redis集群,集群的原理是什么?
答:Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
10. 使用Redis有哪些好处?
答:(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
11. redis相比memcached有哪些优势?
答:(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
(2) redis的速度比memcached快很多
(3) redis可以持久化其数据
12. redis常见性能问题和解决方案:
答:(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3… 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。 13. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据
答:相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:
voltile-lru:从已设置过期时间的数据集(server.db.expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db.expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db.expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db.dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db.dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
14. Memcache与Redis的区别都有哪些?
答:1)、存储方式
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部份存在硬盘上,这样能保证数据的持久性。
2)、数据支持类型
Memcache对数据类型支持相对简单。
Redis有复杂的数据类型。
3)、使用底层模型不同
它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。
Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4).value大小:redis最大可以达到1GB,而memcache只有1MB
15. Redis 常见的性能问题都有哪些?如何解决?
答:1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
16, redis 最适合的场景
答:Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?
如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:
1).Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
2).Redis支持数据的备份,即master-slave模式的数据备份。
3).Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
(1)会话缓存(Session Cache)
最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
(2)全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
(3)队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。
如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。
(4)排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。
(5)发布/订阅
最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。
Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。
17、Redis集群方案应该怎么做?都有哪些方案?
答:1).codis。
目前用的最多的集群方案,基本和twemproxy一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新hash节点。
2).redis cluster3.0自带的集群,特点在于他的分布式算法不是一致性hash,而是hash槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。
3).在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key 进行hash计算,然后去对应的redis实例操作数据。 这种方式对hash层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。
18、Redis集群方案什么情况下会导致整个集群不可用?
答:有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。
19.redis快速的原因
答:①完全基于内存,大部分请求都是纯粹的内存操作,非常快速,数据存储在内存中,例如HashMap,优势是查找和操作的时间复杂度 都是O(1);
②数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
③采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或多线程导致的切换而消耗cpu,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
④使用异步非阻塞IO;(重点说)(同步/异步:是否主动读写数据;阻塞/非阻塞:是否需要等待.)
⑤底层自己构建了VM机制,因为一般系统调用系统函数的话会浪费一定的时间去移动和请求。
20.工作中Redis的持久化方案,你们是怎么做的
答:持久化方案用的是RDB;RDB 是 Redis 默认的持久化方案。
在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。
21.为啥用redis做缓存,而不用HashMap做缓存
答:1).redis 数据可持久化保存,有些缓存你想重启程序后还能继续使用,而map实现不了
2).redis 可以实现分布式部署,只要涉及到多台多进程啥的,map就实现不了
3).redis 有很多数据结构,方便操作,比如 hash set list sort-set等,在某些场景下操作起来比map方便。
22.Redis 是 nosql 数据库,是否适合存储大数据
答:Redis 是 nosql 数据库,但是 redis 是 key-value 形式的 nosql 数据库,数据是存储到内存中的,适合于快速存取,一般作为缓存使用。
并且 redis 是单线程的如果某个操作进行大数据的存储的话其他的进程都处于等待状态,这样就降低了性能。
所以在 redis 中不适合于大数据的存储。
如果是类似商品评论这样的价值不高的大批量数据,我们的做法是采用 mongodb。
23.穿透_雪崩_Redis缓存穿透和redis雪崩处理
答:(1)缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,应该去后端系统查询(比如数据库)。如果key对应的value不存在,并且对key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
(2)缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如数据库)带来很大的压力。
缓存雪崩是指在我们设置缓存是采用了相同的过期时间,导致缓存在某一时期同时失效,请求全部转发到DB,DB瞬间压力过大雪崩。
解决方案
把缓存时间分散开,比如在原来的时间基础上增加一个随机值,比如1~5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
24.集群_redis怎么做集群
答:为了保证可以进行投票,需要至少3个主节点。每个主节点都需要至少一个从节点,所以需要至少3个从节点,一共需要6台redis服务器可以使用6个redis实例6个redis实例的端口号,7001~7006,因为集群中每个redis除了端口号,其他配置其实一样,所以我们可以先配置好一个集群的redis,然后赋值,修改端口号即可。6台准备完毕之后,需要开启集群策略,然后再这基础上创建集群环境。
25.删除_你们怎么处理redis缓存的数据,怎么删除的
答:redis缓存的数据有一些是常驻缓存的,当数据库中数据有变化时做数据同步。
有一些缓存是设置有效期的,当缓存到期后会自动删除。删除redis缓存使用del或者hdel命令。[备注:hdel,hashKey delete]
26.redis的事务
答:(1).redis事务:可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞.
(2).redis事务能干嘛:一个队列中,一次性、顺序性、排他性的执行一系列命令.
[备注: ACID,原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)]
(3).redis监控:锁的介绍
在MySQL中我们都知道有行锁和表锁的概念,所谓的行锁也就是把我需要改的那一行给锁住,不让其他的事务去修改;而表锁就是在修改一张表的时候把整张表都锁住,不让其他的事务修改,所以行锁的效率比表锁的概念更高,那么在redis也存在锁的概念
①乐观锁(Optimistic Lock): 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新
②悲观锁(Pessimistic Lock): 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.[备注: 在Java中,synchronized的思想也是悲观锁]
③CAS(Check And Set):
(4)redis事务三阶段:
①开启:以MULTI开始一个事务
②入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
③执行:由EXEC命令触发事务(execute,执行)
(5).redis事务三大特性:
①单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
②没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
③不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
5.4. ES(搜索模块)
1.什么是ES?
答:es是一个高扩展、开源的全文检索和分析引擎,它可以准实时地快速存储、搜索、分析海量的数据。
2.为什么要使用到ES?
答:因为在我们商城中的数据,将来会非常多,所以采用以往的模糊查询,模糊查询前置配置,会放弃索引,导致商品查询是全表扫面,在百万级别的数据库中,效率非常低下,而我们使用ES做一个全文索引,我们将经常查询的商品的某些字段,比如说商品名,描述、价格还有id这些字段我们放入我们索引库里,可以提高查询速度。
3.Elasticsearch是如何实现Master选举的?
答:①Elasticsearch的选主是ZenDiscovery模块负责的,主要包含Ping(节点之间通过这个RPC来发现彼此)和Unicast(单播模块包含一个主机列表以控制哪些节点需要ping通)这两部分;
②对所有可以成为master的节点(node.master: true)根据nodeId字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。
③如果对某个节点的投票数达到一定的值(可以成为master节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。
补充:master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data节点可以关闭http功能。
4.Elasticsearch中的节点(比如共20个),其中的10个选了一个master,另外10个选了另一个master,怎么办?
答:1)当集群master候选数量不小于3个时,可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes)超过所有候选节点一半以上来解决脑裂问题;
2)当候选数量为两个时,只能修改为唯一的一个master候选,其他作为data节点,避免脑裂问题。
5.客户端在和集群连接时,如何选择特定的节点执行请求的?
答:TransportClient利用transport模块远程连接一个elasticsearch集群。它并不加入到集群中,只是简单的获得一个或者多个初始化的transport地址,并以 轮询 的方式与这些地址进行通信。
6.详细描述一下Elasticsearch索引文档的过程。
答:协调节点默认使用文档ID参与计算(也支持通过routing),以便为路由提供合适的分片。
shard = hash(document_id) % (num_of_primary_shards)
①当分片所在的节点接收到来自协调节点的请求后,会将请求写入到Memory Buffer,然后定时(默认是每隔1秒)写入到Filesystem Cache,这个从Momery Buffer到Filesystem Cache的过程就叫做refresh;
②当然在某些情况下,存在Momery Buffer和Filesystem Cache的数据可能会丢失,ES是通过translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到translog中,当Filesystem cache中的数据写入到磁盘中时,才会清除掉,这个过程叫做flush;
③在flush过程中,内存中的缓冲将被清除,内容被写入一个新段,段的fsync将创建一个新的提交点,并将内容刷新到磁盘,旧的translog将被删除并开始一个新的translog。
④flush触发的时机是定时触发(默认30分钟)或者translog变得太大(默认为512M)时;
补充:关于Lucene的Segement:
1).Lucene索引是由多个段组成,段本身是一个功能齐全的倒排索引。
2).段是不可变的,允许Lucene将新的文档增量地添加到索引中,而不用从头重建索引。
3)对于每一个搜索请求而言,索引中的所有段都会被搜索,并且每个段会消耗CPU的时钟周、文件句柄和内存。这意味着段的数量越多,搜索性能会越低。
4).为了解决这个问题,Elasticsearch会合并小段到一个较大的段,提交新的合并段到磁盘,并删除那些旧的小段。
7.详细描述一下Elasticsearch更新和删除文档的过程。
答:①删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;
②磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。
③在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。
8.详细描述一下Elasticsearch搜索的过程。
答:①搜索被执行成一个两阶段过程,我们称之为 Query Then Fetch;
②在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache的,但是有部分数据还在Memory Buffer,所以搜索是近实时的。
③每个分片返回各自优先队列中 所有文档的 ID 和排序值 给协调节点,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
④接下来就是 取回阶段,协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。
补充:
Query Then Fetch的搜索类型在文档相关性打分的时候参考的是本分片的数据,这样在文档数量较少的时候可能不够准确,DFS Query Then Fetch增加了一个预查询的处理,询问Term和Document frequency,这个评分更准确,但是性能会变差。
9.为什么使用索引工具查询快
答:(使用了倒排索引的技术,大致介绍一下倒排索引,还有索引库中的词都是按照顺序排列,后期根据一个关键词查询的时候,可以利用类似折半查找的算法,查询效率非常高)
使用了倒排索引的技术,一般我们都是这样定义id 关键词,倒排索引是关键词 id正好相反,使用索引工具进行查询时,首先得到关键词,建立倒排索引表,关键词----索引列表包含该关键词所在的文档的id、在该文档中出现的次数、在该文档中出现的位置信息,这种由属性值确定记录的位置的方式成为倒排索引。还有索引库中的词都是按照顺序排列 ,后期根据一个关键词查询的时候,可以利用类似折半查找的算法,查询效率非常高;
10.es集群的脑裂问题
答:es集群有可能会出现脑裂问题,原因主要有两个:
1)如果集群中节点不在同一个网段有可能是网络延迟造成的
2)如果集群中的节点在同一个网段,有可能是主节点负载太大造成的
解决方案主要有两种:
①把主从节点的职责分离,设置三个储备主节点,node.master=true,node.data=false,从节点只存储数据,node.master=false,node.data=true
②增加延迟时间
将储备主节点数最小设为n/2+1个
11.详细描述一下Elasticsearch更新和删除文档的过程。
答:①删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;
②磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。
③在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。
12.在并发情况下,Elasticsearch如果保证读写一致?
答:可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;
另外对于写操作,一致性级别支持quorum/one/all,默认为quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。
对于读操作,可以设置replication为sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication为async时,也可以通过设置搜索请求参数_preference为primary来查询主分片,确保文档是最新版本。
13.如何监控Elasticsearch集群状态?
答:Marvel 让你可以很简单的通过 Kibana 监控 Elasticsearch。你可以实时查看你的集群健康状态和性能,也可以分析过去的集群、索引和节点指标。
14.Elasticsearch中的倒排索引是什么?
答:倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。倒排索引是一种像数据结构一样的散列图,可将用户从单词导向文档或网页。它是搜索引擎的核心。其主要目标是快速搜索从数百万文件中查找数据。
15.ElasticSearch中的分片是什么?
答:在大多数环境中,每个节点都在单独的盒子或虚拟机上运行。
1).索引 - 在Elasticsearch中,索引是文档的集合。
2).分片 -因为Elasticsearch是一个分布式搜索引擎,所以索引通常被分割成分布在多个节点上的被称为分片的元素。
16.ElasticSearch中的副本是什么?
答:一个索引被分解成碎片以便于分发和扩展。副本是分片的副本。一个节点是一个属于一个集群的ElasticSearch的运行实例。一个集群由一个或多个共享相同集群名称的节点组成。
17.ElasticSearch中的分析器是什么?
答:在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。 分析器由一个Tokenizer和零个或多个TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称下注册分析器,然后可以在映射定义或某些API中引用它们。
Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分析器。
18.什么是ElasticSearch中的编译器?
答:编译器用于将字符串分解为术语或标记流。一个简单的编译器可能会将字符串拆分为任何遇到空格或标点的地方。Elasticsearch有许多内置标记器,可用于构建自定义分析器。
19.Elasticsearch是如何实现高亮?
答:使用高亮碎片.先创建高亮字段碎片HighlightBuilder.Field对象.然后设置高亮字段Field进行标签设置(preTags和postTags),最后设置文本前后字节数,fragmentSize
20.如何实现搜索排名
答:当你创建了SearchQuery对象后,你可以对任意字段进行排序,里面的addSort方法,在给他一个Sort对象即可,如果在嵌套域查询中.可设置得分情况,在QueryBuilder中的nesterQuery中可设置得分情况.
|