Redis Replication(主从复制)
Redis复制概述
主从配置
在Redis Replication中,节点分为master和slave两个角色,复制是单向由master复制到slave中。配置slave同步有三种方式:
- 在配置文件中加入slaveof {master_host} {master_port}
- redis-server启动实例时加入–slaveof {master_host} {master_port}
- 直接执行slaveof {master_host} {master_port}命令
Tips:如果启用了AUTH认证登陆还需要指定MASTERAUTH参数来设置主节点的密码
当slaveof执行后,复制流程便开始工作。其主要步骤如下:
- 保存master信息(IP和PORT)
- 从节点内部每秒运行定时任务维护复制逻辑,在发现master时建立socket来接收master发送的命令,如果无法连接会一直尝试
- 发送PING命令
- 权限验证
- 同步数据集
- 持续复制
数据同步
当主从配置完成可以通过info replication命令查看复制状态
master: |
默认情况下,从节点为只读模式,由参数slave-read-only控制,为避免修改从节点带来的数据不一致,强烈建议设置为只读模式。如果想要断开当前复制,可以执行slave no one断开主从的复制关系。
当Redis复制跨机房部署,就需要考虑网络延迟对复制的影响。Redis提供了参数repl-disable-tcp-nodelay控制是否关闭TCP_NODELAY,默认为关闭。当关闭时,主节点的命令数据无论大小都会及时发送给slave,这样就减少延迟增加了网络带宽消耗;当开启时,主节点会合并较小的TCP数据库包节省带宽,默认发送时间取决于Linux内核,通常为40毫秒。
psync
Redis 2.8开始采用psync命令完成主从数据同步,同步分为全量复制和部分复制。全量复制一般用于初始化复制阶段,它会把master所有数据一次性发送给slave;部分复制用于处理因网络闪断等原因造成的同步数据丢失,当slave再次连上时master会重新发送丢失的数据。
psync命令需要复制偏移量、积压缓冲区、运行ID来支持。
- 复制偏移量:每个节点都会维护自身的复制偏移量,master执行完命令后会把命令的字节长度做累加记录,其指标在master_repl_offset中可以查看。slave每秒上报自身的复制偏移量给master,因此master也保留了slave的复制偏移量。slave在同步执行命令后也会累加自身的复制偏移量,slave_repl_offset可以看到。
- 复制积压区:复制积压区是master上一个固定长度的队列,默认为1MB,master不仅将命令发送给slave,也会同步也入复制积压区。该队列采用先进先出,能够保存最近复制的数据。在master是上可以通过info replication查看积压区相关信息,repl_backlog_archive=1表示开启复制缓冲区,repl_backlog_size为缓冲区最大长度,repl_backlog_first_byte_offset表示起始偏移量,repl_backlog_histlen表示已保存数据的长度。
- 运行ID:Redis在启动时会分配一个40位的十六进程字符串作为运行ID,能够标识唯一Redis实例。当Redis重新启动时,运行ID也随之改变,从节点将做全量复制。如果想要重启后运行ID不变可以debug reload命令重新加载并保持ID不变,但命令会阻塞Redis进程,生成RDB文件并清空数据再加载RDB文件。
psync命令格式为
psync {runId} {offset} |
全量复制
全量复制是Redis最早开始就支持的复制方式,主要通过psync命令实现。全量复制的工作流程如下:
- 发送psync命令进行数据同步,由于是第一次进行复制,会直接发送psync ? -1
- 主节点根据psync ? -1判断为全量复制,回复+FULLRESYNC
- slave接收到master的运行ID和偏移量
- 主节点执行bgsave生成RDB文件到本地
- 主节点发送RDB持久化文件给slave,需要注意的是如果RDB文件比较大,传输将会比较耗时,并且时间超过repl-timeout(默认60秒),从节点会放弃接收RDB并清空已下载的文件,导致复制失败
- 主节点仍然响应读写,并把期间产生的数据命令保存在复制客户端缓冲区,待全量复制完成后再批量发送给slave。期间,如果全量复制时间太长,容易造成复制客户端缓冲区内存溢出。参数默认配置为client-output-buffer-limit slave 256MB 64MB 60,其表示为60秒内缓冲区消耗持续大于64MB或者直接超过256MB时,主节点自动关闭slave连接,导致复制失败。
- slave在接收完成后会后台清空自身数据
- slave加载master传过来的RDB文件
- 加载完成后,如果slave开启了AOF持久化,会立即做bgrewriteaof操作
全量复制花费的时间=master bgsave的时间+RDB文件传输时间+slave清空并加载RDB文件时间
Tips:Redis 2.8.18开始支持无盘复制,能够通过子进程直接网络发送RDB文件,适用于磁盘IO性能较差的环境,由参数repl-diskless-sync控制
部分复制
部分复制是对全量复制做出的优化,当网络闪断等异常情况时,slave会向master重新同步丢失的命令数据,如果master上的复制积压区还存在这部分数据则会直接发送给slave。从而避免了不必要的全量复制,减小复制开销。其工作流程如下:
- slave通过保存的master运行ID和自身复制偏移量运行psync命令
- master验证运行ID并根据offset在复制积压缓冲区查找,如果存在则发送+CONTINUE给slave,表示进行部分复制。
- master把数据发送给slave,保证slave进入正常复制状态。
心跳
主从复制会建立长连接并批次发送心跳检测命令,各自模拟成客户端与对方通信,主节点默认10秒发送一次PING命令,判断slave的是否存活可连接;slave每1秒发送replconf ack {offset}命令,上报自身偏移量给master。
Redis主从安装配置
解压安装包
$ tar -xvf redis-4.0.9.tar.gz |
编译安装
$ cd redis-4.0.9 |
创建文件夹
$ mdkir /service/redis/data |
配置参数文件
基础参数
参数 | 建议值 | 说明 |
---|---|---|
daemonize | yes | 是否以后台进程启动 |
databases | 6 | 创建database的数量(默认选中的是database 0) |
port | 自定义 | 进程对应的端口,默认为6379 |
tcp-keepalive | 60 | 指定TCP连接是否为长连接,0则关闭,非0则开启 |
loglevel | notice | Server日志级别:debug调试模式、verbose、notice、warning |
protected-mode | no | 外网保护模式,禁止外网访问redis,如果要设置为no,需确保数据库不会暴漏在外网,设置为YES的话需要设置bind绑定内网IP或设置实例密码 |
pidfile | 自定义 | 指定pid文件的存放位置 |
logfile | 自定义 | 指定日志文件的存放位置 |
requirepass | 自定义 | 设置redis登录密码 |
maxmemory | 80% | 设置redis最大内存大小,单位为byte |
bind | 自定义 | 设置绑定的网口IP,用于接收服务请求 |
hz | 10 | Server执行后台任务的频率 |
持久化参数
参数 | 建议值 | 说明 |
---|---|---|
Save |
生产仅用做缓存时不建议开启 | rdb持久化策略,例如save 300 1表示5分钟内至少一个key变更则触发持久化操作,禁用rdb持久化可设置save “” |
rdbcompression | yes | 是否启用rdb文件压缩,默认为yes |
rdbchecksum | yes | 是否对rdb文件使用CRC64校验和 |
stop-writes-on-bgsave-error | yes | 当bgsave持久化写入错误时是否停止 |
dbfilename | 自定义 | 设置rdb文件名称,默认为dump.rdb |
dir | 自定义 | 指定持久化文件的存放位置 |
appendonly | yes | 是否开启AOF持久化 |
appendfilename | 自定义 | 指定AOF持久化文件名称,默认为appendonly.aof |
appendfsync | everysec | AOF持久化策略,可选值有always、everysec、no |
no-appendfsync-on-rewrite | no | 表示rewrite重写AOF时,是否阻塞AOF持久化,no表示会阻塞 |
auto-aof-rewrite-percentage | 100 | 触发AOF文件rewrite的条件之一,AOF文件大小增长一倍 |
auto-aof-rewrite-min-size | 64mb | 触发AOF文件rewrite的条件之一,AOF文件大小大于64M |
aof-rewrite-incremental-fsync | yes | AOF rewrite是否采取增量文件同步策略 |
复制参数
参数 | 建议值 | 说明 |
---|---|---|
slaveof |
自定义 | 当前为从库时,设置同步主库信息 |
masterauth | 自定义 | 当前为从库时,设置主库的登录密码 |
repl-timeout | 3600 | 复制超时时间,单位为秒,默认为60 |
slave-server-stale-data | yes | 当前为从库时,在与主库连接断开时,是否继续提供服务 |
slave-read-only | yes | 当前为从库时,可读 |
redis-disable-tcp-nodelay | no | 主从是否延迟传输 |
slave-priority | 自定义 | 指定slave的优先级,在哨兵模式下,主库宕机,会将优先级最小的提升为master。如果设置为0则永远不会提升为master |
慢日志参数
参数 | 建议值 | 说明 |
---|---|---|
slowlog-log-slower-than | 10000 | 慢查询日志记录阀值,单位为微秒,超过阀值将被记录慢查询日志 |
slowlog-max-len | 500 | 控制slow log保留多少条记录,超过后将删除最旧的一条记录 |
对象参数
参数 | 建议值 | 说明 |
---|---|---|
hash-max-ziplist-entries | 512 | hash类型默认采用ziplist结构编码,超过该值则采用hashtable |
hash-max-ziplist-value | 64 | ziplist允许条目value值的最大字节数 |
list-max-ziplist-entries | 512 | list类型默认采用ziplist结构编码,超过该值则采用linkedlist |
list-max-ziplist-value | 64 | ziplist允许条目value值的最大字节数 |
set-max-intset-entries | 512 | intset中允许保存的最大条目数,如果达到阀值,才重构为hashtable |
zset-max-ziplist-entries | 128 | zset类型默认采用ziplist编码结构,超过阀值将重构为skiplist |
Zset-max-ziplist-value | 64 | zset允许条目value值的最大字节数 |
activerehashing | yes | 是否开启顶层数据结构得rehash功能 |
客户端及安全配置
参数 | 建议值 | 说明 |
---|---|---|
client-output-buffer-limit normal |
0 0 0 | 客户端buffer控制,normal表示普通连接,hard表示最大值,一旦到达立即关闭连接,soft表示容忍值,和seconds配合表示在超过seconds则关闭连接,都设置为0则表示禁用 |
client-output-buffer-limit slave |
0 0 0 | 客户端buffer控制,slave表示从库连接,hard表示最大值,一旦到达立即关闭连接,soft表示容忍值,和seconds配合表示在超过seconds则关闭连接,都设置为0则表示禁用 |
client-output-buffer-limit pubsub |
32mb 8mb 60 | 客户端buffer控制,pubsub表示pub/sub类型连接,hard表示最大值,一旦到达立即关闭连接,soft表示容忍值,和seconds配合表示在超过seconds则关闭连接,都设置为0则表示禁用 |
rename-command KEYS alias-keys | 重命名keys命令,生产环境执行keys *可能会造成实例阻塞 | |
rename-command FLUSHALL alias-flushall | 重命名FLUSHALL命令 | |
rename-command FLUSHDB alias-flushdb | 重命名FLUSHDB命令 |
参数文件(/etc/redis.conf)
daemonize yes |
Tips: 从库还需要配置slaveof和masterauth参数
配置环境变量
$ echo "export PATH=$PATH:/usr/local/redis/bin" >> /etc/profile |
启动数据库并查看主从同步
$ redis-server /etc/redis.conf |
keepalived安装配置
由于Redis主从并不提供高可用容灾切换,因此我们可以通过keepalived去触发VIP切换,程序通过VIP连接感知较小
安装keepalived
yum install keepalived -y |
配置keepalived(主)
$ cat /etc/keepalived/keepalived.conf |
$ cat /etc/keepalived/redis_check.sh |
$ cat /etc/keepalived/redis_master.sh |
$ cat /etc/keepalived/redis_backup.sh |
$ cat /etc/keepalived/redis_default.sh |
$ cat /etc/keepalived/redis_stop.sh |
配置keepalived(从)
$ cat /etc/keepalived/keepalived.conf |
$ cat /etc/keepalived/redis_check.sh |
$ cat /etc/keepalived/redis_master.sh |
$ cat /etc/keepalived/redis_backup.sh |
$ cat /etc/keepalived/redis_default.sh |
$ cat /etc/keepalived/redis_stop.sh |
启动keepalived
$ systemctl start keepalived |
附录
禁用服务自启动
在Redis主从复制的情况下,如果主服务器关闭了持久化,那应该将数据库服务自启动关闭禁用,否则在主节点异常自动重启后数据将被全部清空,而slave因为master的运行ID变化开始做全量复制,那么从节点的数据也将被连带清空。
ROLE命令
Redis 2.8.12开始提供了一个新的查询复制信息的命令。
10.0.139.163:6379> ROLE |
1)master为当前角色
2)当前主复制偏移量
3)一个包含三个元素的数组,表示slave的IP,端口和偏移量
10.0.139.162:6379> ROLE |
1)slave为当前角色
2)master节点IP
3)master节点端口
4)connect表示实例需要连接到master服务器,connecting表示正在连接master服务器,sync表示正在与master进行同步,connected表示slave online
5)slave节点的偏移量
1) "sentinel" |
1)sentinel为当前角色
2)sentinel实例监控的主机名数组
键过期
当master存储大量设置了TTL的数据时,Redis内部需要定期清理过期数据,删除策略分为惰性删除和定时删除。
- 惰性删除:主节点每次处理读命令时,都会检查键是否超时,如果超时则del删除对象,再把del命令异步发送给slave,slave不会主动删除过期键
- 定时删除:Redis主节点再内部定时循环采样一定数量的键,当发现采样的键过期时执行del命令再同步到slave
当大量数据超时,主节点采样速度跟不上且没有读取过过期键,slave也就没法收到del命令,这时在从节点上就能读到已经过期的键。Redis在3.2中解决了该问题,从节点读取时会检查键的过期时间来判断是否返回数据。
N个副本才允许写入
从Redis 2.8开始,可以将Redis设置为仅在当前至少有N个副本连接到master才允许执行写入。如果至少有N个slave副本,并且延迟小于M秒,则接受写入命令
min-replicas-to-write <number of replicas> |