Redis持久化

RDB

说明

生成内存文件快照

生成方式
  1. 手动触发
    1. save
      1. 由当前主线程执行,会阻塞redis操作,由主线程生成rdb文件
    2. bgsave
      1. 主线程fork出子线程,由子线程来完成rdb写入,但是在fork子线程的过程中,是阻塞的,而且内存越大,fork子线程的时间也就越长。子线程生成完rdb文件后,原子替换新rdb文件为原rdb文件,并通知主线程生成完毕。
  2. 自动触发
    1. redis.conf中若配置了save m n,即m秒中发生了n次修改时,会触发bgsave
    2. 主从复制时,从节点要从主节点全量复制时,也会触发bgsave
    3. debug reload重启时,也会先bgsave保存全量快照
    4. shutdown时,如果没有配置aof,也会触发
rdb配置
  1. ldbfilename:RDB文件在磁盘上的名称。
  2. dir:RDB文件的存储路径。默认设置为“./”,也就是Redis服务的主目录。
  3. stop-writes-on-bgsave-error:上文提到的在快照进行过程中,主进程照样可以接受客户端的任何写操作的特性,是指在快照操作正常的情况下。如果快照操作出现异常(例如操作系统用户权限不够、磁盘空间写满等等)时,Redis就会禁止写操作。这个特性的主要目的是使运维人员在第一时间就发现Redis的运行错误,并进行解决。一些特定的场景下,您可能需要对这个特性进行配置,这时就可以调整这个参数项。该参数项默认情况下值为yes,如果要关闭这个特性,指定即使出现快照错误Redis一样允许写操作,则可以将该值更改为no。
  4. rdbcompression:该属性将在字符串类型的数据被快照到磁盘文件时,启用LZF压缩算法。Redis官方的建议是请保持该选项设置为yes,因为“it’s almost always a win”。
  5. rdbchecksum:从RDB快照功能的version 5 版本开始,一个64位的CRC冗余校验编码会被放置在RDB文件的末尾,以便对整个RDB文件的完整性进行验证。这个功能大概会多损失10%左右的性能,但获得了更高的数据可靠性。所以如果您的Redis服务需要追求极致的性能,就可以将这个选项设置为no。
问题
  1. redis在生成rdb的过程中,如果有新的写操作进来,如何保证一致性?
    由copy-on-write来实现,在写操作的过程中,如果主线程需要修改一块内存值,那么这块内存就会生成一个内存副本,由子线程来修改并写入到rdb中。


    image.png
  1. 在执行快照的阶段时,如果发生服务器崩溃怎么办?
    如果发生服务器崩溃,此次文件写入就会失败,redis会以上次备份的rdb文件为准。

  2. 可以每秒做一次快照吗?
    如果频繁的执行全量快照,会发生2个问题:

    1. 每次全量写入文件,会造成大量的IO,增加系统负担
    2. 虽然子线程不会阻塞主线程,但是主线程fork出子线程的过程,依然是会对主线程阻塞的,依然会影响性能
优缺点
  1. 优点

    1. RDB在数据恢复时,会执行速度较快,优于AOF
    2. RDB是基于全量数据存储的,且经过了LZF算法进行压缩,内存空间远远小于原数据,适用于备份,全量复制。
  2. 缺点

    1. 不具有实时性,无法做到秒级的持久化
    2. 文件是二进制,不利于查看
    3. fork子线程的过程较重,频繁执行成本较高
    4. 版本兼容RDB问题

AOF

说明

写后日志,先执行内存操作,再将操作语句写入日志。

为什么采用写后日志
  1. 优点

    1. 不阻塞内存写入,内存修改完毕后才去写日志
    2. 避免额外的资源开销,如果有一些redis操作是错误的,将不会记录到aof中,减少一部分错误操作的冗余记录
  2. 缺点

    1. 如果内存操作完毕后宕机,日志写入会失败,恢复将丢失数据
    2. 主线程写磁盘慢,导致写盘慢,阻塞后续操作
如何实现
  1. 命令追加
    1. redis在执行完一个写命令后,会将命令追加到服务器的缓冲区内(aof_buf)
  2. 文件写入和同步
    redis提供了3种写入策略
    1. always:每个写命令执行完毕后,立刻同步的写回磁盘
    2. everySec:输出到缓冲区后,每隔1s,将缓冲区内容写回磁盘
    3. no:输出到缓冲区后,由系统控制何时写回磁盘
aof配置
  1. appendonly:默认情况下AOF功能是关闭的,将该选项改为yes以便打开Redis的AOF功能。
  2. appendfilename:这个参数项很好理解了,就是AOF文件的名字。
  3. appendfsync:这个参数项是AOF功能最重要的设置项之一,主要用于设置“真正执行”操作命令向AOF文件中同步的策略。什么叫“真正执行”呢?还记得Linux操作系统对磁盘设备的操作方式吗? 为了保证操作系统中I/O队列的操作效率,应用程序提交的I/O操作请求一般是被放置在linux Page Cache中的,然后再由Linux操作系统中的策略自行决定正在写到磁盘上的时机。而Redis中有一个fsync()函数,可以将Page Cache中待写的数据真正写入到物理设备上,而缺点是频繁调用这个fsync()函数干预操作系统的既定策略,可能导致I/O卡顿的现象频繁 。
  • 与上节对应,appendfsync参数项可以设置三个值,分别是:always、everysec、no,默认的值为everysec
  1. no-appendfsync-on-rewrite:always和everysec的设置会使真正的I/O操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的Redis是没法解决的。为了尽量缓解这个情况,Redis提供了这个设置项,保证在完成fsync函数调用时,不会将这段时间内发生的命令操作放入操作系统的Page Cache(这段时间Redis还在接受客户端的各种写操作命令)。
  2. auto-aof-rewrite-percentage:上文说到在生产环境下,技术人员不可能随时随地使用“BGREWRITEAOF”命令去重写AOF文件。所以更多时候我们需要依靠Redis中对AOF文件的自动重写策略。Redis中对触发自动重写AOF文件的操作提供了两个设置:auto-aof-rewrite-percentage表示如果当前AOF文件的大小超过了上次重写后AOF文件的百分之多少后,就再次开始重写AOF文件。例如该参数值的默认设置值为100,意思就是如果AOF文件的大小超过上次AOF文件重写后的1倍,就启动重写操作。
  3. auto-aof-rewrite-min-size:参考auto-aof-rewrite-percentage选项的介绍,auto-aof-rewrite-min-size设置项表示启动AOF文件重写操作的AOF文件最小大小。如果AOF文件大小低于这个值,则不会触发重写操作。注意,auto-aof-rewrite-percentage和auto-aof-rewrite-min-size只是用来控制Redis中自动对AOF文件进行重写的情况,如果是技术人员手动调用“BGREWRITEAOF”命令,则不受这两个限制条件左右。
AOF重写

AOF会将每个执行命令记录到磁盘中,随着时间增长,文件越来越大,这时候就需要将AOF文件重写,来减少文件的大小。


image.png
问题
  1. AOF重写会阻塞吗?
    AOF日志需要重写时,会由主线程fork出一个子线程bgrewriteaof,并copy一份内存副本,然后子线程就可以根据内存副本,来生成对应的写操作,重写AOF文件。子线程重写的过程中对主线程没有影响,但在fork的过程中会阻塞。

  2. AOF日志何时会重写?
    相关的配置有2个
    auto-aof-rewrite-min-size: 重写时的aof文件的最大大小,默认是64MB
    auto-aof-rewrite-percentage: 重写时当前AOF文件的大小,和上次重写完毕后的AOF文件的差值的百分比。比如上次重写完毕后是10MB,配置为100%,那么在新的aof文件为20MB时,即发生重写。

  3. 重写日志时,如果有新数据写入怎么办?
    一份写入,两份日志,当在重写的过程中,主线程依然正常对之前的AOF文件正常输入,同时会复制一份操作命令,追加到新AOF文件的缓冲区,等待AOF文件重写完毕后。主线程会将新文件的缓冲区输出到新AOF文件中,但在高并发的情况下,缓冲区的数据可能会太多,这样就会对主线程造成阻塞。但Redis后来使用了linux的管道技术,可以让aof重写期间就能进行回放,这样后续只需写入少量操作即可。

  4. 总结:

    1. fork出子线程,子线程去重写AOF文件
    2. 过程中如果有写操作,追加到新文件的缓冲区
    3. 重写完毕后替换老文件,并将缓冲区输出到新文件
  5. 主线程fork出子线程,内存的操作过程是怎么样的
    在主线程fork子线程,阻塞的过程中,会创建一个当前内存的虚拟映射表,根据指针映射到之前的物理内存中。fork完毕后,如果有写入操作,则将待修改的内存创建一份副本,在副本上做修改,而子线程依然指向的是原有的内存。


    image.png
  1. 在重写日志的过程中,哪些地方会发生阻塞

    1. fork出子线程时
    2. 在重写时,如果有bigkey进入,则需要产生对应副本,也可能会阻塞
    3. 重写完毕后,替换原文件,并输出缓冲区,会阻塞
  2. 为什么AOF不采用原AOF的文件

    1. 父子线程同时操作一个文件,会产生竞争
    2. 如果重写失败,会导致原文件被污染,无法再使用

RDB和AOF混合(4.0版本)

image.png

image.png

实践:

  • 降低fork的频率,比如可以手动来触发RDB生成快照、与AOF重写
  • 控制Redis最大使用内存,防止fork耗时过长;
  • 使用更牛逼的硬件;
  • 合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败。

参考文档:
https://www.pdai.tech/md/db/nosql-redis/db-redis-x-rdb-aof.html

你可能感兴趣的:(Redis持久化)