一 主从结构必要性的背景
redis主从结点的必要性,一般来说我们为了防止redis主结点宕机没法立即恢复数据都会对单节点redis做主从结构,另外我们做主从结构也可以做读写分离,提高其吞吐量
读写分离提高吞吐量,单机5W并发能力情况下,则该主从结构可以达到5w写,20w读
但是用redis主从结构我们就要明白redis主从结构数据同步的原理了。
二 Redis的主从赋值replica/同步机制
为了让redis支持数据的弱一致性即最终一致性,我们不需要保证master和slave的数据是同步的,但是过了一段时间,他们的数据是最终同步
根据同步内容的多少可以分为全同步和增量同步
2.1 全同步过程
-
slave node内部有个定时任务
,每秒检查是否有新的master node要连接和复制,如果发现,就跟master node建立socket网络连接(关于连接ip,slave node启动,仅仅保存master node的信息,包括master node的host和ip,但是复制流程没开始,master host和ip是从哪儿来的,redis.conf里面的slaveof配置的) - slave node发送
ping
命令给master node -
口令/密码认证
,如果master设置了口令(密码)requirepass
,那么salve node必须发送master auth的口令过去进行认证 -
Salve主动
发送sync
命令到Master - Master接收到
SYNC
命令后,会异步开一个线程
开始执行BGSAVE
命令生成RDB文件
并使用缓冲区
记录此后执行
的所有写命令;(默认:如果rdb复制时间超过60秒(repl-timeout
),那么slave node就会认为复制失败,可以适当调节大
这个参数,对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s) - Master
BGSAVE执行完后
,向所有从服务器发送快照文件
,并在发送期间继续记录被执行的写命令; - Salve接收到文件后将文件
保存到磁盘
上,然后加载
文件到内存
恢复数据快照到Salve的Redis上
(client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败) - 当
Salve完成数据快照的恢复后
,Master将这期间缓冲区收集的写命令发送给Salve进行回放 - 如果slave node开启了AOF,那么在全量同步结束后slove node会立即执行
BGREWRITEAOF
,重写AOF -
后续Master收集到的写命令
都会通过之前建立的连接
,增量
发送给salve端
2.2 增量同步过程
Redis全量同步后,新数据同步
到从结点的方式,或者全量同步中断
后进行主从数据同步的方式都为增量复制
- 中断后的增量同步
- 1 如果全量复制过程中,master-slave网络连接断掉,那么salve重新连接master时,会触发增量复制
- 2 master直接从自己的
backlog中获取部分丢失的数据
,发送给slave node,默认backlog就是1MB - 3 msater就是根据
slave发送的psync中的offse
t来从backlog中获取数据的
- 新数据的增量同步
- Master接收到用户的操作指令,判断是否需要传播到Slave(增删改需要,读不需要)
- 将操作记录追加到AOF文件
- 将操作传播到其他Slave:
- 没有办法对master进行动态选举,需要使用Sentinel机制或者Redis cluster完成动态选举。
- 只解决了高可用,但是没用解决容量问题.
- 如果多个Slave断线了,需要重启的时候,因为只要Slave启动,就会发送sync请求和主机全量同步,当多个同时出现的时候,可能会导致Master IO剧增宕机
三 主从复制的核心要点我们需要关注和理解
(1)master和slave都会维护一个offset
master会在自身不断累加offset,slave也会在自身不断累加offset
slave每秒都会上报自己的offset给master
,同时master也会保存每个slave的offset
这个倒不是说特定就用在全量复制的,主要是master和slave都要知道各自的数据的offset,才能知道互相之间的数据不一致的情况
(2) backlog
master node有一个backlog,默认是1MB大小
master node给slave node 复制数据时
,也会将数据在backlog中同步写一份
backlog
主要是用于全量复制中断后
的增量复制,引用:backlog
(3)主从复制基于backlog
和 offset
的断点续传
从redis 2.8
开始,就支持主从复制的断点续传
,如果主从复制过程中,网络连接断掉了
,那么可以接着上次复制的地方,继续复制
下去,而不是从头开始复制一份 ,master node
会在内存
维护一个backlog
,master和slave都会保存一个replica offset
还有一个master id
,offset就是保存在backlog中的
。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制.
但是如果没有找到对应的offset,那么就会执行一次resynchronization
(4) 无磁盘化复制
master直接在内存中创建rdb
,然后发送给slave
,不会在自己本地落地磁盘了
开启方式
repl-diskless-sync no //设置开启关闭,默认关闭
repl-diskless-synd-delay 5 //设置等待时间等待一定时长再开始复制,因为最好一次复制中更多结点的同步机会。如果等到更多slave重新连接过来可以一次复制同步更多结点,减少生成rdb的次数。
(5)run id
info server
,可以看到master run id
每个Redis服务器都会有一个表明自己身份的ID,这就是run id . 在主从复制时候如果根据host ip定位master node,是不靠谱的,如果master node重启或者数据出现了变化,那么slave node应该根据不同的run id区分
, run id不同就做全量复制.
为什么说主从复制时候这里根据host ip定位master node,是不靠谱的?因为有的时候我们发现master结点数据错误要恢复到20小时之前的数据,那么这个时候run id变了,数据变了,但是其host和ip是没变的,如果我们还拿着之前的offset进行同步肯定会出现问题,所以即使host和ip没变,当run id变化的时候,我们就要知道数据已经变了,我们就要进行全量复制了。 如果需要不更改run id重启redis,可以使用redis-cli debug reload命令
(6)异步复制
master每次接收到写命令之后,现在内部写入数据,然后异步发送给slave node
(7)乐观复制
Redis采用了乐观复制的策略
,也就是在一定程度内容忍主从数据库的内容不一致
,但是保持主从数据库数据的最终一致性
。上面第六点也说了,Redis在主从复制的过程中,本身就是异步的,在主从数据库执行完客户端请求后会立即将结果返回给客户端,并异步的将命令同步给从数据库,但是这里并不会等待从数据库完全同步之后,再返回客户端。 这一特性虽然保证了主从复制期间性能不受影响,但是也会产生一个数据不一致的时间窗口,如果在这个时间窗口期间网络突然断开连接,就会导致两者数据不一致。
如果不在配置文件中添加其他策略,那就默认会采用这种方式,乐观二字也就体现在这里。 当然了,上面这种方式并不是绝对的
,只要牺牲
一点性能
,还是可以避免
上述问题。在配置文件中:
代表
至少N台从服务器完成复制,才允许主服务器可写入,否则会返回错误。
min-slaves-to-write
3 #允许从服务器断开连接的时间(单位s) min-slaves-max-lag 10