原创

Redis主从搭建

Redis主从搭建

1.主从同步的意义

实现读写分离,在 redis 主从架构中,Master 节点负责处理写请求,Slave 节点只处理读请求。对于写请求少,读请求多的场景,例如电商详情页,通过这种读写分离的操作可以大幅提高并发量,通过增加 redis 从节点的数量可以使得 redis 的 QPS 达到 10W+。

redis主从架构
redis主从架构

2.主从搭建

2.1 主 redis 配置如下:

# basic
daemonize yes
port 6379
logfile /data/redis/redis/6379/redis_6379.log
pidfile /data/redis/redis_6379.pid
dir /data/redis/redis6379
# aof
# 主节点打开AOF机制
appendonly yes

# master
# 绑定本台机器的IP,否则主从节点无法通信
bind 192.168.239.101
# 设置master的认证口令为redis
requirepass redis
# backlog大小
repl-backlog-size 1mb
# 快照同步的超时时间
repl-timeout 60
# 开启无盘复制
repl-diskless-sync yes
# 无盘复制的延迟默认为5s,是为了等待更多的slave连接
repl-diskless-sync-delay 5
# 是否开启主从节点复制数据的延迟机制
# 当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小
# 但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景
# 当开启时,主节点会合并较小的TCP数据包从而节省带宽。
# 默认发送时间间隔取决于Linux的内核,一般默认为40毫秒。
# 这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂或带宽紧张的场景
repl-disable-tcp-nodelay no
# 触发快照同步的条件
# 如果增量同步的缓存大于256MB,或者超过60s大于64MB,则触发快照同步
client-output-buffer-limit slave 256mb 64mb 60
# 主从节点进行心跳的时间间隔
repl-ping-slave-period 10

2.2slave 节点的配置如下

# basic
daemonize yes
port 6379
logfile /data/redis/redis_6379.log
pidfile /data/redis/redis_6379.pid
dir /data/redis/redis6379
# slave
# 绑定本机的IP,另一个为192.168.239.103
bind 192.168.239.102
# 绑定master的ip和port
slaveof 192.168.239.101 6379
# 从节点只读
slave-read-only yes
# 从节点在处于快照同步期间是否对外提供服务
slave-serve-stale-data yes
# 如果 master 检测到 slave 的数量小于这个配置设置的值,将拒绝对外提供服务,0 代表,无论 slave 有几个都会对外提供服务
min-slaves-to-write 0
# 如果 master 发现大于等于 ${min-slaves-to-write} 个 slave 与自己的心跳超过此处配置的时间(单位s)
# 就拒绝对外提供服务
min-slaves-max-lag 10
# master的认证口令
masterauth redis

2.3 重启两个 redis 服务

redis-server /data/redis/redis_6379.conf
redis-server /data/redis/redis_6379.conf

2.4 查看主从日志

vim /data/redis/redis_6379.log

2.5 测试主从是否成功

Master 执行命令:

redis-cli
#输入密码
auth redis
set gameboys gameboys

Slave 执行命令:

get gameboys

3.主从同步的方法:

2.1 增量同步

redis 同步的是指令流,主节点会将那些对自己的状态产生修改性影响的指令记录在本地的内存 buffer 中,然后异步将 buffer 中的指令同步到从节点,从节点一边执行同步的指令流来达到和主节点一样的状态,一边向主节点反馈自己同步到哪里了 (偏移量,这是 redis-2.8 之后才有的特性)。从节点同步数据的时候不会影响主节点的正常工作,也不会影响自己对外提供读服务的功能,从节点会用旧的数据来提供服务,当同步完成后,需要删除旧数据集,加载新数据,这个时候才会暂停对外服务。

因为内存的 buffer 是有限的,所以 redis 主节点不能将所有的指令都记录在内存 buffer 中。redis 的复制内存 buffer 是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。

2.2 快照同步

如果节点间网络通信不好,那么当从节点同步的速度不如主节点接收新写请求的速度时,buffer 中会丢失一部分指令,从节点中的数据将与主节点中的数据不一致,此时将会触发快照同步。

快照同步是一个非常耗费资源的操作,它首先需要在主节点上进行一次 bgsave 将当前内存的数据全部快照到 RDB 文件中,然后再将快照文件的内容全部传送到从节点。从节点将 RDB 文件接受完毕后,立即执行一次全量加载,加载之前先要将当前内存的数据清空。加载完毕后通知主节点继续进行增量同步。

在整个快照同步进行的过程中,主节点的复制 buffer 还在不停的往前移动,如果快照同步的时间过长或者复制 buffer 太小,都会导致同步期间的增量指令在复制 buffer 中被覆盖,这样就会导致快照同步完成后无法进行增量复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环。所以需要配置一个合适的复制 buffer 大小参数,避免快照复制的死循环。

2.3 无盘复制

主节点在进行快照同步时,会进行大量的文件 IO 操作,特别是对于非 SSD 磁盘存储时,快照会对系统的负载产生较大影响。特别是当系统正在进行 AOF 的 fsync 操作时如果发生快照复制,fsync 将会被推迟执行,这就会严重影响主节点的服务效率。

从 Redis 2.8.18 版开始支持无盘复制。所谓无盘复制是主节点会一边遍历内存,一遍将序列化的内容发送到从节点,而不是生成完整的 RDB 文件后才进行 IO 传输从节点还是跟之前一样,先将接收到的内容存储到磁盘文件中,再进行一次性加载

4.主从同步的流程:

(1) 在从节点的配置文件中的 slaveof 配置项中配置了主节点的 IP 和 port 后,从节点就知道自己要和那个主节点进行连接了。

(2) 从节点内部有个定时任务,会每秒检查自己要连接的主节点是否上线,如果发现了主节点上线,就跟主节点进行网络连接。注意,此时仅仅是取得连接,还没有进行主从数据同步。

(3) 从节点发送 ping 命令给主节点进行连接,如果设置了口令认证(主节点设置了 requirepass),那么从节点必须发送正确的口令(masterauth)进行认证。

(4) 主从节点连接成功后,主从节点进行一次快照同步。事实上,是否进行快照同步需要判断主节点的 run id,当从节点发现已经连接过某个 run id 的主节点,那么视此次连接为重新连接,就不会进行快照同步。相同 IP 和 port 的主节点每次重启服务都会生成一个新的 run id,所以每次主节点重启服务都会进行一次快照同步,如果想重启主节点服务而不改变 run id,使用 redis-cli debug reload 命令。

(5) 当开始进行快照同步后,主节点在本地生成一份 rdb 快照文件,并将这个 rdb 文件发送给从节点,如果复制时间超过 60 秒(配置项:repl-timeout),那么就会认为复制失败,如果数据量比较大,要适当调大这个参数的值。主从节点进行快照同步的时候,主节点会把接收到的新请求命令写在缓存 buffer 中,当快照同步完成后,再把 buffer 中的指令增量同步到从节点。如果在快照同步期间,内存缓冲区大小超过 256MB,或者超过 64MB 的状态持续时间超过 60s(配置项:client-output-buffer-limit slave 256MB 64MB 60),那么也会认为快照同步失败。

(6) 从节点接收到 RDB 文件之后,清空自己的旧数据,然后重新加载 RDB 到自己的内存中,在这个过程中基于旧的数据对外提供服务。如果主节点开启了 AOF,那么在快照同步结束后会立即执行 BGREWRITEAOF,重写 AOF 文件。

(7) 主节点维护了一个 backlog 文件,默认是 1MB 大小,主节点向从节点发送全量数据(RDB 文件)时,也会同步往 backlog 中写,这样当发送全量数据这个过程意外中断后,从 backlog 文件中可以得知数据有哪些是发送成功了,哪些还没有发送,然后当主从节点再次连接后,从失败的地方开始增量同步。这里需要注意的是,当快照同步连接中断后,主从节点再次连接并非是第一次连接,所以进行增量同步,而不是继续进行快照同步。

(8) 快照同步完成后,主节点后续接收到写请求导致数据变化后,将和从节点进行增量同步,遇到 buffer 溢出则再触发快照同步。

(9) 主从节点都会维护一个 offset,随着主节点的数据变化以及主从同步的进行,主从节点会不断累加自己维护的 offset,从节点每秒都会上报自己的 offset 给主节点,主节点也会保存每个从节点的 offset,这样主从节点就能知道互相之间的数据一致性情况。从节点发送 psync runid offset 命令给主节点从而开始主从同步,主节点会根据自身的情况返回响应信息,可能是 FULLRESYNC runid offset 触发全量复制,也可能是 CONTINUE 触发增量复制。

(10) 主从节点因为网络原因导致断开,当网络接通后,不需要手工干预,可以自动重新连接。

(11) 主节点如果发现有多个从节点连接,在快照同步过程中仅仅会生成一个 RDB 文件,用一份数据服务所有从节点进行快照同步。

(12) 从节点不会处理过期 key,当主节点处理了一个过期 key,会模拟一条 del 命令发送给从节点。

(13) 主从节点会保持心跳来检测对方是否在线,主节点默认每隔 10 秒发送一次 heartbeat,从节点默认每隔 1 秒发送一个 heartbeat。

(14) 建议在主节点使用 AOF+RDB 的持久化方式,并且在主节点定期备份 RDB 文件,而从节点不要开启 AOF 机制,原因有两个,一是从节点 AOF 会降低性能,二是如果主节点数据丢失,主节点数据同步给从节点后,从节点收到了空的数据,如果开启了 AOF,会生成空的 AOF 文件,基于 AOF 恢复数据后,全部数据就都丢失了,而如果不开启 AOF 机制,从节点启动后,基于自身的 RDB 文件恢复数据,这样不至于丢失全部数据。

5.总结:

Redis 主从读写分离是为了提高并发量;

Redis 主从同步有三种方法:

增量同步是同步指令流,运行较快。

快照同步是增量同步的 buff 丢失,是一个非常耗费资源的操作,他会在 master 执行 bgsave,生成一个 rdb,然后 slave 通过 rdb 来同步,这里注意需要配置一个合适的复制 buffer 大小参数,避免快照复制的死循环。

无盘复制是从 Redis 2.8.18 版开始支持无盘复制。逻辑是主节点会一边遍历内存,一遍将序列化的内容发送到从节点。

正文到此结束