Redis 4.x系列(十五):Redis 主从复制、调优和故障分析
为解决单个数据库可能存因系统崩溃、设备异常等单点故障导致整个应用系统无法提供服务的问题,数据库系统提供了复制技术来支持多存储服务的数据复制同步。
复制机制是读写分离的基础,也是是集群的基础。 Redis 也支持复制机制,可以将一个 Redis 服务器(master,主实例)中的数据复制到其他 Redis 服务器中(slave, 从实例)。
Redis Replication-官方资料, Redis 复制-中文译
复制
Redis 是单线程应用,在一台多核CPU的物理机上可以部署多个 Redis 实例以充分利用计算机资源,注意配置不同的端口。
主从配置
Redis 复制不需要配置主服务器,只需要配置从服务器,即可实现主从复制,配置比较简单。从实例默认开启了只读模式。
把配置文件 redis.conf 复制一份到 redis 的 bin 目录, 复制多份 redis bin 目录用于启动多个 redis 实例,目录名带端口号以作区分。
- 修改从实例的配置文件
在 Linux 环境下,使用 vim 打开 redis.conf 文件,搜索配置属性名, 修改属性值。1
2
3
4
5
6
7
8
9port 6380
pidfile /var/run/redis_6380.pid
dir ./slave
slaveof 127.0.0.1 6379
masterauth 123456 - 查看主从配置
开启两个 redis 客户端,分别连接主从服务器,使用info replication
命令查看主从配置。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37# ./redis-cli -a 123456 -p 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=5376,lag=0
master_replid:57cf5d4ab28b49bff6fbf283e0714e7fd63307c1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:5376
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:5376
# ./redis-cli -a 123456 -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:5348
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:57cf5d4ab28b49bff6fbf283e0714e7fd63307c1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:5348
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:5348 - 验证主从同步
在主实例上设置键值对,在从实例上获取键值对。1
2
3
4
5127.0.0.1:6379> set name tom
OK
127.0.0.1:6380> get name
"tom" - 验证从实例只读模式,在从实例设置键值对
1
2127.0.0.1:6380> set age 33
(error) READONLY You can't write against a read only slave. - 验证从实例重启后同步数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//关闭从实例
127.0.0.1:6380> shutdown
not connected>
//在主实例上设置键值对
127.0.0.1:6379> set nickName "Kitty"
OK
127.0.0.1:6379> get nickName
"Kitty"
//启动从实例
# ./redis-server redis.conf
//在从实例获取数据
127.0.0.1:6380> get nickName
"Kitty"
配置参数
- dir:存放持久化数据文件的目录,默认是存放在当前 bin 目录下,要存放到其它目录,目标目录必须存在,可用
mkdir
创建目录。- masterauth:如果 Redis 主服务器开启了客户端连接需要密码认证(requirepass),则需要配置
masterauth
属性,值为requirepass的值。- slaveof
。 :监听主实例的IP和端口, slaveof <masterip> <masterport>
。slaveof 同时也是 redis-cli 的命令,可动态将一个 redis 服务转换为另一个实例的从实例
复制信息
使用info replication
命令查看复制连接信息;还可以使用role
命令来查看简版本master-slave连接信息。
- master_replid:主实例启动时生成的随机字符串,标识主实例。
- master_repl_offset:复制主实例数据的一个偏移标记,会随主实例的数据增长而增长。从实例的该值等于当时主实例的repl_backlog_histlen的值。
- repl_backlog_size:复制数据的日志大小,与主实例中的该值相等。
复制分析
Redis 复制机制:共有两种重新同步机制,分别是部分同步和全量同步。
- 当 slave 第一次与 master 连接,或连接断开重新连上,从实例总会尝试通过发送(master_replid,master_repl_offset)请求进行部分同步。其中(master_replid,master_repl_offset)表示与主实例同步的最后一个快照。 master 会发一连串的命令流来保持对 slave 的更新。
- 如果 master 接受部分同步的请求,则从 slave 上次同步的最后一个偏移位开始增量同步,部分同步意味着只需获取断开连接期间丢失的命令流。
- 当无法进行部分同步时,slave 会请求全量同步。 master 会创建一个后台进程将所有数据转储到快照文件并将快照发文件(RDB 文件)送给 slave,slave 会清空所有数据,导入 RDB 文件中的数据,之后 master 在数据修改时持续发送命令到 slave。
- Redis 默认使用异步复制,是非阻塞的,即低延迟和高性能,此复制模式是绝大多数 Redis 实例天然的选择。当然也支持同步复制。
- 从实例重启后进行部分重新同步是 Redis 4.0 中的一个新特性。在 Redis 4.0 的实现中,master_replid 和 master_repl_offset 存储在 RDB 文件中。当优优雅地关闭并重启时,Redis 能够从 RDB 文件中读取这两个值,从而支持部分同步功能。
- slave 最好保持默认开启的只读模式,以避免与 master 之间的数据不一致。
- 在 Redis 4.0 之前,当 master_replid 发生改变,从实例是全量同步。 从 Redis 4.0 开始,新主实例会记录旧主实例的 master_replid 和 master_repl_offset。因此能够接受来自其他从实例的部分重新同步请求(即使请求中的 master_replid 不同)。更具体的说,当主实例发生故障迁移时,在新的主实例上,(master_replid , master_repl_offset+1。)将被复制为(master_replid2, second_repl_offset)。
有关Redis复制的一些非常重要的事实:
- Redis 使用异步复制,slave 和 master 之间使用异步来确认要处理的数据。
- master 可以有多个 slave,slave 可以接受其他 slave 的连接。 slave 之间可以级联。从Redis 4.0开始,所有 sub-slaves 将从 master 接收完全相同的复制流。
- 复制在 master 端是非阻塞的。这总味着当一个或多个 slave 执行全量同步或部分同步时,master 可以继续处理查询请求。
- 复制在 slave 端大部分也是非阻塞的。 可以配置 slave 使用旧数据集处理查询请求。但若是全量同步,旧数据集会被清空,同时加载新的数据集,在这期间会阻塞请求(若数据集非常大,可能长达数秒)。从Redis 4.0开始,可以配置 Redis,让删除旧数据集在不同的线程中进行,但是加载新的初始数据集仍然会在主线程执行并阻塞 slave。
- 复制可用于实现 Redis 服务的可伸缩性,实现读写分离,为只读查询提供多个 slave , 可用于提高数据安全性(数据备份)和高可用性(主从切换)。
- 复制还可用来避免 master 将全部数据持久化到磁盘的开销,在 master 关闭持久化,连接 slave,将数据同步到 slave,并不定期保存或启用 AOF。但这样配置要特别小性,当 master 重启后是从空的数据集开始,如果连接了 slave 会试图同步,slave 也会被清空。
Master 关闭持久化的复制安全性
在 Redis 复制的配置中,强烈建议在 master 和 slave 中启用持久化。关闭了持久化并配置了自动重启的 master 是极其危险的,master 和 slave 中的数据都可能被清空。
从实例键过期
从实例不会让键过期,当 master 让一个键过期会生成一个DEL
命令发送给slave。
slave 的键过期是由** master 驱动的,Redis 的过期处理机制也不是实时的,master 无法及时提供 DEL 命令,所以在 slave 的内存中仍然可能存在在逻辑上已过期的 key。为了处理这个问题,slave**在不违反数据一致性的读取操作情况下,使用自己的逻辑时钟来报告 key 的不存在。
调优
复制调优主要对可能影响复制性能的参数进行调整,例如关键参数 repl_backlog_size,可以对该参数进行调整以充分利用部分重新同步的优势来实现更好的主从复制性能。
repl_backlog_size
Slave 与 Master 断开连接期间,Master 上会有一段内存(是一个环形缓冲区)会跟踪最近所有的写入命令,这个缓冲是实际上是一个可配置固定长度的列表。在 Redis 中,这个缓存区被称为 replication backlog,参数名是repl_backlog_size
。Redis 使用这个缓冲区来决定执行部分同步还是全量同步。具体的说,当发送slaveof命令后,从实例会使用最后的 master_replid 和 master_repl_offset 向主实例发送一个部分同步的请求,主实例首先检查请求中的 master_replid与自己的一致,再检查 master_repl_offset 是否在 backlog 缓冲区中,如果在缓冲区中,就从该偏移量位置进行部分同步;如果不在缓冲区中,则拒绝部分同步,改为全量同步。
缓冲区的默认大小为 1M,并不足以应对大数据量高写入情况,可能导致全量同步的大概率发生而降低了性能。 通常我们会把该参数调整为更高的值以满足需求。通过在峰值期间使用
INFO
命令来计算master_repl_offset的变量化来估算 backlog 缓冲区的合适大小。t * (master_repl_offset2 - master_repl_offset1)/(t2 - t1)
t 是网络连接断开的时长
master_repl_offset2 流量峰值的偏移量; t2 是流量峰值的时间点
master_repl_offset1 常规流量的偏移量; t1 是常规流量的时间点若是把 repl_backlog_size 值设置的比 RDB 快照大小的值还大就没有意义了,因为部分同步和全量同步传输的数据大小几乎相同,就无法充分利用部分同步的优点。
repl-backlog-ttl
当主实例与所有从实例的连接断开,该参数用于设置主实例延迟多久释放缓冲区(backlog),默认是 3600s,当设置为 0 时表示不释放缓存。注意:从实例不要设置超时释放缓存,因为可能会升级为主实例,从而让其它从实例可以从这个新主实例进行部分同步。
repl-disable-tcp-nodelay
默认值是no
, 表示减少从实例端的复制延迟,但会使用更多的带宽。当设置成yes
时,主实例会将几个小的数据包合并成一个包发送给从实例,从而减少带宽的使用,但可能会增加数据在从实例的延迟,使用默认配置的 Linux 内核延迟最多可达 40 毫秒,如果主从实例所在设备相距较远或需要考虑网络传输可设置。repl-diskless-sync
正常情况下,一个全量同步要求在磁盘上创建 RDB 文件,然后加载该RDB文件发送给从实例。
该参数默认值是 no 。对于大数据量慢速磁盘,主实例的全量同步操作是非常有压力的。 Redis 支持无盘复制,若开启(yes), Disk-backed 将切换成 Diskless ,子进程直接将 RDB 数据发送给从实例而不用先写到磁盘,可以节省大量的磁盘I/O和内存。在生产环境谨慎使用。repl-diskless-sync-delay
当设置了 repl-diskless-sync 为 yes 开启了无磁盘复制时,该参数可以延迟启动数据传输以等待第一个从实例连接成功后更多的从实例
到达。
故障诊断
复制超时处理
Redis master 每隔一段时间会向 slave 发送 ping 命令以确定 slave 是否正常运行,默认间隔是 10秒(repl-ping-slave-period 10)。Redis slaves 每秒会对 master 发送 REPLCONF ACK {offset} 来报告它已复制的偏移量。PING 和 REPLCONF ACK 的超时时长由同一个参数(repl-timeout 60)控制,默认是 60 秒,大多数情况下,该参数的默认值是合适的。如果 PING 或 REPLCONF ACK 超时,或在 repl-timeout 期间,主从实例之间没有数据流,则主从实例的复制连接会被断开。可通过(debug sleep 60)设置休眠时长来验证超时机制的处理。
在实际生产环境中 repl-ping-slave-period 的值必须小于 repl-timeout的值,否则只要每次主实例和从实例之间没有流量时就会造成超时。因为 Redis 是单线程处理命令,所以阻塞操作可能导致复制超时,应尽量避免使用导致长时间阻塞的命令。
主实例中 slave 类型客户端输出缓存超限处理 **
在主从复制建立时,主实例首先创建 RDB 文件并发送给从实例,从实例接收完 RDB 文件并加载到内存中。在这些步骤执行期间,主实例的所有写入命令会被缓存在一个类型为slave的客户端输出缓冲区(client-output-buffer-limit slave),在从实例加载完 RDB 后会将这个缓冲区的数据发送给从实例**。这个 slave类型客户端输出缓冲区是有大小限制的(client-output-buffer-limit slave 256mb 64mb 60),256mb 是默认的硬性限制,一旦缓冲超过限制则会断开从实例连接; 默认的 64mb 60 是软限制的整体参数,当缓冲区大小超过 64mb 并且持续时长超过 60 秒, 主实例会关闭与从实例的连接。当设置为 0 时,表示不限制缓存大小,也就是主实例不会因为缓存大小断开从实例连接。
在高写入数据的环境中,客户端的缓冲区大小可在 redis.conf 文件设置,也可通过 CONFIG SET命令修改,若使用 CONFIG SET命令设置值需要注意不支持MB 或 GB单位,只支持Byte单位。
CONFIG SET命令设置缓冲区大小示例,硬件限制为512MB,软限制为 120MB 120秒:127.0.0.1:6379> config set client-output-buffer-limit ‘slave 536870912 134217728 120’
客户端缓存输出限制:
1
2
3
4
5
6client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
class 有以下三个可选
normal -> normal clients including MONITOR clients
slave -> slave clients
pubsub -> clients subscribed to at least one pubsub channel or pattern可通过在数据同步过程中调用
CLIENT LIST
命令, 获取cmd = sysc/psysc
且flages=S (slave)的客户端连接,查看 omem 的指可知 slave类型输出缓冲区(output buffer)所使用的内存字节大小。
主实例:1
2
3127.0.0.1:6379> client list
id=12 addr=127.0.0.1:45462 fd=10 name= age=7657 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
id=14 addr=127.0.0.1:45468 fd=8 name= age=2349 idle=1 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=replconf从实例:
1
2
3127.0.0.1:6380> client list
id=10 addr=127.0.0.1:53388 fd=10 name= age=7698 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
id=3 addr=127.0.0.1:6379 fd=8 name= age=89371 idle=1 flags=M db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
Redis 4.x系列(十五):Redis 主从复制、调优和故障分析
http://blog.gxitsky.com/2018/11/09/Redis-15-replication-master-slave/