
1.Redis可靠性:
体现:
(1)数据尽量少丢失 -- RDB、AOF
(2)服务尽量少中断 -- 增加副本冗余,将一份数据同时保存在多个服务器上
2.主从模式
2.1.主从库同步
第一次同步流程:
-
第一阶段:确认连接,主库和从库建立连接,主库发送确认信号给从库后,主从就可以开始同步了
从库发送psync命令,命令包括主库的runID和复制进度offset参数
runID:每个redis实例启动时产生的唯一id
offset:-1,代表第一次复制
主库接收到psync命令后,会用FULLRESYNC响应命令带上主库的runID和主库目前复制进度返回给从库,从库会记下这些值(FULLRESYNC响应代表第一次复制采取的是全量复制)
-
第二阶段:主库将数据同步给从库,从库将数据全量加载到内存,该过程依赖RDB快照
详细来说,主库fork()出bgsave子进程生成RDB快照,将快照发送给从库后,从库会先清空数据(为了避免之前加载了其他数据),再本地加载数据,再主库同步的这个过程中不会产生堵塞,主库仍然接受读写请求,再同步RDB快照的这个过程中,会将生产快照的时刻之后的读写请求写入replication buffer,为了保证主从一致性
-
第三阶段:把第二阶段执行过程中新接受到的写命令,再发送给从库。

主从级联:
由于生成RDB快照(需要fork出bgsave进程)和传输RDB快照当从节点过多时,其会堵塞主线程影响服务,因此就有了主从级联的模式为主节点分担压力
就是说我们部署主从集群的时候,可以手动选择一个从库级联其他从库。然后我们可以再其他从库执行如如下操作去建立主从关系
replicaof 所选从库的IP 6379
如下图:

2.2.基于长连接的命令传播
一旦主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销
2.3.主从网络中断该如何处理
Redis 2.8 之前会再次进行全量复制
Redis 2.8 之后,增量复制
增量复制:
主从库断连后,主库会将写请求写入replication buffer和repl_backlog_buffer,
其中repl_backlog_buffer包含两个指针,master_repl_offset用于记录主库写到的位置,slave_repl_offset用于记录读库读到写命令的位置,正常情况下两者相等。

当从库断连又重新连接后,从库会发送psync命令给主库,并包括从库当前的slave_repl_offset的值发送给主库,主库比较master_repl_offset和slave_repl_offset的差值,这时只需将差距中间的命令同步给从库即可

需要注意:
由于repl buffer是环形缓冲区,如果从库执行命令比较慢,可能会导致还未执行的命名被主库的写给覆盖了,因此我们需要调节repl_backlog_size参数去避免这个问题
repl_backlog_size = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小
实际应用会将该值的大小*2,即repl_backlog_size = 缓冲空间大小 * 2,如果还是覆盖的话还是会导致主从数据不一致,这个时候可以考虑切片集群来分担单个主库的压力
举个例子,如果主库每秒写入2000个操作,每个操作的大小为2KB,网络每秒能传输1000个操作,那么,有1000个操作需要缓冲起来,这就至少需要2MB的缓冲空间。否则,新写的命令就会覆盖掉旧操作了。为了应对可能的突发压力,我们最终把repl_backlog_size设为4MB。
主从复制为什么不采用AOF?
(1)AOF文件比RDB文件更大
(2)初始化数据时,RDB文件执行的更快
3.主从同步中的坑
3.1.主从数据不一致
概念:客户端从从库读取到的值和主库中读取到的值不一致
产生原图:
- 原因一:主从库之间的网络存在延迟,从库无法及时收到主库的命令
- 原因二:从库当前正被自身耗时的操作阻塞,例如查询bigkey、范围查询等。同步命令阻塞在队列中,无法马上执行
- 原因三:主从库设置的 maxmemory 不同,如果 slave 比 master 小,那么 slave 内存就会优先达到 maxmemroy,然后开始淘汰数据,此时主从库也会产生不一致
如何解决:
- 方法一:保证主从库间的网络连接状况良好,避免将主从部署在不同的机房
- 方法二:Redis的
INFO
指令能够监控主从的接收命令的进度信息(master_repl_offset)和从库复制的进度信息(slave_repl_offset),因此我们可以监控主从之间的两者进度差值,当差值达到阈值就将从库从客户端的连接中移除
3.2.读取过期数据
Redis有两种策略删除过期数据,分别是惰性删除和定期删除(注意与淘汰策略区分开来,淘汰策略指的是redis内存不足,需要清理数据,而过期数据的删除策略又是另一回事了)
读取过期数据的发生场景:
-
场景一:数据过期,请求主节点时会删除数据,但是在redis 3.2版本之前,读取从数据库哪怕数据过期也会返回过期数据
解决措施:升级redis版本。3.2版本之后,数据过期就会返回空值给客户端,但是不会删除。
-
场景二:过期时间设置在主从节点存在延迟,不过只存在于EXPIRE和PEXPIRE
命令
设置过期时间的操作一共有四种,如下图,

对于EXPIRE
和PEXPIRE
,其超时时间的计算是指执行命令的当前时间后推ttl秒过期,所以命令通过给从节点时,主从之间的过期时间会存在延迟,所以就从从节点读到了在主节点过期的数据
解决措施:使用EXPIREAT
和PEXPIREAT
命令,该命令时指定具体过期的时间戳,但是得主从机器的时钟得保持一致
3.3.不合理配置项导致的服务挂掉
-
protected-mode
配置项:限定哨兵实例能否被其他服务器访问,配置项设置为yes时,哨兵实例只能在部署的服务器本地进行访问。当设置为no时,其他服务器也可以访问这个哨兵实例
所以当该配置设置为yes时,其他哨兵无法访问该哨兵,就无法监控、选主等。
正常的设置如下:
protected-mode no
//bind 其他哨兵实例的ip,这样哨兵之间才可以通信
bind 192.168.10.3 192.168.10.4 192.168.10.5
-
cluster-node-timeout
配置项:设置了Redis Cluster中实例响应心跳的超时时间
如果主节点发送故障,在执行主从切换的过程中,由于网络延迟和切换操作执行的影响,切换时间往往比较长,可能会导致实例的心跳超时(超出配置项)。实例超时后又被判定为异常。
如果执行主从切换的实例超过半数,而主从切换时间又长,可能会有半数节点超时,导致整个集群挂掉,建议配置10~20s
关于slave-read-only
配置项:配置从节点是否可读
再解释一下 slave-read-only 的作用,它主要用来控制 slave 是否可写,但是否主动删除过期 key,根据 Redis 版本不同,执行逻辑也不同。
1、如果版本低于 Redis 4.0,slave-read-only 设置为 no,此时 slave 允许写入数据,但如果 key 设置了过期时间,那么这个 key 过期后,虽然在 slave 上查询不到了,但并不会在内存中删除,这些过期 key 会一直占着 Redis 内存无法释放。
2、Redis 4.0 版本解决了上述问题,在 slave 写入带过期时间的 key,slave 会记下这些 key,并且在后台定时检测这些 key 是否已过期,过期后从内存中删除。
但是请注意,这 2 种情况,slave 都不会主动删除由 master 同步过来带有过期时间的 key。也就是 master 带有过期时间的 key,什么时候删除由 master 自己维护,slave 不会介入。如果 slave 设置了 slave-read-only = no,而且是 4.0+ 版本,slave 也只维护直接向自己写入 的带有过期的 key,过期时只删除这些 key。