美文网首页面试题准备
Redis中 bgsave 方式持久化的细节问题

Redis中 bgsave 方式持久化的细节问题

作者: 白花蛇草可乐 | 来源:发表于2019-12-11 22:15 被阅读0次

    因为有小伙伴问Redis的bgsave命令里面,cow(copy on write)到底是如何实现的,所以顺便复习一下RDB相关的知识点。

    1.RDB的基本概念

    Redis有两种数据持久化的方式:AOF和RDB。

    简单来说,AOF是记录数据增量的方式,将每次对服务器写的操作存入日志(类似MySQL的binlog);而RDB是记录全量数据,根据指定的时间间隔对数据进行快照存储,以二进制格式文件(后缀RDB)保存在硬盘当中。

    2.RDB的触发方式

    2-1、配置文件

    最常见的使用RDB进行持久化的方式,是在配置文件中配置Redis进行快照保存的时机:

    save [seconds] [changes]
    

    意为在[seconds]秒内如果发生了[changes]次数据修改,则进行一次RDB快照保存,例如

    save 60 100
    

    可以配置多条save指令,让Redis执行多级的快照保存策略。

    Redis默认开启RDB快照,默认的RDB策略如下:

    save 900 1
    save 300 10
    save 60 10000
    

    2-2、手工触发

    也可以直接使用手工命令的方式触发RDB生成快照文件。

    一种是 save 命令

    redis> save
    OK
    

    save 命令是同步方式生成快照,会造成Redis阻塞,所有后续到达的命令要等待save完成以后才能执行。

    另一种是 bgsave 命令

    redis> bgsave
    Background saving started
    

    bgsave 命令采用异步方式生成快照,Redis会fork出一个子进程进行RDB文件的生成。

    Redis只有在fork子进程时被阻塞,子进程完成快照生成的同时,Redis可以正常工作。

    2-3、其他触发方式

    • 主从复制时,自动生成RDB文件
    • Redis中的debug reload提供debug级别的重启(不清空内存),此时自动生成RDB文件
    • shutdown会自动生成RDB文件

    3.bgsave的工作流程

    重点说一下 bgsave 是如何使用异步方式生成快照的。

    一般资料提到这里的时候都是一句话带过,说Redis创建子进程以后,利用cow方式完成快照文件的生成。这没有错,但是大多数都没说清楚这个cow是如何工作的。

    我甚至在一些博客上看到“fork消耗额外内存”、“fork时对内存的消耗比较大”这样的说法。

    这其实是没有理解清楚Redis fork出来的子进程是如何工作的。

    3-1、什么是cow

    cow = copy on write

    这是一种简单的读写分离思想,适用于读多写少的并发场景。比如黑白名单,热点文章等等。

    正常情况下我们说cow,指的是修改共享资源时,将共享资源copy一份,加锁后修改,再将原容器的引用指向新的容器。

    对于java来说,是有线程的cow容器的,比如CopyOnWriteArrayList。

    另外就是cow保证的是最终一致性而不是强一致。

    3-2、Redis面临的问题

    在Redis生成快照这个问题上,显然不能直接使用标准的cow流程来操作。

    很简单,这会导致Redis的可用内存容量就直接减半。

    cow的第一步是要将Redis在内存中的内容copy一份副本;然后主进程操作原数据,进行正常的读写操作,子进程利用副本专心写盘,写完以后销毁子进程。

    真的直接copy一份副本的话,多少内存够用啊?这不是简单的copy一个java容器那么简单。

    3-3、Redis的cow

    1. Redis创建子进程以后,根本不进行数据的copy,主进程与子线程是共享数据的。主进程继续对外提供读写服务。
    2. 虽然不copy数据,但是kernel会把主进程中的所有内存页的权限都设为read-only,主进程和子进程访问数据的指针都指向同一内存地址。
    3. 主进程发生写操作时,因为权限已经设置为read-only了,所以会触发页异常中断(page-fault)。在中断处理中,需要被写入的内存页面会复制一份,复制出来的旧数据交给子进程使用,然后主进程该干啥就干啥。

    也就是说,在进行IO操作写盘的过程中(on write),对于没有改变的数据,主进程和子进程资源共享;只有在出现了需要变更的数据时(写脏的数据),才进行copy操作。

    在最理想的情况下,也就是生成RDB文件的过程中,一直没有写操作的话,就根本不会发生内存的额外占用。

    当然,仍然需要合理配置Linux的内存分配策略。避免在写操作过于集中时,发生因为物理内存不足导致fork失败的情况。

    相关文章

      网友评论

        本文标题:Redis中 bgsave 方式持久化的细节问题

        本文链接:https://www.haomeiwen.com/subject/qcoagctx.html