做为Java程序员,在面试过程当中,缓存相关的问题是躲不掉的,确定会问,例如缓存一致性问题,缓存雪崩、击穿、穿透等。说到缓存,那确定少不了Redis,我在面试的时候也是被问了不少关于Redis相关的知识,可是Redis的功能太强大了,并非一时半会儿能掌握好的,由于有些高级特性或是知识平时并不会用到。
因此回答的很差,人家就会以为你对本身平时使用的工具都没有了解,天然就凉凉了。其实很早就有这个打算,打算好好总结一下Redis的知识,但也是因为本身都没有好好的了解Redis呢,因此一直没有开始。此次准备慢慢的来总结。程序员
Redis是一个由C语言编写的开源的,基于内存,支持多种数据结构可持久化的NoSQL数据库。
它速度快主要是有如下几个缘由:面试
官网上给出单台Redis的能够达到10w+的QPS的, 一台服务器上在使用Redis的时候单核的就够了,可是目前服务器都是多核CPU,要想不浪费资源,又能提交效率,能够在一台服务器上部署多个Redis实例。redis
虽然单台Redis的的性能很好,可是Redis的单节点并不能保证它不会挂了啊,毕竟单节点的Redis是有上限的,并且人家单节点又要读又要写,小身板扛不住咋办,因此为了保证高可用,通常都是作成集群。算法
Redis官方是支持主从同步的,并且还支持从从同步,从从同步也能够理解为主从同步,只不过从从同步的主节点是另外一个主从的从节点。
有了主从同步的集群,那么主节点就负责提供写操做,而从节点就负责支持读操做。数据库
若是Slave(从节点)是第一次跟Master进行链接,数组
psync
;这里有一点须要注意的就是,主节点的缓存区是有限的,内部结构是一个环形数组,当数组被占满以后就会覆盖掉最先以前的数据。缓存
因此若是因为网络或是其余缘由,形成缓存区中的数据被覆盖了,那么当从节点处理完主节点的RDB文件后,就不得不又要进行一全量的RDB文件的复制,才能保证主从节点的数据一致。安全
若是不设置好合理的buffer区空间,是会形成一个RDB复制的死循环。bash
当主从间的数据同步完成以后,后面主节点的每次写操做就都会同步到从节点,这样进行增量同步了。服务器
因为负载的不断上升就致使了主从之间的延时变大,因此就有了上面我说的从从同步了,主节点先同步到一部分从节点,而后由从节点去同步其余的从节点。
在Redis从2.8.18开始支持无盘复制,主节点经过套接字,一边遍历内存中的数据,一边让数据发送给从节点,从节点和以前同样,先将数据存储在磁盘文件中,而后再一次性加载。
另外因为主从同步是异步的,因此从Redis3.0以后出现了同步复制,就是经过wait命令来进行控制,wait命令有两个参数,第一个是从库数量,第二个是等待从库的复制时间,若是第二个参数设置为0,那么就是表明要等待全部从库都复制完才去执行后面的命令。
可是这样就会存在一个隐患,当网络异常后,wait命令会一直阻塞下去,致使Redis不可用。
哨兵能够监控Redis集群的健康状态,当主节点挂掉以后,选举出新的主节点。客户端在使用Redis的时候会先经过Sentinel来获取主节点地址,而后再经过主节点来进行数据交互。当主节点挂掉以后,客户端会再次向Sentinel获取主节点,这样客户端就能够无感知的继续使用了。
哨兵集群工做过程,主节点挂掉以后会选举出新的主节点,而后监控挂掉的节点,当挂掉的节点恢复后,原先的主节点就会变成从节点,重新的主节点那里创建主从关系。
Redis Cluster是Redis官方推荐的集群模式,Redis Cluster将全部数据划分到16384个槽(slots
)中,每一个节点负责一部分槽位的读写操做。
Redis Cluster默认是经过CRC16算法获取到key的hash值,而后再对16384进行取余(CRC16(key)%16384
),获取到的槽位在哪一个节点负责的范围内(这里通常是会有一个槽位和节点的映射表来进行快速定位节点的,一般使用bitmap来实现),就存储在哪一个节点上。
当Redis Cluster的客户端在和集群创建链接的时候,也会得到一份槽位和节点的配置关系(槽位和节点的映射表),这样当客户端要查找某个key时,能够直接定位到目标节点。
可是当客户端发送请求时,若是接收请求的节点发现该数据的槽位并不在当前节点上,那么会返回MOVED
指令将正确的槽位和节点信息返回给客户端,客户接着请求正确的节点获取数据。
通常客户端在接收到MOVED
指令后,也会更新本身本地的槽位和节点的映射表,这样下次获取数据时就能够直接命中了。这整个重定向的过程对客户端是透明的。
当集群中新增节点或删除节点后,节点间的数据迁移是按槽位为单位的,一个槽位一个槽位的迁移,当迁移时原节点状态处于:magrating
,目标节点处于:importing
。
在迁移过程当中,客户端首先访问旧节点,若是数据还在旧节点,那么旧节点正常处理,若是不在旧节点,就会返回一个-ASK + 目标节点地址
的指令,客户端收到这个-ASK
指令后,向目标节点执行一个asking
指令(告诉新节点,必须处理客户端这个数据),而后再向目标节点执行客户端的访问数据的指令。
Redis Cluster能够为每一个主节点设置多个从节点,当单个主节点挂掉后,集群会自动将其中某个从节点提高为主节点,若没有从节点,那么集群将处于不可用状态。
Redis提供了一个参数:cluster-require-full-coverage
,用来配置能够容许部分节点出问题后,还有其余节点在运行时能够正常提供服务。
另一点比较特殊的是,Cluster中当一个节点发现某个其余节点出现失联了,这个时候问题节点只是PFail
(Possibly
-可能下线),而后它会把这个失联信息广播给其余节点,当一个节点接收到某个节点的失联信息达到集群的大多数时,就能够将失联节点标记为下线,而后将下线信息广播给其余节点。若失联节点为主节点,那么将当即对该节点进行主从切换。
Redis高可用就先说到这里吧,后面其实还有Codis,可是目前Cluster逐渐流行起来了,Codis的竞争力逐渐被蚕食,并且对新版本的支持,更新的也比较慢,因此这里就不说它了,感兴趣的能够本身去了解一下,国人开源的Redis集群模式Codis。
Redis持久化的意义在于,当出现宕机问题后,能将数据恢复到缓存中,它提供了两种持久化机制:一种是快照(RDB),一种是AOF日志。
快照是一次全量备份,而AOF是增量备份。快照是内存数据的二进制序列化形式,存储上很是紧凑,而AOF日志记录的是内存数据修改的指令记录文本。
由于Redis是单线程的,因此在作快照持久化的时候,一般有两个选择,save命令,会阻塞线程,直到备份完成;bgsave会异步的执行备份,实际上是fork出了一个子进程,用子进程去执行快照持久化操做,将数据保存在一个.rdb文件中。
子进程刚刚产生的时候,是和父进程共享内存中的数据的,可是子进程作持久化时,是不会修改数据的,而父进程是要持续提供服务的,因此父进程就会持续的修改内存中的数据,这个时候父进程就会将内存中的数据,Copy出一份来进行修改。
父进程copy的数据是以数据页为单位的(4k一页),对那一页数据进行修改就copy哪一页的数据。
子进程因为数据没有变化就会一直的去遍历数据,进程持久化操做了,这就是只保留了建立子进程的时候的快照。
那么RDB是在何时触发的呢?
# save <seconds> <changes> save 60 10000 save 300 10
上这段配置就是在redis.conf文件中配置的,第一个参数是时间单位是秒,第二个参数执行数据变化的次数。
意思就是说:300秒以内至少发生10次写操做、
60秒以内发生至少10000次写操做,只要知足任一条件,均会触发bgsave
Redis在接收到客户端请求指令后,会先进行校验,校验成功后,当即将指令存储到AOF日志文件中,就是说,Redis是先记录日志,再执行命令。这样即便命令还没执行忽然宕机了,经过AOF日志文件也是能够恢复的。
AOF日志文件,随着时间的推移,会愈来愈大,因此就须要进行重写瘦身。AOF重写的原理就是,fork一个子进程,对内存进行遍历,而后生成一系列的Redis指令,而后序列化到一个新的aof文件中。而后再将遍历内存阶段的增量日志,追加到新的aof文件中,追加完成后当即替换旧的aof文件,这样就完成了AOF的瘦身重写。
由于AOF是一个写文件的IO操做,是比较耗时。因此AOF日志并非直接写入到日志文件的,而是先写到一个内核的缓存中,而后经过异步刷脏,来将数据保存到磁盘的。
因为这个状况,就致使了会有还没来得急刷脏而后就宕机了,致使数据丢失的风险。
因此Redis提供了一个配置,能够手动的来选择刷脏的频率。
AOF默认是关闭的,须要在配置文件中手动开启。
# 只有在“yes”下,aof重写/文件同步等特性才会生效 appendonly yes ## 指定aof文件名称 appendfilename appendonly.aof ## 指定aof操做中文件同步策略,有三个合法值:always everysec no,默认为everysec appendfsync everysec ## 在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no” no-appendfsync-on-rewrite no ## aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb” auto-aof-rewrite-min-size 64mb
Redis4.0提供了一种新的持久化机制,就是RDB和AOF结合使用,将rdb文件内容和aof文件存在一块儿,AOF中保存的再也不是所有数据了,而是从RDB开始的到结束的增量日志。
这样在Redis恢复数据的时候,能够先伪装RDB文件中的内容,而后在顺序执行AOF日志中指令,这样就将Redis重启时恢复数据的效率获得了大幅度提高。
恩,此次就先总结到这里吧,后面会继续总结Redis相关知识,LRU、LFU、内存淘汰策略,管道等等。