关于redis:Redis-主从复制的机制浅析

48次阅读

共计 3699 个字符,预计需要花费 10 分钟才能阅读完成。

前言

明天持续来看看无关 Redis 的一个问题,主从复制。通常,对于大多数的场景来说,读比写更多,于是对于缓存的程度扩大,其中的一个形式“主从复制”就是一个常见的思路。有了主从复制,那么能够扩大出很多从节点来应答大量的读申请。那么问题来了 Redis 的主从复制是如何实现的呢?

PS:本文仅关怀复制的机制,不关怀主节点下线从新选等等异常情况

前置常识

  • 你须要晓得 Redis 的长久化形式,RDB 和 AOF
  • Redis 执行命令的基本思路

审题

题目自身不简单,提问者问这个问题的想法可能会有上面几个方面

  1. 理解 Redis 的主从复制机制的话,如果在理论应用过程中呈现问题就更容易排查
  2. 在设计复制机制的时候须要留神和思考什么问题
  3. 这样的设计是否能利用在别的场景中

尝试思考

假如你齐全没有看过 Redis 源码来思考这个问题,能够从上面几个角度去尝试剖析,并猜想答案。

  1. 首先,想到一个关系户,也就是咱们罕用的 Mysql,它也有主从复制,如果你理解 binlog 那么能够尝试从这里着手,尽管不同,但思路应该是差不多的。
  2. 而后,简化问题,主从复制,无非就是将数据发送过来,对方承受保留
  3. 不可能每次都复制的是全量数据,那么必定须要有机制去确保如何每次复制增量的数据
  4. 复制的是什么?

    1. 复制的是数据自身?数据只有变动就将变动的 kv 间接扔给从节点?
    2. 复制的是执行命令?将客户端执行的命令发送给子节点执行一次?

解决

有了下面的思考,其实理论也就有思路的。首先主从复制必定有两种状况,一种就是第一次复制,也就是要执行一次全量复制,将主节点的所有数据到复制到从节点下来;另一种就是增量复制,在数据同步之后后续的增量数据放弃同步。

全量同步

长久化数据

因为须要全量同步所有数据,咱们晓得 Redis 数据在内存外面,既然要发送,那势必须要先长久化一次。也就是先 SYNC 一遍,通过办法 startBgsaveForReplication 来实现的
代码地位在:https://github.com/redis/redis/blob/14f802b360ef52141c83d477ac626cc6622e4eda/src/replication.c#L855
这个问题不大,就是保留一个 RDB 文件。

发送数据

这个也很不难,就是将数据间接扔过来就好了。
代码地位在:https://github.com/redis/redis/blob/14f802b360ef52141c83d477ac626cc6622e4eda/src/replication.c#L1402

增量同步

后续的工作就是增量同步后续产生的数据了。在猜想时咱们想到有两种复制形式,一种是间接复制数据,这种形式复制 RDB 是可行,在全量同步的时候用这个必定更好,如果同步命令那么从节点还需再执行一次过于简单和麻烦,还耗时。而对于后续的增量同步来说,必定是同步命令来的更高效(不过还是得看理论)。

上面就是流传命令的办法:

/* Propagate the specified command (in the context of the specified database id)
 * to AOF and Slaves.
 *
 * flags are an xor between:
 * + PROPAGATE_NONE (no propagation of command at all)
 * + PROPAGATE_AOF (propagate into the AOF file if is enabled)
 * + PROPAGATE_REPL (propagate into the replication link)
 *
 * This is an internal low-level function and should not be called!
 *
 * The API for propagating commands is alsoPropagate().
 *
 * dbid value of -1 is saved to indicate that the called do not want
 * to replicate SELECT for this command (used for database neutral commands).
 */
static void propagateNow(int dbid, robj **argv, int argc, int target) {if (!shouldPropagate(target))
        return;

    /* This needs to be unreachable since the dataset should be fixed during
     * replica pause (otherwise data may be lost during a failover) */
    serverAssert(!(isPausedActions(PAUSE_ACTION_REPLICA) &&
                   (!server.client_pause_in_transaction)));

    if (server.aof_state != AOF_OFF && target & PROPAGATE_AOF)
        feedAppendOnlyFile(dbid,argv,argc);
    if (target & PROPAGATE_REPL)
        replicationFeedSlaves(server.slaves,dbid,argv,argc);
}

这个办法就是将增量命令流传给 AOF 和 Slaves,AOF 就是长久化的另一种形式,而 Slaves 就是咱们须要同步的从节点了。具体 replicationFeedSlaves 办法就不具体看了。

监控状态

这个其实是咱们在猜想的时候漏掉的,想来也是,master 必定须要晓得 slave 的状态,如果连不上了,必定要解决,在 replication.c 中有这样一个办法

/* Replication cron function, called 1 time per second. */
void replicationCron(void) {

看名字和正文就秒懂了,每秒执行一次的同步定时工作。

而其中调用了 replicationFeedSlaves 办法,也就是 PING 一下,看看活着没

replicationFeedSlaves(server.slaves, -1, ping_argv, 1);

可能导致的问题

第一次同步 RDB 工夫太长?

如果咱们 redis 寄存的数据很多,第一次同步会有两个工夫,一个是 bgsave 的工夫,这个工夫其实还好,毕竟平时就是要执行的,而第二个工夫就是传输数据的工夫,这个工夫就取决于带宽了。

不过首先这个操作时,主节点仍旧能够被读写,只不过操作均被缓存了,所以倒是不用放心这段时间无奈被应用。难就在如果数据过多可能真的会导致一个问题就是,同步 -> 超时 -> 重试,而后一直循环,所以为了防止这样的状况呈现,倡议 Redis 返回别间接把主机全副内存吃完。通常 maxmemory 设置为 75% 就绝对不会呈现问题,也不容易 OOM。

当然,有人必定会问,能不能间接先手动拷贝 RDB 文件来缩小同步工夫,实际操作过我通知你,不要手动操作,容易呈现意想不到的问题,当呈现问题之后,数据还是会不同步,还是会执行从新同步,还不如第一次就手动让程序本人来。

优化

流传 cache

命令在流传的阶段设置了主从同步发送的缓冲区,通过保护一个缓冲区来保障当主节点无需期待,从节点本人凭实力拿就好了,即便有一段时间忽然抖动了一下,也没事,缓冲区外面还有,持续同步就行嘞。但当齐全超过缓冲区的接受范畴,那么还是须要执行一次全量同步来保证数据统一。

无盘加载

之前看代码的时候就留神到了一个参数 repl_diskless_sync 翻译过去就是无盘同步,显然这个优化是 Redis 留神到第一次同步的时候,如果马上写入 RDB 显然是有点慢了,间接 dump 内存必定会来的更快,所以这就是无盘,也就是不先落盘。

总结

最初用一张图来总结整个过程

咱们看着这个图咱们也能够想到,其实这样复制的策略在绝大多数复制的场景中都是实用的,如果理论没有命令这个说法,那就将数据拆分成小块 (chunk) 来同步。须要留神点和优化点可能 Redis 都帮你想好了,对着抄就能够了。所以,我称为一种设计为”单向同步“,那么如果什么是多向同步呢?也就是多集体同时编辑或操作数据,相互同步的策略,此时就须要一些 diff 算法和策略了,你也能够思考设计看看,看具体会遇到什么问题。

参考链接

  • https://redis.io/docs/management/replication/
  • https://redis-doc-test.readthedocs.io/en/latest/topics/replic…
  • https://developer.baidu.com/article/detail.html?id=294748
  • http://machicao2013.github.io/gitbook/redis/replication/princ…
  • https://segmentfault.com/a/1190000040248346
  • https://www.xiaolincoding.com/redis/cluster/master_slave_repl…

正文完
 0