0.主从复制原理
当建立主从关系时,slave配置slaveof host port. slave服务器会向主服务器发送一个sync命令,master 接受并fork一个进行来执行bgsave命令。该命令生成一个RDB文件并且全量发送个slave服务器,slave服务器接受并载入RDB文件,同时,主服务器将缓冲区的命令以增量的方式发送给从服务器,最终是从服务器的数据状态和主服务器保持一致。
注意:当slave第一次slaveof master时,master会生成一个RDB文件,然后把这个RDB文件传输给slave,但是当master如果有新的数据写入时,这时新的数据会直接发送给slave服务器。。。
RDB的工作原理
当redis生成dump.rdb文件时,工作过程如下:
- redis主进程fork一个子进程
- fork出来的子进程将内存的数据集dump到临时的RDB中
- 当子进程对临时的RDB文件写入完毕,redis用新的RDB文件代替旧的RDB文件。
AOF的工作原理
AOF:append only file。每当redis执行一个改变数据集的命令时,这个命令都会被追加到AOF文件的末尾。当redis重新启动时,程序可以通过AOF文件恢复数据。
1.主从复制过程
在从节点执行slaveof命令后,复制过程边开始运作,redis的主从复制大致上分为6个过程:
1)保存主节点(master)的信息
执行slaveof后从节点只保存主节点的地址信息便直接返回了,这时建立复制流程还没有开始
2)从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接,从节点建立一个socket 套接字,如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行slaveof no one取消复制
3)发送ping命令。
连接建立成功后从节点发送ping请求进行首次通信,ping请求主要目的如下:
-
- 检测主从之间网络套接字是否可用
- 检测主节点当前是否可接受处理命令
如果发送ping命令后,从节点没有收到主节点的pong回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重试
4)权限验证。如果主节点设置了requirepass参数,则需要密码验证,从节点必须配置masterauth参数保证与主节点相同密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。
5)同步数据集。主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤
6)命令持续复制,当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送个从节点,保证主从数据一致性。
总结:
1)保存主节点信息-->2)主从建立socket连接--->3)发送ping命令--->4)权限验证--->5)同步数据集--->6)命令持续复制
2.数据同步
Redis在2.8以及以上的版本使用的是psync命令完成主从数据同步,同步过程分为:全量复制和部分复制
- 全量复制:一般用于初次复制场景,Redis早期支持的复制功能只有全量复制,它会把主节点全部数据一次性发送给从节点,当数据量较大时,会对从节点和网络造成很大的开销。
- 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销。
psync命令运行需要以下组件支持
- 主从节点各自复制偏移量
- 主节点复制积压缓冲区
- 主节点运行id
2.1 复制偏移量
- 参与复制的主从节点都会维护自身复制偏移量。主节点在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在info replication的master_repl_offset指标中。
- 从节点(slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量
127.0.0.1:6379> info replication # Replication role:master connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=24766,lag=0 ##这里是从节点的偏移量 master_failover_state:no-failover master_replid:7b0e995ff19d40cdc37247fe4e1700509f87801e master_replid2:0000000000000000000000000000000000000000 master_repl_offset:24766 ##这里是主节点的偏移量
- 从节点在接受到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在info replication中的slave_repl_offset指标中
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:6 master_sync_in_progress:0 slave_read_repl_offset:25200 slave_repl_offset:25200 ##从节点的偏移量 slave_priority:100 slave_read_only:1 replica_announced:1 connected_slaves:0 master_failover_state:no-failover master_replid:7b0e995ff19d40cdc37247fe4e1700509f87801e master_replid2:0000000000000000000000000000000000000000 master_repl_offset:25200 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:25200
2.2.复制积压缓冲区
复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区中。
由于缓冲区本质上是先进先出的定长队列,所以能实现保存最近已复制数据的功能,用于部分复制和复制命令丢失的数据补救,复制缓冲区相关统计信息保存在主节点的info replication中:
role:master ##这里是主节点 repl_backlog_active:1 ##开启复制缓冲区 repl_backlog_size:1048576 ##缓冲区最大长度 repl_backlog_first_byte_offset:1 ##起始偏移量,计算当前缓冲区可用范围 repl_backlog_histlen:25242 ##已保存数据的有效长度
根据统计指标,可算出复制积压缓冲区内的可用偏移量范围:[repl_backlog_first_byte_offset,repl_backlog_first_byte_offset+repl_backlog_histlen]
2.3 主节点运行id
每个Redis节点启动后都会动态分配一个40位的十六进制字符串作为运行id,运行id的主要作用是用来唯一识别Redis节点,比如从节点保存主节点的运行id识别自己正在复制的是那个主节点。如果只使用ip+port的方式识别主节点,那么主节点重启变更了整体数据集(如替换RDB/AOF)文件,从节点再基于偏移量复制数据将是不安全的,因此当当运行id变化后从节点将做全量复制。可以运行info server 命令查看当前节点运行的id
127.0.0.1:6379> info server # Server redis_version:6.2.6 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:dbb063e6f0357f98 redis_mode:standalone os:Linux 3.10.0-862.el7.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:atomic-builtin gcc_version:4.8.5 process_id:1998 process_supervised:no run_id:3385736efb4c28615a6a4a38a301210fbbdf0e1c ##运行的id tcp_port:6379 server_time_usec:1647114012450275 uptime_in_seconds:21871 uptime_in_days:0 hz:10 configured_hz:10 lru_clock:2946844 executable:/data/6379/redis-server config_file:/data/6379/redis.conf io_threads_active:0
这里需要注意的是redis关闭在启动后,运行id会随之改变,可以通过命令: redis-cli -p 端口号 info server |grep run_id 进行查看
如何在不改变运行id的情况下重启呢?
当需要调优一些内存相关配置,例如hash-max-ziplist-value等,这些配置需要redis重新加载才能优化已存在的数据,这时可以用debug reload命令重新加载RDB并保持运行的id发生不变,从而避免不必要的全量复制。
[root@redis 6380]# redis-cli -p 6380 debug reload ##注意这里的debug reload使用 OK
debug reload 命令会阻塞当前redis节点主线程,阻塞期间会生成本地RDB快照并清空数据之后再加载RDB文件,因此对于大数据量的主节点和无法容忍阻塞的应用场景,谨慎使用。1
3.心跳
主从节点在建立复制后,它们之间维护着长连接并彼此发送心跳命令:
1)主从节点彼此都有心跳检测机制,各自模拟成对方的客户端进行通信,通过client list 命令查看复制相关客户端信息,主节点的连接状态为flags=M,从节点连接状态为Flags-S
2) 主节点默认每个10秒对从节点发送ping命令,判断从节点的存活性和连接状态。可通过参数repl-ping-slave-perios控制发送频率。
3)从节点在主线程中每隔1s发送replconf ack {offset}命令,给主节点上报自身当前的复制偏移量。replconf命令主要作用如下:
- 实时检测主从节点网络状态
- 上报自身复制偏移量,检查复制数据是否丢失,如果从节点数据丢失,再从主节点的复制缓冲区中拉取丢失数据
- 实现保证从节点的数量和延迟性功能,通过min-slave-to-write,min-slaves-max-lag参数配置定义。
主节点根据replconf命令判断从节点超时时间,体现在info replication统计中的lag信息中,lag表示与从节点最后一次通信延迟的秒数,正常延迟应该在0和1之间,如果超过repl-timeout配置的值(默认60秒),则判定从节点下线并断开复制客户端连接,即使主节点判定从节点下线后,如果从节点重新恢复,心跳检测会继续进行。
4.异步复制
主节点不但复制数据读写,还负责把写命令同步给从节点,写命令的发送过程是异步完成,也就是说主节点自身处理完写命令后直接返回给客户端,并不等待从节点复制完成
由于主从复制过程是异步的,就会造成从节点的数据相对主节点存在延迟。具体延迟多少字节,这里可以在主节点执行info replicatioin命令查看相关指标获得。如下:
slave0:ip=127.0.0.1,port=6380,state=online,offset=938,lag=1 master_repl_offset:938
在统计信息中可以看到从节点slave0信息,分别记录了从节点的ip和port,从节点的状态,offset表示当前从节点的复制偏移量,master_repl_offset表示当前主节点的复制偏移量,两者的差值就是当前从节点复制延迟量,Redis的复制速度取决于主从之间网络环境,repl_disable_tcp_nodelay,命令处理速度等,正常情况下,延迟在1秒以内。
RDB文件状态监控
其中跟RDB文件状态监控相关的参数:
- rdb_changes_since_last_save:表明上次RDB保存以后改变的key次数
- rdb_bgsave_in_progress:表示当前是否进行bgsave操作。是为1
- rdb_last_save_time :上次保存RDB文件的时间戳
- rdb_last_bgsave_time_sec:上次保存的耗时
- rdb_last_bgsave_status:上次保存的状态
- rdb_current_bgsave_time_sec:目前保存RDB文件已花费的时间
AOF文件状态监控
其中跟AOF文件状态监控相关的参数
- aof_enable aof文件是否启用
- aof_rewrite_in_progress 表示当前是否在进行写入aof文件操作
- aof_rewrite_scheduled
- aof_last_rewrite_time_sec 上次写入的时间戳
- aof_current_rewrite_time_sec : -1
- aof_last_bgrewrite_status:ok 上次重写状态
- aof_last_write_status:ok 上次写入状态
参数:rdb_last_bgsave_time_sec:0 注意一下这个参数,这个值表示上次bgsave命令执行的时间,在磁盘io定量的情况下,redis占用的内存越大,这个值也就越大。通常该参数的时间取决于两个因素:
1.Redis占用的内存大小
2.磁盘的写入速度
参数:rdb_last_bgsave_time_sec:85 这个标识表示我们上次保存dump RDB文件的时间。这个耗时受限于上面提到的两个因素。
当redis处于rdb_bgsave_in_progress状态时,通过vmstat命令查看性能,得到wa值偏高,也就是说cpu在等待io的请求完成,因此可以看到bgsave对于IO的性能影响比较大。
那么该如何解决这个问题呢?
通过的设计思路就是利用replication机制来解决:即master不开启RDB和AOF日志,来保证master的读写性能。而slave则开启rdb和aof来进行持久化,保证数据的持久性。
灾难恢复
灾难恢复是指当我们的master挂掉了之后,这时我们需要先做的步骤如下:
1.首先停掉从库的复制关系,slaveof no one
2.重试重启主库,看看数据有没有丢失,如果有丢失的话,需要把从库的数据文件拷贝到主库中,然后再启动主库
3.当主库启动完毕之后,看看主从数据是否一致,如果是一致的话,那么我们就可以在去从库中开启对主库的复制:slaveof host:port