前言
回顾一下Redis集群的相关知识点
-
配置
- 一个Redis数据库系统有一个主数据库,多个从数据库
- 主数据库不需要做任何配置,从数据库在配置中加入–slaveof ip port即可
- 从数据库默认只读,就算改成可写也没有实际用处
- 除了在配置文件中添加,也可以在运行时使用slaveof命令,来修改主数据库
-
持久化回顾
- RDB 该方式将Redis进程fork一份,子进程将内存写入文件,然后替换之前的RDB快照
- AOF 该方式Redis将每条命令都写入文件中。当AOF文件太大的时候,每追加一定数量的命令时,需要重写一次AOF文件,因为其中有些命令时冗余的。写入文件由于操作系统有缓存,可以进行配置。不配置的情况下30s写入磁盘一次。可以配置为每条命令都同步缓存,也可以配置为每秒同步一次。
原理
- 初始化复制
-
- 当从数据库启动后,向主数据库发送SYNC命令
-
- 主数据库进行一次快照保存,同时在保存快照时将命令缓存下来,然后将快照与保存快照期间的命令一起发送给从数据库
-
- 如果断线之后,Redis2.6以及以前的版本会重新进行初始化复制。Redis2.8之后的版本可以近传输断线期间的命令。
-
- 从数据库将接受到的临时快照替换为配置中设定的RDB快照文件。之后的操作和RDB持久化恢复的过程一致。
-
- 复制同步
- 之后,主数据库会将导致数据库变化的命令发送给从数据库
- Redis复制策略使用的是乐观复制,存在不一致时间窗。主数据库可以配置当有多少数据库连接时可写,以及允许数据库最长失去链接时间。可以降低一定程度的数据不一致问题。
读写分离提高性能
一些带有耗时的读操作可以通过复制功能建立多个数据库从节点来加快速度
slave数据库持久化
使用从数据库时可以关闭主数据库的持久化。但是主数据库崩溃的时候,主从数据库需要进行身份转换才可以恢复主数据库的内容。从数据库使用slaveof no one,主数据库使用slaveof命令设置为从数据库。
这种情况下,如果主数据库崩溃后,不能立刻重新启动。因为如果此时从新启动,主数据库因为没有持久化,就会将从数据库中的内容清空。
无硬盘复制
主从复制可以通过RDB持久化方式来实现初始化同步。 当主数据库禁用RDB快照,但是当执行了初始化复制,仍然会生成RDB快照。此时主数据库重启动时将会使用RDB快照进行恢复。因为同步时间点不确定,甚至可能很久都没有进行初始化同步。这可能导致恢复的时间点可能是任意的。
另外,初始化同步时需要创建RDB快照,如果磁盘速度太慢可能会影响同步的效率。
从Redis2.8.18开始,Redis引入了无硬盘复制,开启该选项的时候,初始化复制将不再创建RDB快照,而是直接通过网络将RDB快照内容发送走。
增量复制
主数据库和从数据库如果断开链接后,下一次重新链接如果使用初始化复制,开销会比较高。
- 数据库在每次运行时,会生成唯一ID。
- 复制同步阶段,主数据库会将每一个命令放入回放日志中。并记下当前命令的偏移量。
- 从数据库接受命令时,也需要记录下命令偏移量。
- 当主从连接准备就绪之后,从数据库发送psync来告诉主数据库可以使用增量复制。
- 主数据库判断从数据库发送的id和自己是否相同。不相同的情况下,主数据库可能已经重启过了。
- 然后判断从数据库的命令偏移量是否和在backlog回放日志中,如果在,将回放日志中的命令发给从数据库。
- backlog的大小可以配置,默认时1MB。越大可以使断线重连后的容忍程度越长。
哨兵
哨兵系统的出现,目的是为了解决之前的主从数据库的维护的不方便。以前存在两个问题,主数据库挂了无法感知。主数据库挂了之后,主从转换问题。
哨兵进程启动时,会读取配置文件。配置文件中有master-name ip port 。当主节点故障时,哨兵将自动转换其中一个从节点,变为主节点。当之前的主节点恢复后,会自动转变为从节点。
哨兵启动之后,会定时执行下面3个操作:
- 10秒向master、slave发送INFO
- 2秒向master、slave的
__sentinel__:hello
发送自己的信息 - 1秒向主数据库、从数据库、和其他哨兵发送PING
INFO信息使得哨兵可以获取当前数据库的相关信息。向master发送INFO可以获取其从数据库。 向频道发送自己的信息,所有哨兵可以收到监控同一个数据库的其他哨兵的信息。 哨兵之间建立连接目的是为了发送ping信息。 哨兵发送ping命令时没有进行回复,就会认为其主观下线。如果发现一个主数据库主观下线,就会询问其他哨兵是否认为其主观下线。如果达到一定数量的时候,超过哨兵启动参数中设置的quorum,哨兵认为其客观下线。
RAFT选举:
- 发现主数据库客观下线的哨兵A向其他哨兵发送命令,请求选自己成为领头哨兵。
- 如果被请求的哨兵还没有选过其他人,则会同意
- 如果A发现同意的数量超过了哨兵数量的一半和quorum参数,则A成为领头哨兵。
- 否则,随机等待一个时间重新发起请求,进行下一轮选举
领头哨兵会进行如下操作:
- 查看从数据库的优先级,优先级越高的选他
- 查看从数据库的复制命令偏移量,偏移量越大的选他
- 选择ID小的从数据库
被选中的数据库会被领头哨兵发送slaveof no one成为主数据库,其他从数据库会收到slaveof命令更改其主数据库。最后,更改内部记录,将已经停止的数据库更新为主数据库的从数据库,使当其恢复服务时以从数据库的身份恢复。
部署
哨兵一般部署在每个节点上,如果redis集群很大,所有哨兵与一个从数据库之间的连接数量太多会影响redis对所有哨兵的回复。所以部署的数量不能太多。
集群
水平扩容这块,在腾讯实习期间解除了codis。codis分为redis proxy和从节点。主要原理就是对key进行hash之后,放入到具体的redis slave中。脚本在执行的过程中,被分配到具体的slave中执行,可能会遇到找不到其他key的情况。
除了codis,还有redis cluster。
在启动redis实例时,配置中cluster-enables改为yes,即可打开集群配置。
使用脚本可以方便的创建集群。增加新节点可以使用cluster meet命令添加ip和端口。该节点添加新节点后,会使用gossip协议将该节点的信息广播到所有节点。
插槽的分配。新的节点加入集群后有两种选择,要么使用cluster replicate命令复制每个主数据库来以从数据库的形式运行,要么向集群申请分配插槽slot来以主数据库的形式运行。
key使用crc16做hash运算,如果存在{*},则使用该部分做hash运算,然后分给16384个槽中的一个。集群在创建的开始时就有一个槽分配的映射表。最终数据会存在对应的redis节点中。