哨兵
吹哨人巡查监控后台master主机是否故障,如果故障了根据投票数自动将某一个从库转换为主库,继续对外服务
哨兵的作用:
- 监控redis运行状态,包括master和slave
- 当master宕机了,能自动将slave转换为master
哨兵的功能:
- 主从监控:监控主从redis库运行是否正常
- 消息通知:哨兵可以将故障转移的结果发送给客户端
- 故障转移:如果master异常,则会进行主从切换,将其中一个slave作为新的master
- 配置中心:客户端通过连接哨兵来获得当前redis服务的主节点地址
哨兵的配置
在sentinel配置文件中,除了bind、daemonize、protected-mode、port、logfile、pidfile、dir的配置之外(以上配置同普通的redis.conf来就行),还有这些配置项要配置:
主要:
- sentinel monitor <master-name> <ip> <redis-port> <quorum>:除了指定master的名字、IP地址、端口号,还有一个quorum标识最少有几个哨兵认可认可客观下线,同意故障迁移的法定票数。
补充:网络有时候不可靠,有时候sentinel会因为网络阻塞误以为一个master redis已经宕机了,在sentinel集群环境下需要多个sentinel互相沟通来确认某个master时候真的宕机了,quorum这个参数是进行客观下线的一个依据,意思是至少有quorum个sentinel认为这个master有故障,才会对这个master进行下线以及故障转移。增加了容错率、高可用性。
- sentinel auth-pass <master-name> <password>:master设置了密码,指定连接master服务的密码
其他:
- sentinel down-after-milliseconds <master-name> <milliseconds>:指定多少毫秒之后,主节点没有应答哨兵,此时哨兵主观上认为主节点下线
- sentinel parallel-syncs <master-name> <nums>:表示允许并行同步的slave个数,当Master挂了后,哨兵会选出新的Master,此时,剩余的slave会向新的master发起同步数据
- sentinel failover-timeout <master-name> <milliseconds>:故障转移的超时时间,进行故障转移时,如果超过设置的毫秒,表示故障转移失败
- sentinel notification-script <master-name> <script-path> :配置当某一事件发生时所需要执行的脚本
- sentinel client-reconfig-script <master-name> <script-path>:客户端重新配置主节点参数脚本
注:
启动哨兵的命令是:redis-sentinel sentinel.conf --sentinel
如果主机1挂了,从机2上位成为主机,那么原来的1重新启动后,不会重新变成主机,而是会变成2的从机。哨兵会动态修改哨兵、主机、从机的相关配置文件的内容。
所以主机也要设置masterauth,这样当这台主机挂了之后重新启动成为从机时候能连接上新的主机
哨兵运行流程
当一个主从配置中的master失效之后,sentinel可以选举出一个新的master用于自动接替原master的工作,主从配置中的其他redis服务器自动指向新的master同步数据。一般建议sentinel采取
主观下线
主观下线(SDOWN)(Subject Down)是单个sentinel自己主观检测到的关于master,从sentinel的角度来看,如果发送了ping后,在一段时间没有收到合法的回复,就达到了SDOWN的条件。
sentinel配置文件中的down-after-milliseconds设置了判断主观下线的时间长度啊(单位是毫秒)。默认是30秒。
客观下线
客观下线(ODOWN)(Object Down)是需要一定的sentinel,多个哨兵达成一致的意见才能认为一个master客观上已经宕机了
选举领导者哨兵
当主节点被判断客观下线后,各个节点会进行协商,先选举出一个领导者哨兵节点并由该领导者节点进行failover(故障迁移)(使用raft算法)
raft算法的了解:监视该主节点的所有哨兵都有可能被选为领导者,基本思路是先到先得:即在一轮选举中,哨兵A向B发送成为新的领导者的申请,如果B没有同意过其他哨兵,则同意A成为领导者
选出新的master
由哨兵领导者开始推动故障切换流程并选出一个新的master。
关于master选举算法
- 选举出新的master,先根据优先级(配置文件中的slave-priority或者replica-priority,越小,优先级越高),选优先级大的,如果优先级一样的话,看哪个slave复制的更完整就选哪个,如果完整性也一样,就根据run ID最小的选(直接根据ascii码值)。
- 领导者哨兵会发送给执行slaveof no one命令让选出来的从节点变成主节点,并通过slaveof命令让其他节点变成主节点。
- 领导者哨兵会将之前已经下线的老master设置为新选出的master的从节点,当老master重新上线后,会成为新的master的从节点。老master降级为slave并恢复正常工作。
使用建议:
- 哨兵节点的数量应该为多个,哨兵本身应该集群,保证高可用性
- 哨兵节点的数量应该是奇数,才好选举领导者哨兵
- 各个哨兵节点的配置应该一致
- 如果哨兵节点部署在Docker等容器中,尤其要注意端口的正确映射
- 哨兵集群加主从复制并不能保证数据零丢失
集群
由于数据量过大,单个master难以承担,因此需要对多个复制集进行集群,形成水平拓展每个复制集只负责整个数据库的一部分,这就是redis的集群,其作用是提供在多个redis节点间共享数据的程序集。
redis集群是一个提供在多个redis节点间共享数据的程序集,redis集群可以支持多个master。
集群的功能
- redis集群支持多个master,又支持多个slave。可以实现读写分离、支持数据的高可用、支持海量数据的读写存储操作。
- 由于cluster自带的sentinel的故障转移机制,内置了高可用的支持,无需再去使用哨兵功能
- 客户端与redis的节点连接,不再需要连接集群中的所有节点,只需要任意连接集群中的一个可用节点即可
- 槽位slot负责分配到各个物理服务节点,又对应的集群来负责维护节点,插槽和数据之间的关系
槽位slot
集群的密钥空间被分为16384个槽位,有效地设置了16384个主节点的集群大小上限(但是建议的最大节点的大小约为1000个)
集群中的每个主节点处理16384个哈希槽中的一个子集。当没有集群重新配置正在进行时(即哈希槽从一个节点移动到另一个节点),集群是稳定的。当集群稳定时,单个哈希槽将又单个节点提供服务。
集群没有使用一致性hash,而是引入了哈希槽的概念。redis集群有16384个哈希槽,每个key通过CRC16校验之后对16384取模来决定放置哪个位置的槽。集群的每个节点负责一部分的hash槽。
分片
使用redis集群时我们会将存储的数据分散到多态redis机器上,这成为分片。简言之,集群中的每个redis都被认为是整个数据的一个分片。
如何找到指定的key的分片:为了找到给定key的分片,我们对key进行CRC16(key)算法处理并通过对总分片数量取模。然后,使用确定性哈希函数,这意味着给定的key将多次始终映射到同一个分片。
分片加槽位的优势:方便扩容缩容和数据分派查找,无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态
槽位映射算法
哈希取余分区
用户每次读写操作都是根据公式:hash(key) % N个机器台数,计算出哈希值,用来决定数据映射到哪个节点上。
优点:简单有效,只需要预估好数据规划好节点,例如三台、八台、十台,就能保证一段时间的数据支撑。使用hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡+分而治之的作用。
缺点:原来规划好的节点,进行扩容或缩容就更麻烦了。扩容缩容时,每次数据变动导致节点有变动,映射关系需要重新进行计算,再服务器个数固定不变时没有问题,如果需要弹性扩容或者故障停机的情况下,原来的取模公式就会发生变化。此时地址经过取余运算后的结果将会发生很大变化,根据公式获取服务器也会变得不可控。某个redis机器宕机了,由于机器数量改变会导致hash区域全部数据重新洗牌。
一致性哈希算法分区
一致性哈希算法设计目标是为了解决分布式缓存数据变动和映射问题(分母数量改变了 ,取余数不行了),当服务器个数发生变动时,尽量减少影响客户端到服务器的映射关系。
步骤:
- 算法构建一致性哈希环:最终哈希值变为 哈希函数求得的值变为对 2的32次方 取模,并将0和2的32次方等同,将整个全量看作一个环,环内的范围就是(0,2^31-1)。
- redis服务器ip节点映射:将集群中的各个ip节点映射到环上的某一个位置。
- key落到服务器的落键规则:当我们需要存储一个kv键值对时,首先计算key的hash值,确定数据在环上的位置。从此位置沿环顺时针"行走",第一台遇到的服务器就是其应该定位到的服务器,并将该键值对存储在该节点上。
优点
- 容错性:如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中的上一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之前的数据,且这些数据会转移到下一台服务器上(沿着顺时针方向遇到的第一台服务器)进行存储。
- 拓展性:数据量增加了,需要重新增加一台节点,受到影响的只是上一台服务器,会重新把上一台服务器上应该录到新服务器的数据重新到新的服务器。
缺点
一致性hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题。
哈希槽分区
哈希槽实质就是一个数组,大小是[0,2^14-1]形成的hash slot空间。
它能解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,就相当于节点上放的是槽,槽里面放的是数据。
槽解决的是粒度问题,相当于把粒度变大了,如此便于数据移动。哈希解决的是映射问题,使用key的哈希槽来计算所在的槽,便于数据分配
一个集群只能有16384个哈希槽,编号0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。
集群会记录节点和槽的对应关系,解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取模,余数是几key就落入对应的槽里。HASH_SLOT = CRC16(key) mod 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
为何最大槽数是16384
- 如果槽位为65536,发送的心跳信息的消息头达到了8k,发送的心跳包过于庞大。
- redis的集群主节点数量基本不可能超过1000个。
- 槽位越小,节点少的情况下,压缩比高,容易传输。
注
- redis集群不保证强一致性,这意味着在特定的条件下,redis集群可能会丢掉一些被系统收到的写入请求命令。
- redis集群也存在类似于哨兵的机制,主机宕机后,从机会上位成为主机,主机再次归来的时候会变为从机。如果想要节点从属调整使用命令:CLUSTER FAILOVER
- 使用 redis-cli -a 密码 --cluster reshard IP地址:端口号 命令扩容后重新分配槽位后,新的主机对应的槽位是原来的各个主机各自匀一点凑出来的。缩容的时候可以将空出来的槽位给指定的主机全部接收。
- 不在同一个slot槽位下的键值无法使用mset、mget等多键操作。可以通过{}来定义同一个组的概念,使key中{}内相同内容的键值对放到一个slot槽位去。比如mset k1{aaa} 111 k2{aaa} 222,mget k1{aaa} k2{aaa}