Redis_xiaolingcode
# Redis
# 高可用篇
# Redis如何实现服务高可用
要想设计一个高可用的Redis服务,一定要从Redis的多服务节点来考虑,比如Redis的主从复制,哨兵模式,切片集群。
# 主从复制
主从复制是Redis高可用服务的最基础的保证,实现方案就是将从前的一台Redis服务器,同步数据到多台从Redis服务器上,即一主多从的模式,且从服务器之间采用的是读写分离的方式。
主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来的写操作命令,然后执行这条命令。

也就是说,所有的数据修改只在主服务器上进行,然后将最新的数据同步给服务器,这样就使得主从服务器的数据是一致的。
注意,主从服务器之间的命令复制时异步进行的。
具体来说,在主服务器命令传播阶段,主服务器接受到新的写命令后,会发送给从服务器。但是,主服务器并不会等从服务器实际执行完命令后,再把结果返回给客户端,而是主服务器自己在本地执行完命令后,就会向客户端返回结果了。如果从服务器还没有执行主服务器同步过来的命令,主从服务器间的数据就不一致了。
所以,无法实现强一致性保证(主从数据时时刻刻保持一致),数据不一致是难以避免的。
# 哨兵模式
在使用Redis主从服务的时候,会有一个问题,就是当Redis的主从服务出现故障宕机时候,需要手动进行恢复。
为了解决这个问题,Redis增加了哨兵模式,因为哨兵模式做到了可以监控主从服务器,并且提供主从节点故障转移的功能。

# 切片集群模式
当Redis缓存数据量大到一台服务器无法缓存时,就需要使用Redis切片集群方案,它将数据分布在不同服务器上,一次来降低系统对单主节点的依赖,从而提高Redis服务的读写性能。
Redis Cluster方案采用哈希槽(Hash Slot),来处理数据和节点之间的映射关系。在Redis Cluster方案中,一个切片集群共有16384个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的key,被映射到一个哈希槽中,具体执行过程分为两大步:
- 根据键值对的key,按照CRC16算法计算一个16bit的值。
- 再用16bit值对16384取模,得到0~16383范围的模数,每个模数代表一个相应编号的哈希槽。
接下来问题就是,这些哈希槽怎么被映射到具体的Redis节点上
- 平均分配:在使用cluster create 命令创建Redis集群时,Redis会自动把所有哈希槽平均分布到集群节点上。比如集群中有9个节点,则每个节点上槽的个数为16384/9个
- 手动分配:可以使用cluster meet命令手动建立节点间的连接,组成集群,再用cluster addslots命令,指定每个节点上的哈希槽个数。

上图中的切片集群一共有2个节点,假设有4个哈希槽(slot0~slog3)时,我们就可以通过命令手动分配哈希槽,比如节点1保存哈希槽0和1,节点2保存哈希槽2和3
redis-cli -h 192.168.1.10 –p 6379 cluster addslots 0,1
redis-cli -h 192.168.1.11 –p 6379 cluster addslots 2,3
2
然后在集群运行的过程中,key1和key2计算完CRC16值后,对哈希槽总个数4进行取模,再根据各自的模数结果,就可以被映射到哈希槽1(对应节点1)和哈希槽2(对应节点2)
# 集群脑裂导致数据丢失怎么办
什么是脑裂? 集群的脑裂现象,这就好比一个人有两个大脑,那么到底受谁控制呢? 那么在Redis中,集群脑裂产生数据丢失的现象是怎样的呢?
在Redis主从架构中,部署方式一般是一主多从,主节点提供写操作,从节点提供读操作,如果主节点的网络出问题,它与所有的从节点都失联了,但是此时的主节点和客户端的网络是正常的,这个客户端并不知道Redis内部已经出了问题,还在照样想找个失联的住系欸但写数据,此时这些数据被旧主节点缓存到了缓冲区里,因为主从节点之间的网络问题,这些数据都是无法同步给从节点的。
这时,哨兵发现主节点失联了,它就认为主节点挂了(但实际上主节点正常运行,只是网络出问题了),于是哨兵就会在从节点中选举出一个leader作为主节点,这时集群就有两个主节点了,脑裂出现了。
然后,网络突然好了,哨兵因为之前已经选举出一个新节点了,他就会把旧主节点降级为从节点,然后从节点会向新主节点请求数据同步,因为第一次同步时全量同步的方式,此时的从节点会清空自己本地的数据,然后再做全量同步。所以,之前客户端在A写入的数据就会丢失,也就是集群产生脑裂数据丢失的问题。
总结一句话就是:由于网络问题,集群节点之间失去联系。主从数据不同步;重新平衡选举,产生两个主服务。等网络恢复,旧主节点降为从节点,再与新主节点进行同步复制的时候,由于会从节点清空自己的缓冲区,所以导致之前客户端写入的数据丢失了。
# 解决方案
当主节点发现从节点下线或者通信超时的总数量小于阈值时候,那么禁止主节点进行写数据,直接把错误返回给客户端。
再Redis的配置文件中有两个参数我们可以设置:
- min-slaves-to-write x,主节点必须要有至少x个从节点连接,如果小于这个数量,主节点会禁止写数据。
- min-slaves-max-lag x,主从数据复制和同步的延迟不能超过x秒,如果超过,主节点会禁止写数据。 我们可以把min-slaves-to-write和min-slaves-max-lag这两个配置项搭配起来使用,分别给他们设置一定的阈值,假设为N和T。
这个两个配置项组合后的要求是,主库连接的从库中至少有N个从库,和主库进行数据复制时,ACK消息延迟不饿能超过T秒,否则,主库不会再接受客户端的写请求了。
即使原主库时假故障,它再故障期间也无法相应哨兵心跳,也不能和从库进行同步,自然也就无法和从库进行ACK确认了。这样一来,min-slaves-to-write和min-slaves-max-lag的组合要求就无法得到满足,原主库就会被限制接受客户端写请求,客户端也就不能在原主库中写入新数据了。
等到新主库上线时候,就只有新主库能接受和处理客户端请求,此时,新写的数据会被直接写到新主库中。而原主库会被哨兵降为从库,即使它的数据被清空了也不会有数据丢失。