小酌Redis(一)redis集群

1 概述

一般的文档,都把redis的集群方式分成三种:主从、哨兵、集群(这里的集群只是广义集群的一种)。但是这么分类很不严谨,哨兵模式,单独使用是没有意义的,哨兵的作用有两个:

  • 监控:监控主节点和从节点是否正常运行
  • 提醒:当被监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
  • 故障迁移:主数据库出现故障时自动将从数据库转换为主数据库

说白了,哨兵就是一个打辅助的,本身并不提供数据存储功能,能独立使用的方式只有两种,主从模式和集群模式,所以我认为将redis分为两类比较合适:

  1. 主从集群配合哨兵使用
  2. 分布式(分区)集群

主从集群

主从集群,将数据库分为两中角色,一种是主数据库(master),另一种是从数据库(slave)。主数据库可以进行读写操作,从数据库只能有读操作(并不一定,只是推荐这么做,后续会说明)。当主数据库有数据写入,会将数据同步复制给从节点,一个主数据库可以同时拥有多个从数据库,而从数据库只能拥有一个主数据库。值得一提的是,从节点也可以有从节点,级联结构。
图片来源:https://www.cnblogs.com/kevingrace/p/5685332.html
配置
在从节点的redis.conf配置文件中加入
slaveof 主数据库ip 主数据库port
先启动主节点,再启动从节点即可

复制原理

当从节点启动后,会向主数据库发送SYNC命令。同时主数据库收到SYNC命令后会开始在后台保存快照(即RDB持久化,在主从复制时,会无条件触发RDB),并将保存快照期间接收到的命令缓存起来,当快照完成后,redis会将快照文件和所有缓存命令发送给数据库。从数据库接收到快照文件和缓存命令后,会载入快照文件和执行命令,也就是说redis是通过RDB持久化文件和redis缓存命令来时间主从复制。一般在建立主从关系时,一次同步会进行复制初始化。
以上过程为复制初始化,复制初始化结束后,主数据库每当受到写命令时,就会将命令同步给从数据库,保证主从数据一致性。
这里需要提一句,在Redis2.6之前,每次主从数据库断开连接后,Redis需要重新执行复制初始化,在数据量大的情况下,非常低效。而在Redis2.8之后,在断线重连后,主数据库只需要将断线期间执行的命令传送给从数据库。
在这里插入图片描述

乐观复制

Redis采用了乐观复制的策略,也就是在一定程度内容忍主从数据库的内容不一致。具体来说,Redis在主从复制的过程中,本身就是异步的,在主从数据库执行完客户端请求后会立即将结果返回给客户端,并异步的将命令同步给从数据库,但是这里并不会等待从数据库完全同步之后,再返回客户端。这一特性虽然保证了主从复制期间性能不受影响,但是也会产生一个数据不一致的时间窗口,如果在这个时间窗口期间网络突然断开连接,就会导致两者数据不一致。如果不在配置文件中添加其他策略,那就默认会采用这种方式,乐观二字也就体现在这里(是不是有点想当然的认为自己不会这么倒霉的停在这个空窗期)。
当然了,上面这种方式并不是绝对的,只要牺牲一点性能,还是可以避免上述问题。在配置文件中:

min-slaves-to-write 3
min-slaves-max-lag 10

前者表示当3个或者3个以上的从数据库同步主数据库时,主数据库才是可写的,否则会返回错误。
后者表示允许从数据库最长失联时间(单位s),如果从数据库最后与主数据库保持联的时间小于这个时间,则认为还存活。

增量复制

增量复制是基于以下4点实现的:

  1. 主节点除了备份RDB文件之外还会维护者一个环形积压队列,以及环形队列的写索引和从节点同步的全局offset,环形队列用于存储最新的操作数据。
  2. 从数据库会存储主数据库的运行id,每个redis实例会拥有一个唯一的运行id,当实例重启后,就会自动生成一个新的id。
  3. 主节点在复制同步阶段,主数据库每将一个命令传递给从数据库时,都会将命令存放到积压队列,并记录当前积压队列中存放命令的偏移量。
  4. 从数据库接收到主数据库传来的命令时,会记录下偏移量。

在2.8之后,主从复制不再发送SYNC命令,取而代之的是PSYNC,格式为:“PSYNC ID offset”。
当从节点和主节点断开重连之后,会把从节点维护的offset,也就是上一次同步到哪里的这个值告诉主节点,同时会告诉主节点上次和当前从节点连接的主节点的runid,满足下面两个条件,Redis不会全量复制,也就是说,不满足以下条件还是会全量复制

1.从节点传递的run id和master的run id一致。
2.主节点在环形队列上可以找到对应offset的值。

积压队列本质上是一个固定长度的循环队列,默认情况下积压队列的大小为1MB,可以通过配置文件:

repl-backlog-size 1mb

来设置,积压队列越大,允许主从数据库断线的时间就越长
Redis同时也提供了当没有slave需要同步的时候,多久可以释放环形队列,默认一小时:

repl-backlog-ttl 3600

哨兵模式

哨兵的作用就是健康redis节点的运行状态。

1.监控主数据库和从数据库是否能够正常运行
2.主数据库出现故障时自动将从数据库转换为主数据库。

普通的主从模式,当主数据库崩溃时,需要手动切换从数据库成为主数据库:

  1. 在从数据库中使用SLAVE NO ONE命令将从数据库提升成主数据继续服务。

  2. 启动之前崩溃的主数据库,然后使用SLAVEOF命令将其设置成新的主数据库的从数据库,即可同步数据。

注意: 当开启复制并且主数据库关闭持久化功能时,一定不能使用Suoervisor自动重启功能。当主数据库重启后,因为没有开启持久化功能,数据都被清空,从数据库也会同步清空,导致从数据库的持久化失去意义。

手动重启和恢复都相对麻烦,这时候就需要哨兵登场了。
哨兵是一个独立的进程:
在这里插入图片描述
哨兵本身也有单点故障的问题,所以在一个一主多从的Redis系统中,可以使用多个哨兵进行监控,哨兵不仅会监控主数据库和从数据库,哨兵之间也会相互监控。

在这里插入图片描述

哨兵实现原理

哨兵在启动进程时,会读取配置文件的内容,通过如下的配置找出需要监控的主数据库:

sentinel monitor master-name ip port quorum
master-name是主数据库的名字
ip和port 是当前主数据库地址和端口号
quorum表示在执行故障恢复操作前需要多少哨兵节点同意。

这里之所以只需要连接主节点,是因为通过主节点的info命令,获取从节点信息,从而和从节点也建立连接,同时也能通过主节点的info信息知道新增从节点的信息。

一个哨兵节点可以监控多个主节点,但是并不提倡这么做,因为当哨兵节点崩溃时,同时有多个集群切换会发生故障。
哨兵启动后,会与主数据库建立两条连接。

1.订阅主数据库_sentinel_:hello频道以获取同样监控该数据库的哨兵节点信息
2.定期向主数据库发送info命令,获取主数据库本身的信息。

和主数据库建立连接后会定时执行以下三个操作:

  1. 每隔10s向主数据库和从数据库发送info命令
  2. 每隔2s向主数据里和从数据库的_sentinel_:hello频道发送自己的信息。
  3. 每隔1s向所有数据库节点和所有哨兵节点发送ping命令。

第一条操作的作用是获取当前数据库信息,比如发现新增从节点时,会建立连接,并加入到监控列表中,当主从数据库的角色发生变化进行信息更新。

第二条操作的作用是将自己的监控数据和哨兵分享,发送的内容为:
<哨兵地址>,<哨兵端口>,<哨兵运行id>,<哨兵配置版本>,<主数据库名字>,<主数据库地址>,<主数据库端口>,<主数据库配置版本>,每个哨兵会订阅数据库的_sentinel_:hello频道,当其他哨兵收到消息后,会判断该哨兵是不是新的哨兵,如果是则将其加入哨兵列表,并建立连接。

第三条操作的作用是监控节点是否存活。该时间间隔有down-after-millisecond实现,当该值小于1s时,哨兵会按照设定的值发送ping,当大于1s时,哨兵会间隔1s发送ping命令。

主观下线和客观下线

当超过down-after-millisecond时间后,如果节点未回复,则哨兵认为主观下线。主观下线表示当前哨兵认为该节点已经下面,如果该节点为主数据库,哨兵会进一步判断是够需要对其进行故障恢复,这时候就要发送SENTINEL is-master-down-by-addr命令询问其他哨兵节点是否认为该主节点是主观下线,当达到指定数量时,哨兵就会认为是客观下线,该数量由sentinel monitor master-name ip port quorum的quorum参数设定。
当主节点客观下线时就需要进行主从切换,主从切换的步骤为:

  1. 选出领头哨兵
  2. 领头哨兵从在线的从数据库中,选择优先级最高的从数据库。优先级可以通过slave-priority选项设置。
  3. 如果优先级先攻,则从复制的命令偏移量越大(即复同步数据越多,数据越新),越优先。
  4. 如果以上条件都一样,则选择run ID较小的从数据库。

选择一个从数据库后,哨兵发送slave no one命令升级为主数据库,并发送slaveof命令将其他从节点的主数据库设置为新的主数据库。

其中选择领头哨兵的过程使用了Raft算法,其具体思想如下:

  1. 发送主数据库客观下线的哨兵向每个哨兵命令,要求对方选择自己为领头。
  2. 如果没有选择过其他哨兵,则会同意请求
  3. 如果发现有超过半数,且超过quorum的哨兵同意自己的请求,则自己就是哨兵领头。

这种算法在多个哨兵同时参选时,会出现没有任何节点当选的可能。此时,每个哨兵会等待一个随机时间重新参选,每个节点的随机时间不一定相同,进行下一轮选举,直到成功。

猜你喜欢

转载自blog.csdn.net/u012133048/article/details/88558657