上一节聊了Redis的持久化方式,这一节我们来看看Redis的复制和阻塞概念、
复制
- 复制的使用方式: 如何建立或断开复制、安全性、只读等。
- 说明复制可支持的拓扑结构,以及每个拓扑结构的使用场景。
- 分析复制的原理,包括:建立复制、全量复制、部分复制、心跳等
- 介绍复制过程中常见的开发和运维问题:读写分离、数据不一致、规避全量复制等。
配置
建立复制:
参与复制的节点分为master节点、slave节点。默认情况下redis都是主节点。每个从节点只有一个主节点,而主节点可以同时有多个从节点。复制的数据流动是单向的,只能master -> slave.
复制方式:
- 配置文件中 slaveof masterHost masterPort 注意此命令是异步命令
- redis-server 启动命令中加 --slaveof masterHost masterPort 生效
- client 直接使用命令: slaveof masterHost masterPort 生效
- info replication 可以查看复制关系和信息
断开复制: 执行命令slaveof no one
- 断开与主节点的复制关系
- 从节点晋升为主节点
其实还可以调整从节点slave 其他主节点。比如slaveof newMasterHost newMasterPort
切主复制流程:
- 断开与旧主节点复制关系
- 与新主节点建立复制关系
- 删除从节点当前所有数据
- 对新主节点进行复制操作
安全性
只读
默认从节点使用 slave-read-only=yes 配置为只读模式。因为复制只能从master -> slave, update slave can different data for master and salve, so prod not update this param
传输延迟:
复制时会有网络延迟, repl-disable-tcp-nodelay 控制是否关闭 TCP_NODELAY, 默认关闭。
- 关闭: master -> slave all command
- 开启: master merge small tcp data package 从而节省带宽。默认发送时间间隔取决于linux内核 (40ms)。节省带宽但是增大主从之间的延迟。 比如跨机房部署。
- 要求低延迟,一般同机房部署并关闭 repl-disable-tcp-nodelay, 如果考虑容灾性,考虑开启此参数。
拓扑
- 一主一从: 主节点宕机时从节点提供故障转移支持
- 一主多从: 读比较大,从节点可分担读压力。缺点: 并发比较高时,从节点会导致主节点写命令的多次发送而过度消耗带宽,同时也增加了主节点的负载影响服务稳定性。
- 树型结构: 1. 复制数据 2.通过引入复制中间层,减轻主节点压力。
原理
1、复制过程
注意:从节点每秒 的同步r任务很重要,因为它会主动发送同步命令。一个是第一次全量,一个是网络闪退时主动发起给master 补发丢失数据。
从节点指定slaveof 命令后,复制过程就开始了,下面是整个建立复制的完整流程。
主要分为以下6个步骤:
- 保存master信息
- 建立socket链接: 从节点内部通过每秒运行的定时任务维护复制相关逻辑,并且和master 建立socket 链接,专门用于接收master 发送过来的复制命令。
- 发送ping命令: 建立复制成功后,从节点发送ping请求进行首次通信。目的:套接字是否可用、主节点可用状态。
- 权限验证:master可能设置requirepass参数,则需要密码验证。slave必须配置masterauth参数才能保证与master节点相同密码才能ok。
- 同步数据集:
主从复制链接正常通信后,对于首次复制场景,主节点会把所持有的数据全部发送给从节点,耗时很长。
分为全量同步和部分同步。 - 命令持续同步:
master 把所有的数据同步给slave后,便完成了复制的建立流程。接下来主节点会持续的把写命令发送给从节点,保证主从数据一致性。
2、数据同步
2.8 version 以上的使用psync命令完成主从数据同步,同步过程分为: 全量复制和 部分复制。
- 全量复制: 一般使用于初次复制场景,Redis早期支持的复制功能只有全量复制,它会把master数据一次性发给slave。当然数据量比较大时,会对主节点和网络造成很大的开销。
- 部分复制:用于处理master 主从复制中因网络闪断等原因造成的数据丢失场景,当slave再次连接上master后,如果条件允许,master会补发丢失数据给slave。因为丢失数据少。
psync命令运行需要以下几个组件支持
- 主从节点各自复制偏移量
- master写命令执行后,会把命令的字节长度做累加记录,master_repl_offset
- slave 每秒上报自己的 offset 给 master,因此master也会保存slave复制偏移量
- slave 收到master 发送的命令后,也会累加记录自己的偏移量。slave_repl_offset
- 可以master_repl_offset - slave_repl_offset 来分析网络是否阻塞等等
- 主从节点复制积压缓冲区
-
此缓冲区是master上一个固定长度queue,默认1MB。主节点写命令执行时,发一份给从节点,并且写入复制缓冲区
-
复制缓冲区的主要作用在于 用于部分复制和 复制命令丢失的数据补救。
-
- 主节点运行id
- master 每次启动后,都会动态分配一个40位的十六进制字符串作为 runId。
- 因为每次 ip + port都不能保证唯一。因为这个时候有可能master 持久化文件 RDB 和 aof被替换了,这个时候仅仅用偏移量去复制就不行了,需要用runId,因为每次runId变更之后都会做全量复制。
- 命令: info server 查看runId
psync 命令: 从节点使用这个命令 psync runId offset(当前从节点已复制的offset)
- slave send psync to master, runId -> 从节点保存的master 的运行id。无runId, 也是-1, 参数offset第一次为-1
- master 根据psync 参数和自身数据情况决定响应结果。
- +FULLRESYNC runId offset 从节点将触发全量复制流程。
- +CONTINUE: 从节点将触发部分复制流程
- +ERR, 说明redis版本低于2.8 无法识别 psync,从节点将发送旧版sync 命令触发全量复制流程。
3、全量同步
主从第一次复制必须经历, 2.8 sync >=2.8 psync
下面主要是psync流程。
1、 发送psync -1: 因为 slave这个时候不知道materRunId 和 offset
注意: 主节点bgsave -> 子进程RDB
slave开始接受到接受完成这一阶段,master这段时间内的命令写入复制客户端缓冲区.
从节点加载完RDB文件后,master发送这一部分数据给slave. 注意缓冲区配置,很有可能会溢出。
client-output-buffer-limit slave 256MB64MB60
if 60s 内缓冲区size > 64MB || 256MB, master close 复制客户端链接,造成全量同步失败。
全量复制存在很高的时间开销:
- master bgsave 时间
- RDB 文件网络传输时间
- slave clear old data time
- slave load rdb time
- 可能的AOF 重写时间
比如数据量 在 6G左右的主节点,从节点发起全量复制的总消耗在2分钟左右。
应当避免或者少的全量复制发生,由于成本,发明了部分复制。
4、部分同步
当master 发生网络问题和闪退问题时,从节点主动发出 psync runId offset(从节点自己的offset) , master 接到后,check runId , check offset之后的数据是否在复制积压缓冲区,在进行发送continue,进行发送这一部分数据。
5、心跳
主从节点建立复制后,会维护长连接,并彼此发送心跳命令。
机制:
- 主从之间ping通信,client list可以查看相关信息
- master 每隔10秒对slave 发送ping。存活或者连接状态。repl-ping-slave-period 控制发送频率
- slave 在主线程每隔1秒发送replconf ack offset, 上报从节点自身偏移量
- replconf 实时检测主从节点网络状态
- 上报自身偏移量,检查数据是否丢失,如果从节点数据丢失,再从master的复制积压缓冲区拉取丢失数据。
- 实现保证从节点的数量和延迟性功能。通过min-slave-to-write、min-slaves-max-lag参数配置
- 主节点会根据replconf 判断从节点超时时间,正常延迟时间 0 -1 , 如果超发 repl-timeout(60秒),判定从节点下线并断开复制客户端链接。
6、异步复制
主节点执行完一条写命令,这个时候立即返回客户端,异步发送写命令同步给从节点
开发和运维中的问题
读写分离
主从配置不一样
规避全量复制
规避复制风暴: 多台从节点对同一主节点或者同一台机器的多个主节点短时间内发生全量复制问题。
单主节点复制风暴: 树形结构
单机器复制风暴: 分散主节点
阻塞
内在原因
- 不合理的使用API或数据结构
- 慢查询 showlog get n 查询最近n条,默认是10ms的命令就会记录到一个定长队列中
- 大对象 redis-cli -h ip -p port bigkeys 内部分段scan操作
- CPU饱和
- 单核利用率100%, 比如一些不合理的设置hash的 hash-max-ziplist-entries 和 hash-max-ziplist-value, 导致数据操作时间很长
- 持久化阻塞
- fork阻塞 RDB fork bgsave,AOF文件重写 bgrewriteaof
- AOF 刷盘阻塞
- 一般fsync 都是每s执行一次,当磁盘压力过大时,主线程会主动阻塞,直到fsync完成。
- HugePage 写操作阻塞
- 子进程执行重写期间利用linux写时复制技术降低内存开销,因此只有写操作时Redis才复制要修改的内存页。对于开启Transparent HugePages的os,每次写命令引起的复制内存页单位由4K变为2MB,这样会拖慢写的执行时间,导致慢查询
如果自身没有找到原因,这个时候找外在原因
外在原因
- CPU竞争
- 进程竞争: Redis是典型的 CPU密集型应用,不建议与其他多核CPU密集型服务部署在一块。
- 绑定CPU: 一般为了利用多核CPU,一个机器上部署多个实例,每个实例绑定一个cpu,这样可以降低cpu频繁切换的开销。但是有持久化或者复制的实例不建议,因为有fork子进程,共享cpu问题。
- 内存交换
- 其实就是内存和磁盘数据交换,因为如果os开启部分内存数据存储到磁盘上,这个时候就会有内存交换问题,操作数据缓慢。
- 网络问题
- 连接拒绝
- 网络闪断
- redis主动拒绝 maxclients 可以控制client最大连接数。10000
- 连接溢出
- 网络延迟
- 比如机房,机器
- 网卡软中断
- 连接拒绝
下一节
Redis技术指南-5-理解内存