Redis 的部署模式
里面如果对一致性hash算法不了解的,可以参考:一致性哈希算法原理详解
Redis 有哪些部署方式?
单机模式:这也是最基本的部署方式,只需要一台机器,负责读写,一般只用于开发人员自己测试。
哨兵模式:哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立运行的进程,其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。它具备自动故障转移、集群监控、消息通知等功能。
cluster集群模式:在redis3.0版本中支持了cluster集群部署的方式,这种集群部署的方式能自动将数据进行分片,每个master上放一部分数据,提供了内置的高可用服务,即使某个master挂了,服务还可以正常地提供。
主从复制:在主从复制这种集群部署模式中,我们会将数据库分为两类,第一种称为主数据库(master),另一种称为从数据库(slave)。主数据库会负责我们整个系统中的读写操作,从数据库会负责我们整个数据库中的读操作。其中在职场开发中的真实情况是,我们会让主数据库只负责写操作,让从数据库只负责读操作,就是为了读写分离,减轻服务器的压力。
哨兵有哪些作用?
1.监控整个主数据库和从数据库,观察它们是否正常运行;
2.当主数据库发生异常时,自动的将从数据库升级为主数据库,继续保证整个服务的稳定。
哨兵选举过程是怎么样的?
1.第一个发现该master挂了的哨兵,向每个哨兵发送命令,让对方选举自己成为领头哨兵
2.其他哨兵如果没有选举过他人,就会将这一票投给第一个发现该master挂了的哨兵
3.第一个发现该master挂了的哨兵如果发现由超过一半哨兵投给自己,并且其数量也超过了设定的quoram参数,那么该哨兵就成了领头哨兵
4.如果多个哨兵同时参与这个选举,那么就会重复该过程,直到选出一个领头哨兵
选出领头哨兵后,就开始了故障修复,会从选出一个从数据库作为新的master
cluster集群模式是怎么存放数据的?
数据分片机制
将不同的 key 分散放置到不同的 redis 节点,通常的做法是获取 key 的哈希值,然后根据节点数来求模,分发到不同节点,但这种做法有其明显的弊端,当我们需要增加或减少一个节点时,会造成大量的 key 无法命中,这种比例是相当高的,所以就有人提出了一致性哈希的概念。
一致性哈希有四个重要特征:
均衡性:平衡性,是指哈希的结果能够尽可能分布到所有的节点中去,这样可以有效的利用每个节点上的资源。
单调性:当节点数量变化时哈希的结果应尽可能的保护已分配的内容不会被重新分派到新的节点。
分散性和负载:这两个其实是差不多的意思,就是要求一致性哈希算法对 key 哈希应尽可能的避免重复。
Redis 集群没有使用一致性hash,而是引入了哈希槽的概念。
Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,每个key通过CRC16校验后对16384取模来决定放置哪个槽(Slot),每一个节点负责维护一部分槽以及槽所映射的键值数据。
计算公式:slot = CRC16(key) & 16383。
这种结构很容易添加或者删除节点,并且无论是添加删除或者修改某一个节点,都不会造成集群不可用的状态。使用哈希槽的好处就在于可以方便的添加或移除节点。
当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;
当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了。
虚拟槽分区特点
解耦数据和节点之间的关系,简化了节点扩容和收缩难度。
节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据
支持节点、槽和键之间的映射查询,用于数据路由,在线集群伸缩等场景。
为什么RedisCluster会设计成16384个槽【 0 ~ 16383 】呢?
1.如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
心跳消息 (Heartbeat Message) 是一种发送源发送到接收方的消息,这种消息可以让接收方确定发送源是否以及何时出现故障或终止。
在消息头中,最占空间的是 myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb,因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
2.redis的集群主节点数量基本不可能超过1000个。
集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
3.槽位越小,节点少的情况下,压缩率高。
Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。而16384÷8=2kb。
综上所述,作者决定取16384个槽,不多不少,刚刚好!
一个cluster集群中总共有16384个节点,集群会将这16384个节点平均分配给每个节点,当然,我这里的节点指的是每个主节点,就如同下图:
cluster的故障恢复是怎么做的?
判断故障的逻辑其实与哨兵模式有点类似,在集群中,每个节点都会定期的向其他节点发送ping命令,通过有没有收到回复来判断其他节点是否已经下线。
如果长时间没有回复,那么发起ping命令的节点就会认为目标节点疑似下线,也可以和哨兵一样称作主观下线,当然也需要集群中一定数量的节点都认为该节点下线才可以,我们来说说具体过程:
1.当A节点发现目标节点疑似下线,就会向集群中的其他节点散播消息,其他节点就会向目标节点发送命令,判断目标节点是否下线
2.如果集群中半数以上的节点都认为目标节点下线,就会对目标节点标记为下线,从而告诉其他节点,让目标节点在整个集群中都下线
主从同步原理是怎样的?
1.当一个从数据库启动时,它会向主数据库发送一个SYNC命令,master收到后,在后台保存快照,也就是我们说的RDB持久化,当然保存快照是需要消耗时间的,并且redis是单线程的,在保存快照期间redis受到的命令会缓存起来
2.快照完成后会将缓存的命令以及快照一起打包发给slave节点,从而保证主从数据库的一致性。
3.从数据库接受到快照以及缓存的命令后会将这部分数据写入到硬盘上的临时文件当中,写入完成后会用这份文件去替换掉RDB快照文件,当然,这个操作是不会阻塞的,可以继续接收命令执行,具体原因其实就是fork了一个子进程,用子进程去完成了这些功能。
因为不会阻塞,所以,这部分初始化完成后,当主数据库执行了改变数据的命令后,会异步的给slave,这也就是我们说的复制同步阶段,这个阶段会贯穿在整个中从同步的过程中,直到主从同步结束后,复制同步才会终止。
无硬盘复制是什么?
我们刚刚说了主从之间是通过RDB快照来交互的,虽然看来逻辑很简单,但是还是会存在一些问题,但是会存在着一些问题。
1.master禁用了RDB快照时,发生了主从同步(复制初始化)操作,也会生成RDB快照,但是之后如果master发成了重启,就会用RDB快照去恢复数据,这份数据可能已经很久了,中间就会丢失数据
2.在这种一主多从的结构中,master每次和slave同步数据都要进行一次快照,从而在硬盘中生成RDB文件,会影响性能
为了解决这种问题,redis在后续的更新中也加入了无硬盘复制功能,也就是说直接通过网络发送给slave,避免了和硬盘交互,但是也是有io消耗
Redis 哨兵模式
Redis 主从模式是一主多从,从节点宕机还有其他的备份,但是主节点宕机了,必然引起系统的故障。为了解决这个问题,Redis 提供了哨兵模式,监控主从模式中的所有节点,如果主节点宕机,会选举从节点重新成为主节点。使整个主从服务正常运行。
Redis 提供了哨兵机制来实现主从集群的自动故障恢复。其主要起到的作用如下:
- 监控:Sentinel 会监控不断检查主从集群的主从节点是否正常工作;
- 故障恢复:主节点宕机,Sentinel 会从从节点中选举出一个节点,将其设置为主节点,故障恢复后原先的主节点将置为从节点;
- 通知:Sentinel 充当客户端的服务发现来源,当集群发现故障转移后,会将最新消息推送到客户端。
服务状态监控
Sentinel 基于心跳机制监测服务状态,每个一秒向集群中的每个实例发送 ping 命令。
- 主观下线:如果某 Sentinel 节点发现某个节点未在规定的时间响应心跳,那么就认为该节点主观下线;
- 客观下线:如果指定数量的 Sentinel 认为某个节点下线,那么该节点将被认为客观下线。
选举新的主节点
一旦发现主节点故障, sentinel 需要在从节点选择新的主节点,选择依据:
- 首先会判断从节点和主节点的断开的时间,如果超过指定值则会排除该从节点;
- 判断节点的优先等级,越小优先级越高,如果是0说明不参与选举;
- 判断从节点同步数据偏移量,雨大说明数据越新,优先级越高;
- 租后判断节点的运行id,越小优先级越高。
如何故障转移
当选中其中一个从节点为新的主节点,故障转移的步骤如下:
- sentinel 给备选的从节点发送 slaveof no one 命令,该节点就成为主节点;
- sentinel 给其他所有的从节点发送 slave of 指令,让其他的节点称为新主节点的从节点,开始从新的主节点同步数据。
- 最后 sentinel 会将故障节点标记为从节点,当故障节点重启后自动变为新主节点的从节。