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


    文章目录
    1.RDB的基本概念
    2.RDB的触发方式
    2-1、配置文件
    2-2、手工触发
    2-3、其他触发方式
    3.bgsave的工作流程
    3-1、什么是cow
    3-2、Redis面临的问题
    3-3、Redis的cow

    因为有小伙伴问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失败的情况。

  • 相关阅读:
    Informix IDS 11系统办理(918考试)认证指南,第6部分:IDS备份和恢复(1)
    Informix IDS 11体系处置(918检验)认证指南,第 4 局部: 性能调优(7)
    我常用网址整理
    System.Insert 插入字符串
    System.Length 获取字符串或数组的长度
    System.New、System.Dispose 为某个指针申请和释放内存
    System.GetMem、System.FreeMem 申请和释放内存
    学习 TList 类的实现[1]
    学习 TList 类的实现[2]
    System.ReallocMem 重新申请内存
  • 原文地址:https://www.cnblogs.com/xiaolei2017/p/15713194.html
Copyright © 2020-2023  润新知