Redis源码剖析与实战
蒋德钧
中科院计算所副研究员
新⼈⾸单¥59
2808 人已学习
课程目录
已更新 24 讲 / 共 33 讲
0/4登录后,你可以任选4讲全文学习。
课前导读 (2讲)
开篇词 | 阅读Redis源码能给你带来什么?
免费
01 | 带你快速攻略Redis源码的整体架构
数据结构模块 (6讲)
02 | 键值对中字符串的实现,用char*还是结构体?
03 | 如何实现一个性能优异的Hash表?
04 | 内存友好的数据结构该如何细化设计?
05 | 有序集合为何能同时支持点查询和范围查询?
06 | 从ziplist到quicklist,再到listpack的启发
07 | 为什么Stream使用了Radix Tree?
事件驱动框架和执行模型模块 (7讲)
08 | Redis server启动后会做哪些操作?
09 | Redis事件驱动框架(上):何时使用select、poll、epoll?
10 | Redis事件驱动框架(中):Redis实现了Reactor模型吗?
11 | Redis事件驱动框架(下):Redis有哪些事件?
12 | Redis真的是单线程吗?
13 | Redis 6.0多IO线程的效率提高了吗?
14 | 从代码实现看分布式锁的原子性保证
缓存模块 (3讲)
15 | 为什么LRU算法原理和代码实现不一样?
16 | LFU算法和其他算法相比有优势吗?
17 | Lazy Free会影响缓存替换吗?
不定期加餐 (1讲)
加餐1 | Redis性能测试工具的使用
可靠性保证模块 (3讲)
18 | 如何生成和解读RDB文件?
19 | AOF重写(上):触发时机与重写的影响
20 | AOF重写(下):重写时的新写操作记录在哪里?
期中测试 (2讲)
期中测试 | 这些Redis源码知识,你都掌握了吗?
期中测试题答案 | 这些问题你都答对了吗?
Redis源码剖析与实战
15
15
1.0x
00:00/00:00
登录|注册

20 | AOF重写(下):重写时的新写操作记录在哪里?

你好,我是蒋德钧。
在上节课,我给你介绍了 AOF 重写过程,其中我带你重点了解了 AOF 重写的触发时机,以及 AOF 重写的基本执行流程。现在你已经知道,AOF 重写是通过重写子进程来完成的。
但是在上节课的最后,我也提到了在 AOF 重写时,主进程仍然在接收客户端写操作,那么这些新写操作会记录到 AOF 重写日志中吗?如果需要记录的话,重写子进程又是通过什么方式向主进程获取这些写操作的呢?
今天这节课,我就来带你了解下 AOF 重写过程中所使用的管道机制,以及主进程和重写子进程的交互过程。这样一方面,你就可以了解 AOF 重写日志包含的写操作的完整程度,当你要使用 AOF 日志恢复 Redis 数据库时,就知道 AOF 能恢复到的程度是怎样的。另一方面,因为 AOF 重写子进程就是通过操作系统提供的管道机制,来和 Redis 主进程交互的,所以学完这节课之后,你还可以掌握管道技术,从而用来实现进程间的通信。
好了,接下来,我们就先来了解下管道机制。

如何使用管道进行父子进程间通信?

首先我们要知道,当进程 A 通过调用 fork 函数创建一个子进程 B,然后进程 A 和 B 要进行通信时,我们通常都需要依赖操作系统提供的通信机制,而管道(pipe)就是一种用于父子进程间通信的常用机制。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
该试读文章来自付费专栏《Redis源码剖析与实战》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥59
立即订阅
登录 后留言

精选留言(4)

  • Kaito
    1、AOF 重写是在子进程中执行,但在此期间父进程还会接收写操作,为了保证新的 AOF 文件数据更完整,所以父进程需要把在这期间的写操作缓存下来,然后发给子进程,让子进程追加到 AOF 文件中

    2、因为需要父子进程传输数据,所以需要用到操作系统提供的进程间通信机制,这里 Redis 用的是「管道」,管道只能是一个进程写,另一个进程读,特点是单向传输

    3、AOF 重写时,父子进程用了 3 个管道,分别传输不同类别的数据:

    - 父进程传输数据给子进程的管道:发送 AOF 重写期间新的写操作
    - 子进程完成重写后通知父进程的管道:让父进程停止发送新的写操作
    - 父进程确认收到子进程通知的管道:父进程通知子进程已收到通知

    4、AOF 重写的完整流程是:父进程 fork 出子进程,子进程迭代实例所有数据,写到一个临时 AOF 文件,在写文件期间,父进程收到新的写操作,会先缓存到 buf 中,之后父进程把 buf 中的数据,通过管道发给子进程,子进程写完 AOF 文件后,会从管道中读取这些命令,再追加到 AOF 文件中,最后 rename 这个临时 AOF 文件为新文件,替换旧的 AOF 文件,重写结束

    课后题:Redis 中其它使用管道的地方还有哪些?

    在源码中搜索 pipe 函数,能看到 server.child_info_pipe 和 server.module_blocked_pipe 也使用了管道。

    其中 child_info_pipe 管道如下:

     /* Pipe and data structures for child -> parent info sharing. */
        int child_info_pipe[2]; /* Pipe used to write the child_info_data. */
        struct {
            int process_type; /* AOF or RDB child? */
            size_t cow_size; /* Copy on write size. */
            unsigned long long magic; /* Magic value to make sure data is valid. */
        } child_info_data;


    从注释能看出,子进程在生成 RDB 或 AOF 重写完成后,子进程通知父进程在这期间,父进程「写时复制」了多少内存,父进程把这个数据记录到 server 的 stat_rdb_cow_bytes / stat_aof_cow_bytes 下(childinfo.c 的 receiveChildInfo 函数),以便客户端可以查询到最后一次 RDB 和 AOF 重写期间写时复制时,新申请的内存大小。

    而 module_blocked_pipe 管道主要服务于 Redis module。

    /* Pipe used to awake the event loop if a client blocked on a module command needs to be processed. */
    int module_blocked_pipe[2];

    看注释是指,如果被 module 命令阻塞的客户端需要处理,则会唤醒事件循环开始处理。
    2021-09-11
    3
  • 土豆种南城
    一些补充:
    1. 重写子进程写入多少从父进程传来的操作后发出ack?
      答:子进程在正常的重写完成后至多再等一秒,在这一秒内如果有连续20ms没有可读事件发生,那么直接发送ack

    2. 子进程收到父进程回复的ack时管道内还有数据怎么处理?
     答:父进程收到子进程ack后设置server.aof_stop_sending_diff为1,然后回复ack
    子进程收到ack时会再次调用aofReadDiffFromParent尝试把管道里可能存在的数据都读出来
    最后一步将aof_child_diff的内容写入文件,并将文件名rename为temp-rewriteaof-bg-pid.aof
    父进程在serverCron中调用wait3来确认重写子进程执行结果,读取子进程重写的aof文件,在文件末尾再次写入子进程执行结束后父进程积累的数据,最后将文件名重命名成最终文件
    2021-09-14
  • 曾轼麟
    首先回答老师问题:
    1、slave同步RDB文件中返回状态和错误
    通过rdb_pipe_write_result_to_parent和rdb_pipe_read_result_from_child。在进行fork之前,创建一个管道,用于将成功接收到所有写操作的slave服务器的id发送回父服务器。一般是slave在等待bgsave的情况下主库完成bgsave后进行的处理。在rdbSaveToSlavesSockets函数中调用。

    2、用于传输子进程info给父进程
    child_info_pipe,用于将RDB / AOF保存进程的信息从子进程传输给父进程。(例如同步期间产生写时复制的量等信息)。

    3、唤醒由于处理module命令而阻塞的event loop
    module_blocked_pipe(具体细节还没看完)。

    4、自检函数linuxMadvFreeForkBugCheck
    针对arm64 Linux内核的一个bug(太旧的内核可能会导致数据损坏),自检时候会fork出一个子进程来进行判断是否发现bug,如果发现则响应主进程并输出日志提示升级内核。

    总结:
    老师本期介绍了,在AOF重写期间,主进程产生的增量写命令是如何通过管道技术同步给子进程,(管道技术是基于内核进行的跨进程同步方案)。为此Redis实现了三种管道来完成AOF重写期间,主子进程的命令同步。三种管道分别为:
    1、父进程传输给子进程的命令管道。
    2、子进程响应父进程的ack管道。
    3、父进程确认收到子进程ack的管道。
    (这里会发现设计上多少有点和tcp握手类似)

    此外需要主意的是,在管道交互期间,读取管道的数据是通过aeCreateFileEvent创建文件事件进行的。那么结合前面的文章内容-IO多线程可以得知:在主子进程同步命令和处理ack信号是有IO线程参与的。
    2021-09-13
  • 可怜大灰狼
    回答问题。
    1.rdbSaveBackground。2.rdbSaveToSlavesSockets,diskless模式下直接把rdb通过socket发送给slave。3.moduleInitModulesSystem,和第三方子模块通信。4.linuxMadvFreeForkBugCheck,检查MADV_FREE在arm64 Linux内核上bug,具体见https://github.com/redis/redis/commit/b02780c41dbc5b28d265b5cf141c03c1a7383ef9。
    2021-09-11
收起评论
4
返回
顶部