redis 系列22 复制Replication (下)

一. 复制环境准备

  1.1 主库环境(172.168.18.201)

环境

说明

操作系统版本 CentOS  7.4.1708 
IP地址 172.168.18.201
网关Gateway 172.168.18.1
DNS 172.168.16.11
Redis版本和端口 4.0.6  和 6379
Redis 密码 123456
是否RDB持久化 开启
是否 AOF持久化 开启

  1.2 从库环境(172.168.18.203)

环境

说明

操作系统版本 CentOS  7.4.1708 
IP地址 172.168.18.203
网关Gateway 172.168.18.1
DNS 172.168.16.11
Redis版本和端口 4.0.6  和 6379
Redis密码 123456
是否RDB持久化 开启
是否 AOF持久化 开启
是否只读库 只读

 

二. 复制配置

  2.1 主库环境密码设置

    在201主库上,找到redis目录下的redis.conf文件,打开文件找到requirepass,在500行,下面脚本修改,密码为:123456

    -- 原脚本 
    500 # requirepass foobared
    -- 修改后
    500  requirepass 123456

    修改后,设置防为墙,重启redis服务,重新登录主服务器,客户端连接成功,脚本如下:

    [root@hsr bin]# iptables -F
    [root@hsr bin]# ./redis-cli -h 172.168.18.201 -p 6379 -a 123456
    172.168.18.201:6379> config get requirepass 
    1) "requirepass"
    2) "123456"

    在slave从库203上,使用203从库的redis客户端连接201主库redis服务器,主从网络连接没有问题,连接成功,脚本如下:

    [root@hsr bin]# ./redis-cli -h 172.168.18.201 -p 6379 -a 123456
    172.168.18.201:6379> config get requirepass 
    1) "requirepass"
    2) "123456"

  2.2 主库设置RDB持久化和AOF持久化

    redis服务器默认是使用的RDB持久化,下面是save自动保存的触发条件点,以及rdb文件名, 脚本如下:

    172.168.18.201:6379> config get save
    1) "save"
    2) "900 1 300 10 60 10000"
    172.168.18.201:6379> config get dbfilename
    1) "dbfilename"
    2) "dump.rdb"

    全量复制(RDB)虽然耗时,但是对于从库来说,如果是第一次同步,全量复制是无法避免的,所以建议:一个 Redis 实例的数据库不要太大,一个实例大小在几 GB 级别比较合适,这样可以减少 RDB 文件生成、传输和重新加载的开销。另外,为了避免多个从库同时和主库进行全量复制,给主库过大的同步压力,我们也可以采用“主 - 从 - 从”这一级联模式,来缓解主库的压力。

  修改redis.conf文件,开启AOF持久化,在配置文件中找到appendonly将no改为yes, appendonly.aof 文件路径默认产生在启动所在的目录,为./dir。缓冲区数据写入磁盘也是默认模式Everysec ,相关脚本如下:

    -- 在672行开启AOF
    672 appendonly  yes
    --  263行文件默认路径
    263 dir ./

    --- 关闭redis服务,重启后查看
    172.168.18.201:6379> config get appendonly
    1) "appendonly"
    2) "yes"

    172.168.18.201:6379> config get appendfsync
    1) "appendfsync"
    2) "everysec"
    -- 开启后,持久化文件生成如下列表:
    [root@hsr bin]# ls -l
    总用量 11544
    -rw-r--r-- 1 root root      56 12月 11 14:27 appendonly.aof
    -rw-r--r-- 1 root root     108 12月 11 14:23 dump.rdb

   如遇到了网络断连,增量复制(AOF)就派上用场了。特别留意一下 repl_backlog_size 这个配置参数。如果它配置得过小,在增量复制阶段,可能会导致从库的复制进度赶不上主库,进而导致从库重新进行全量复制。所以,通过调大这个参数,可以减少从库在网络断连时全量复制的风险。

  2.3 从库配置

    从库也一样,设置密码为123456, 设置防火墙,开启AOF持久化,从库默认是只读模式。参考主库设置,这里就不再介绍,最后查看脚本如下:

    [root@xuegod64 redis]# redis-cli -h 172.168.18.203 -p 6379 -a     123456
    172.168.18.203:6379> config get appendonly
    1) "appendonly"
    2) "yes"
    172.168.18.203:6379> config get requirepass
    1) "requirepass"
    2) "123456"
    172.168.18.203:6379> config get slave-read-only
    1) "slave-read-only"
    2) "yes"

  2.4 启动复制

    在从库203上设置slaveof参数,让一个从服务器去复制主服务器。下面修改从库配置文件,先不用重启redis服务,脚本如下:

    -- 将280行配置
    280 # slaveof <masterip> <masterport>
    -- 修改后如下,设置好对应的主库ip和端口
    280 slaveof 172.168.18.201  6379

  2.5 设置身份验证

    在从库203上设置身份验证 masterauth选项(slave 对 master 进行验证),在需要进行身份验证时,从服务器将向主服务器发送一条auth命令,命令的参数为从服务器masterauth选项的值。

    -- 将287行配置修改
    287 # masterauth <master-password>
    -- 修改后如下, 设置主库的密码为123456
    287 masterauth 123456
    -- 最后客户端关闭从库服务,重启服务
    [root@xuegod64 redis]# redis-cli -h 172.168.18.203 -p 6379 -a 123456
    172.168.18.203:6379> shutdown
    not connected> exit
    [root@xuegod64 redis]# redis-server redis.conf

  

三.复制数据同步测试

  3.1 测试主从数据同步

    -- 在主库201设置一个kev value, 脚本如下:
    172.168.18.201:6379> set repl "hello"
    OK
    -- 在从库203上读取主库该key成功,脚本如下:
    172.168.18.203:6379> get repl
    "hello"

    -- 在主库删除该key,脚本如下:
    172.168.18.201:6379> del repl
    (integer) 1
    --在从库上读取该key,脚本如下:
    172.168.18.203:6379> get repl
    (nil)    

  3.2 测试从库是否只读

    --下面在从库写入一个key,错误信息如下:
    172.168.18.203:6379> set msg1 "hello"
    (error) READONLY You can't write against a read only slave.

  3.3 在从库开启了日志记录,查看redis.log文件,相关复制的信息如下:

19424:S 11 Dec 15:11:18.610 * Connecting to MASTER 172.168.18.201:6379
19424:S 11 Dec 15:11:18.610 * MASTER <-> SLAVE sync started
19424:S 11 Dec 15:11:18.611 * Non blocking connect for SYNC fired the event.
19424:S 11 Dec 15:11:18.612 * Master replied to PING, replication can continue...
19424:S 11 Dec 15:11:18.613 * Partial resynchronization not possible (no cached master)
19424:S 11 Dec 15:11:18.615 * Full resync from master: 0c208c84b99e1970721404abf88a92d80c50d0d2:0
19424:S 11 Dec 15:11:18.708 * MASTER <-> SLAVE sync: receiving 191 bytes from master
19424:S 11 Dec 15:11:18.708 * MASTER <-> SLAVE sync: Flushing old data
19424:S 11 Dec 15:11:18.708 * MASTER <-> SLAVE sync: Loading DB in memory
19424:S 11 Dec 15:11:18.708 * MASTER <-> SLAVE sync: Finished with success
19424:S 11 Dec 15:11:18.710 * Background append only file rewriting started by pid 19428
19424:S 11 Dec 15:11:18.748 * AOF rewrite child asks to stop sending diffs.
19428:C 11 Dec 15:11:18.748 * Parent agreed to stop sending diffs. Finalizing AOF...
19428:C 11 Dec 15:11:18.749 * Concatenating 0.00 MB of AOF diff received from parent.
19428:C 11 Dec 15:11:18.749 * SYNC append only file rewrite performed
19428:C 11 Dec 15:11:18.749 * AOF rewrite: 6 MB of memory used by copy-on-write
19424:S 11 Dec 15:11:18.812 * Background AOF rewrite terminated with success
19424:S 11 Dec 15:11:18.812 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
19424:S 11 Dec 15:11:18.812 * Background AOF rewrite finished successfully

 

四. 复制运行状态参数信息

  4.1 主库缓冲队列信息

    缓冲队列的大小默认是1MB,可以在redis.conf中的配置项repl-backlog-size进行设置,还有一个配置项repl-backlog-ttl,表示当主从断开后,缓冲队列的缓存时间。

    172.168.18.201:6379> config get repl-backlog-ttl
    1) "repl-backlog-ttl"
    2) "3600"

    172.168.18.201:6379> config get repl-backlog-size
    1) "repl-backlog-size"
    2) "1048576"

    举个例子,如果主库每秒写入 2000 个操作,每个操作的大小为 2KB,网络每秒能传输 1000 个操作,那么,有 1000 个操作需要缓冲起来,这就至少需要 2MB 的缓冲空间。否则,新写的命令就会覆盖掉旧操作了。为了应对可能的突发压力,我们最终把 repl_backlog_size 设为 4MB。这样一来,增量复制时主从库的数据不一致风险就降低了。不过,如果并发请求量非常大,连两倍的缓冲空间都存不下新操作请求的话,此时,主从库数据仍然可能不一致。

 

4.2 info replication命令查看主库当前复制状态信息

172.168.18.201:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.168.18.203,port=6379,state=online,offset=3328,lag=1
master_replid:0c208c84b99e1970721404abf88a92d80c50d0d2
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3328
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:3328

  4.3  info replication命令查看从库当前复制状态信息

172.168.18.203:6379> info replication
# Replication
role:slave
master_host:172.168.18.201
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_repl_offset:3398
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:0c208c84b99e1970721404abf88a92d80c50d0d2
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:3398
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:3398

  注意:上一篇讲到主从偏移量相同时,表示主从服务器处于一致状态,反之则不一致。但这里显示的主从偏移量不一致,以后再了解。

  4.4  role命令查看主库和从库信息

    ROLE命令它提供 master 和 slave 的复制状态以及它们的复制偏移量,连接的 slaves 列表等等。role输出一个数组,第一个参数是 master, slave, sentinel 三个中的一个。

172.168.18.201:6379> role
1) "master"
2) (integer) 3594
3) 1) 1) "172.168.18.203"
      2) "6379"
      3) "3594"

172.168.18.203:6379> role
1) "slave"
2) "172.168.18.201"
3) (integer) 6379
4) "connected"
5) (integer) 3482

   

五. 复制的内部实现步骤

  5.1 客户端设置主服务器的地址和端口

    从服务器首先将主服务器IP地址和端口保存到服务器状态的redisserver结构下masterhost和masterport属性中。

  5.2 建立套接字连接

    在slaveof命令执行之后,从服务器将根据命令所设置的ip地址和端口,创建连向主服务器的套接字连接:

    (1)如果从服务器成功连接到主服务器,那么从服务器将为这个套接字关联一个专门用于处理复制工作的文件事件处理器。执行的工作包括如接收RDB文件,主服务器传播的写命令等。

    (2)如果主服务器成功接受从服务器,将为该套接字创建相应的客户端状态,并将从服务器看作是一个连接到主服务器的客户端来对待。这时从服务器将同时具有服务器和客户端两个身份。

  5.3 发送ping命令

    从服务器成为主服务器客户端之后,做的第一件事就是向主服务器发送一个ping命令。该命令有两个作用:

    (1) 虽然主从服务器成功建立起了套接字连接,但双方并未使用该套接字进行过任何通信,通过发送ping命令可以检查套接字的读写状态是否正常。

    (2) 通过发送ping命令可以检查主服务器能否正常处理命令请求。

    从服务器发送ping命令之后将遇到三种情况的其中一种:

    (1) 如果主服务器向从服务器返回了一个命令回复,但从服务器却不能在规定的时限(timeout)内读出命令回复内容,那么表示主从服务器之间的网络连接状态不佳,这种情况,从服务器会断开重新创建连向主服务器的套接字。

    (2) 如果主服务器向从服务器返回了一个错误,那么从服务器会断开并重新创建连向主服务器的套接字。

    (3) 如果主服务向从服务器成功回复,那么网络正常并且主服务器可以正常处理从服务器发送的命令请求。

  5.4 身份验证

    从服务器收到主服务器返回的ping 回复后,接着决定是否进行身份验证:

    (1) 如果从服务器设置了masterauth选项,那么进行身份验证。

    (2) 如果从服务器没有设置masterauth选项,那么不进行身份验证。

    从服务器在身份验证阶级可以遇到的情况有以下几种:

    (1)如果主服务器没有设置requirepass选项, 从服务器也没有设置masterauth选项,那么复制工作可以继续进行。

    (2)如果从服务器的masterauth选项设置的密码与主服务器密码不相同,那么主服务器返回一个invalid password错误。

    (3)主服务器没有设置requirepass选项,但从服务器设置了masterauth选项,那么主服务器将返回一个no password  is set 错误。

  5.5 发送端口信息

    从服务器在身份验证后,从服务器将执行replconf listening-port port_number,向主服务器发送从服务器的监听端口号。

    主服务器在接受到这个命令之后,会将端口号记录在从服务器所对应的客户端状态的slave_listening_port属性中。通过info replication命令查看从服务器端口号。

  5.6 数据同步

    从服务器将向主服务器发送psync命令,执行同步操作,并将自己的数据库更新到主服务器数据库当前所处的状态。

  5.7 命令传播

    当完成了同步之后,主从服务器就会进入命令传播阶段,这时主服务器只要一直将自己执行的命令发送给从服务器,而从服务器只要一直接收并执行主服务器发来的写命令,就可以保证 主从一致了。

      

六. 复制的心跳检测

  在命令传播阶级,从服务器默认会以每秒一次的频率,向主服务器发送命令:replconf ack  offset。 发送replconf ack命令对于主从服务器有三个作用:

(1) 检测主从服务器的网络连接状态。

(2) 辅助实现min-slaves选项。

(3) 检测命令丢失。

  6.1 检测主从服务器的网络连接状态

    主从服务器可以通过发送和接收replconf ack 命令来检查两者之间的网络连接是否正常:如果主服务器超过一秒钟没有收到从服务器发来的replconf ack命令,那么主服务器就知道主从服务器之间的连接出现问题了。

    通过向主服务器发送info replication命令,在列出的从服务器列表的lag一栏中,我们可以看到相应从服务器最后一次向主服务器发送replconf ack 命令距离现在过了多少秒。 

    172.168.18.201:6379> info replication
    # Replication
    role:master
    connected_slaves:1
   slave0:ip=172.168.18.203,port=6379,state=online,offset=92368,lag=0

    上面lag=0 表示刚刚发送过 replconf ack命令。       在一般情况下,lag的值应该在0秒或者1秒之间跳动,如果超过1秒的话,那么说明主从服务器之间的连接出现了故障。

  6.2 辅助实现min-slaves配置选项

    redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。意思是:至少有 N 个 slave ,并且滞后小于 M 秒,则写入将被接受,反之写入不被接受,master 将会回复一个 error。  

    --例如:在主库配置如下:
    min-slaves-to-write 3
    min-slaves-max-lag 10

    如果从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10秒时,主服务器将拒绝执行写命令,这里的lag延迟值就是上面提到的INFO replication命令的lag值。

  6.3 检测命令丢失

    如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么当从服务器向主服务器发送replconf ack命令时,主服务器将发觉从服务器当前的复制偏移量小于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。

    注意:主服务向从服务器补发缺失数据这一操作的原理和部分重同步操作的原理非常相似,区别在于,补发缺失数据操作在主从服务器没有断线的情况下执行,而部分重同步操作则在主从服务器断线并重连之后执行。

  

7  Redis复制如何处理 key 的过期

  (1) slave 不会让 key 过期,而是等待 master 让 key 过期。当一个 master 让一个 key 到期(或由于 LRU 算法将之驱逐)时,它会合成一个 DEL 命令并传输到所有的 slave。

  (2) 由于这是 master 驱动的 key 过期行为,master 无法及时提供 DEL 命令,所以有时候 slave 的内存中仍然可能存在在逻辑上已经过期的 key 。为了处理这个问题,slave 使用它的逻辑时钟以报告只有在不违反数据集的一致性的读取操作(从主机的新命令到达)中才存在 key。

  (3) 当一个Lua脚本运行时,master 中的时间是被冻结的,这样脚本运行的时候,一个给定的键要么存在要么不存在。这可以防止 key 在脚本中间过期,保证将相同的脚本发送到 slave ,从而在二者的数据集中产生相同的效果。

  一旦一个 slave 被提升为一个 master ,它将开始独立地过期 key,而不需要任何旧 master 的帮助。

 

8.启动复制的其它方法

  (1)在从库启动Redis服务的时候,可以指定主服务器信息,命令:redis-server --slaveof masterip masterport。 例如:redis-server --slaveof 172.168.18.201 6379 。

       (2)在从库客户端指定主服务器信息,命令:redis> SLAVEOF masterip masterport。这样SLAVEOF命令会停止与原有主服务器的同步,转而向新主服务器进行同步。

 

最后一些问题汇总:

1. 一个从库如果和主库断连时间过长,造成它在主库repl_backlog_buffer的slave_repl_offset位置上的数据已经被覆盖掉了,此时从库和主库间将进行全量复制。
2. 每个从库会记录自己的slave_repl_offset,每个从库的复制进度也不一定相同。在和主库重连进行恢复时,从库会通过psync命令把自己记录的slave_repl_offset发给主库,主库会根据从库各自的复制进度,来决定这个从库可以进行增量复制,还是全量复制。

 

 

posted on 2018-12-12 17:30  花阴偷移  阅读(582)  评论(1编辑  收藏  举报

导航