Redis教程

Redis Replication主备复制

本文主要是介绍Redis Replication主备复制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Redis 主备复制

1 相关配置:

  1)  replicaof <masterip> <masterport>

  2) masterauth <master-password> 设置时候 replica 在发送同步相关的命令时会被要求 authenticate

  3) masteruser <username> 当这个字段指定是 replica 发送同步请求前会被要求 AUTH: <username><password>

  4) replica-serve-stale-data yes 当和master失连或者在运行时 replica 是否提供过期数据,设置为no 则除了少数命令回复以外其他的命令都回复错误

  5)  replica-read-only replica 是否只读,还是可以允许写一些临时数据,因为执行一次 resync 后数据消失

  

  6) repl-diskless-sync no/yes 当psync 不能进行时,需要全同步,此时有两种情况

    1 master 创建个子进程把rdb文件写到磁盘上,写完之后由master进程同步到各个replicas

    2 master 创建一个子进程直接把rdb文件写到replica的socket里,不写到磁盘

    这两种的区别是一旦rdb文件写到了磁盘,那么父进程就可以queue更多的replicas,然后等子进程写完磁盘以后,复制给replicas

    如果是diskless 则子进程只能针对当前queue的replicas进行同步操作,同步完以后,在这过程中来到的replicas只能等下一次子进程把rdb文件写到这些replicas中

    也就是diskless需要多次write rdb file, 一般当磁盘速度慢,网络速度快时,用这个配置,一般还是 写到磁盘

  7) repl-diskless-sync-delay 5 当diskless时, 配置一个等待时间,等待本次更多的replicas到来再一起传输,增加效率

  8) repl-diskless-load disabled 当replica 收到master的rdb 文件时有两选择,一是存储到磁盘,然后等全部接收以后在重新加载到内存, 二是直接先接受所有rdb文件到内存中,然后直接解析这个rdb文件

      disabled: 不使用

      on-empty-db: 当绝对安全时再使用

      swapdb:内存中先拷贝一份当前db内容,当来自master的rdb文件解析成功以后在删除掉拷贝,不成功再恢复,这个选项需要巨大的内存 导致oom

  9) repl-ping-replica-period 10 replica 向服务器发送ping的时间间隔,默认 10秒

  10) repl-timeout 60 默认60秒

      1)) master 认为replica 超时时间

      2)) replicas 认为 master超时时间

      3)) replica 在 bulk transfer i/o 时超时时间

      ** 确保改时间间隔 大于 repl-ping-replica-period 时间间隔。

  11) repl-disable-tcp-nodelay no 是否设置主从之间tcp链接为nodelay.

  12) repl-backlog-size 1mb 存储最新需要发送给replica的数据缓存,以便当replica一段时间断开链接,重新链接以后可以进行 partial resynchronization

      该缓存只有当至少一个 replica 存在时才会存在,并且缓存越大,允许replica失联再重连的间隔越长,根据配置选择

  13) repl-backlog-ttl 3600 一个master失去与最后一个replica的连接时算起 经过 3600 秒的时间后 释放 backlog 缓存。注意:对于replica来说不会因超时而释放backlog缓存,

      因为他们可能被升为master 然后和其他的replicas 进行partical resynchronization, 因此 replicas 总是 累计 backlog

   14) replica-priority 100 该值当 redis 处于 Sentinel 模式时,当master不能正确工作时候,用于在replicas 中选出一个promote 成为master. 该值越小越容易被推举为新的master。

              当该值为 0 时,表示该replica 不能被推举为新的master 

  15) min-replicas-to-write 3

     min-replicas-max-lag 10 这两个值得含义 参考 Replication--redis 文档

  16) replica-announce-ip 5.5.5.5

     replica-announce-port 1234 这两个还不能理解

 

2 复制相关参数

  1) server.masterhost  如果该server instance是master 该值为NULL,若为replica 该值为其master的IP地址

  2) server.masterport  如果该server instance是master 该值为NULL,若为replica 该值为其master的端口号 

  3) server.master (client* )  如果该实例是replica, 则处理与master连接的client实例  

  4) server.cached_master (client *)  被重用的master(client* ), 用于接下来与新的master 同步时 可能的 psync 过程, 新的replica 可能之前是个master,新的master可能之前是旧的master的replica,两者关系互换

      这种情况下新的replica可能与新的master进行PSYNC,而不是全同步,此时的replica 先用自己当前的一些复制信息创建 master(client* ), 随后将该master(client*)指派给cache_master,随后将master(client*)

      指向空,完成cache_master的创建,master(client*) 用做该replica同新的master交互client

  5) server.repl_backlog: 差异缓存(circular buffer),用来记录与replicas的差异,满足一定条件时,例如replica短时间与master断开链接,当重新连接后,可以执行psync 而不用fullsync,也就是master可以直接同步该缓存中部分数据给replica,避免全同步

  6) server.repl_backlog_size: 差异缓存的大小,该值越大缓存的数据越大,从而允许replicas断开与master链接的时间越长,视情况设置

  7) server.repl_backlog_idx: 数据可以存放在该缓存的开始下标

  8) server.repl_backlog_histlen: 当前缓存中缓存的数据总量,该值 <= server.repl_backlog_size

  9) server.master_repl_offset:  global replicatiion offset, 往 master.repl_backlog 写数据时,都会对写入的数据进行累加,统计总共多少数据写到过缓存中。

  10) server.repl_backlog_off: 该值等于 server.master_repl_offset - server.repl_backlog_histlen + 1, 即可明白该值的含义,相对于

  |-------------------------------------------------|------------------------------------------------------|

  1                                                         2                                                               3

  .1-3 为server.repl_backlog, 1-3的长度为 server.repl_backlog_size

  . 2所在位置为当前server.repl_backlog_idx

  .如果2-3之间没有数据则server.repl_backlog_histlen = 1-2 长度,如果2-3之间有数据即写满缓存之后又重头覆盖写此时server.repl_backlog_histlen = server.repl_backlog_size

  .如果假设当前为止server.master_repl_offset为x, 则此时server.repl_backlog_off 为 (x - server.repl_backlog_histlen + 1)

  我觉得可以类比为一个滑动窗口:

  开始写数据时如果未写满缓存  |.........................................................................|                                                   |

                 0,1                                                                   2                                                  3

  0-2为server.master_repl_offset, 1-2为server.repl_backlog_histlen, server.repl_backlog_off = 0-2长度减去1-2长度 + 1 = 1

  写满以后: ..................................................................|.......................................|..................................................|  

        0                                                              1                                     2                                                 3

  0-3长度为server.master_repl_offset, 1-3长度为server.repl_backlog_histlen, server.repl_backlog_off = 0-3长度减去1-3长度 + 1 这个off是相对于server.master_repl_offset长度而言的

  即,缓存中永远只缓存最新的长度最大为server.repl_backlog_size 的数据

  11) server.replid: master和replica 同步数据时的标识, changeReplicationId()生成的随机字符串

  12) server.replid2: 当一个 replica 通过命令或选举为 master 时,之前的replid会转移给replid2.一种场景:replica2 是 replica1 的 slave, 同时 replica3 是 replica2 的slave;此时

    replica3 的 master_replid 是 replica2 的 replid, 某一时刻, replica2 从slave 提升为master,这时 replica2 的 replid 会 转移到 replid2 同时伴随 repl_off,replica2 用腾出来的

    replid 来开启新的 replic history; 这时当replica3短暂断开与 replica2的连接之后,重新与 replica2 连接(可能是执行 replicaof 命令),此时便会用到 replica2 的 replid 和 replid2,repl_off2 

    与replica3传来的master_replid 进行比较 来确定是否能够进行 psync.

  13) server.repl_state: replica 和 master 进行数据同步过程中的状态,主要是connectWithMaster之后与master交互过程中状态

  14) server.repl_transfer_s(connection*): replica与master同步时创建的TLS或Socket连接 

  15) sever.repl_syncio_timeout: replica与master同步时网络I/O读写操作的超时时间(秒)

  16) server.repl_transfer_size: 同步过程中,master将同步过来的RDB文件的大小

  17) server.repl_transfer_read: 目前已经读取的master同步的RDB文件内容大小

  18) server.repl_transfer_lastio: 同步数据时,上一次读取socket数据时间戳,用来检测同步过程中的超时。

  19) server.repl_diskless_load: 当同步数据时,replica 收到 master 的数据时有两个选择,一是暂时存在一个文件中,当收到全部数据之后,加载文件,从文件中重新Load;二是

先备份当前自己的数据,然后接收到master的数据直接放在内存中,然后直接解析这份数据,成功之后,把之前的备份删除,如果失败了,直接回复之前的备份数据.此值是配置用哪种方式接收master同步过来的数据的

   20)server.repl_transfer_tmpfile: 当repl_diskless_load 为 false 时,需要临时文件存放master同步过来的数据,此值即为临时文件名

  21) server.repl_transfer_fd: 此值为满足上一条(20))文件的文件描述符

  22) server.repl_transfer_last_fsync_off: 当master同步数据时,replica的server.repl_diskless_load 为 false 时,replica 把数据暂存到临时文件,但是往临时文件里写数据并不能保证数据会及时被同步到硬盘上,和操作系统相关,并且当一个很大的文件从内存向硬盘存放时,也会非常耗时,因此reids采用时不时的调用rdb_fsync_range向硬盘写数据。此操作发生在当读取的数据repl_transfer_read和该值的差值大于等于REPL_MAX_WRITTEN_BEFORE_FSYNC时发生。因此该值也是一个累加值,累加每次同步到硬盘的数据量, server.repl_transfer_last_fsync_off += sync_size。

  23) server.rdb_del_sync_files: 当replica 在在repl_diskless_load 为false 时,创建临时RDB文件存储master同步过来的RDB文件,此值用来设置是否在这之后删除这个临时RDB文件.

  24) server.repl_down_since: 初始化为0,该值记录当该replica实例检测到失去与master连接时,设置为当前服务器时间,该值在处于集群时会用到。

  25) server.repl_timeout: server.unixtime - slave->repl_ack_time > server.repl_timeout, 此时会判断为和该slave 断开连接

 

3 当某个实例 执行 replicaof 命令之后到与其'master' 完成 psync 或 sync 的过程:

  假设两个redis单独运行的实例 s1 and s2, s1 通过客户端执行 replicaof 命令请求成为 s2 的 replica

  1. s1设置自己的masterhost 和 masterport, 断开自己的blocked clients(if any), 断开自己的slaves (if any),  取消 replicationHandShake(if any), 因为自己之前是master, s1 会创建 cacheMaster(client*) 然后设置自己的同步状态为 REPL_STATE_CONNECT, 然后创建与master连接,并且注册 syncWithMaster 为回调函数处理同步问题

  2.连接成功后,s1回调syncWithMaster, 首先发送给s2

一个 "PING" 命令,但是在发送之前在需要先

connSetReadHandler(conn, syncWithMaster);
connSetWriteHandler(conn, NULL);

设置处理s2返回数据的回调函数

  3.s2收到PING,调用pingcommand 处理,返回给s1 一个"PONG".

  4.s1在2步骤中设置了回调函数为syncWithMaster,在这函数里处理s2的返回

如果s2配置了需要AUTH操作,则s1进行AUTH认证,认证之后进入REPL_STATE_SEND_PORT状态,根据配置通过"REPLCONF"命令发送自己的port 给s2 供s2 在INFO命令中显示replicas 的监听端口 "replconf listening-port portval" 发送完之后状态变为REPL_STATE_RECEIVE_PORT

  5.s2 在收到s1的 "replconf" 命令之后,通过replconfCommand命令进行处理,该命令供replicas 在进行同步前与master 进行一些同步处理. master 在replconfCommand中进行参数比对,判断是设置监听端口,于是读取端口值,并将此值赋值给处理与s1的(client*)

c->slave_listening_port = port;

  6. s1收到s2的回应之后,用与4同样的方式,判断时候需要向s2 发送自己的slave_announce_ip, 如果配置了就发送,没配置不发送,状态变为REPL_STATE_SEND_CAPA, 处在这个状态后,s1 向 s2 发送自己的当前的capabilities

sendSynchronousCommand(SYNC_CMD_WRITE,conn,"REPLCONF",
"capa","eof","capa","psync2",NULL),这个通知告诉s2, 

1 "capa","eof": 告诉 s2自己(s1) "supports EOF-style RDB transfer for diskless replication",这种是在master配置了 repl-diskless-sync 情况下发生

2 "capa", "psync2" 告诉 s2 自己(s1) "supports PSYNC v2, so understands +CONTINUE <new repl ID>"

之后进入 REPL_STATE_RECEIVE_CAPA 状态

  7. s2 在收到步骤6发来的消息后, 用 replconfCommand 处理将处理与s1连接的

client* 设置为: c->slave_capa |= SLAVE_CAPA_EOF; c->slave_capa |= SLAVE_CAPA_PSYNC2; 然后返回给 s1 "OK".

  8. s1 收到 s2 回应之后进入 REPL_STATE_SEND_PSYNC 状态,并且尝试与s2 进行

partialResync,于是调用slaveTryPartialResynchronization,向s2发送"PSYNC" 自己当前的psync_replid 以及 psync_offset 供 s2 判断能否进行 psync. 发送成功后进入PSYNC_WAIT_REPLY 状态

  9. s2 收到 s1的 psync 命令后,用 syncCommand 处理函数处理,判断如果是 psync

调用 masterTryPartialResynchronization 判断能否进行psync, 如果可以返回给 s1 "+CONTINUE" 及对应部分同步的数据,如果不能则执行 full sync. 将处理与s1连接的client置为 c->flags |= CLIENT_SLAVE 状态. s2 进行 full sync 时,需要创建子进程去执行bgsave,

当master 收到 replica 的同步命令时,如果此时master 正有子进程执行bgsave, 则需要判断正在进行的bgsave产生的数据是否对replicas 们有效,判断方法就是检查当前server.slaves列表中是否有slave处于SLAVE_STATE_WAIT_BGSAVE_END状态,如果有的话,表明在子进程bgsave过程新的slave们可以共用这次bgsave产生的数据,否则就要等待下一次bgsave.补充slave的SLAVE_STATE_WAIT_BGSAVE_END的状态,同步开始之前某个slave处于该状态,表明返回给slave的 snprintf(buf,sizeof(buf),"+FULLRESYNC %s %lld\r\n",

server.replid,offset).

如果此时没有子进程进行bgsave,则开始startBgsaveForReplication(c->slave_capa)

  10.s1 收到 s2 的回应,可能是fullsync 也可能是psync,处理完之后,读取s2随后传来的数据。这里边有一个细节,当s1和s2之间可以进行PSYNC时,此时s1根据s2的返回值,判断可以进行psync后,

将处理master传来的数据回调函数设置为 readQueryFromClient 而在处理 FULLSYNC 时的回调函数是 readSyncBulkPayload。因为在进行 psync 时,s2 是把和 s1的差异缓存中的数据传过来,而差异缓存中的数据是正经的redis协议格式的数据,此时s1实际上是相当于接受master传来的命令进行执行,所以用readQueryFromClient 来进行命令处理。当进行FULLSYNC时回调函数设为readSyncBulkPayload,因为此时,master传来的数据是经过bgsave产生的数据,需要单独处理。同时从s1角度来说,设置和没设置repl-diskless-load 又需要不同处理。处理过程参考该字段含义及上文提到的内容。具体处理过程见该函数本身readSyncBulkPayload。

这篇关于Redis Replication主备复制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!