Redis持久化机制

参考:Redis详解(3)数据持久化机制

一、Redis持久化方式

  • RDB定时快照方式(snapshot): RDB 将数据库的快照(snapshot)以二进制的方式保存到磁盘中。
  • AOF基于语句追加文件的方式:则以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。

二、RDB定时快照方式(snapshot)

1.具体实现

RDB实际是在Redis内部一个定时器事件,每隔固定时间去检查当前数据发生的改变次数与时间是否满足配置的持久化触发的条件,如果满足则通过操作系统fork调用来创建出一个子进程,这个子进程默认会与父进程共享相同的地址空间,这时就可以通过子进程来遍历整个内存来进行存储操作,而主进程则仍然可以提供服务,当有写入时由操作系统按照内存页(page)为单位来进行copy-on-write保证父子进程之间不会互相影响。

具体实现:是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。

2.bgsave实现流程

1)Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(后面会详细介绍该命令)的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。

2)FORK子进程:RDB写入时,会连内存一起Fork出一个新进程(子进程默认会与父进程共享相同的地址空间),遍历新进程内存中的数据写文件,这样就解决了些Snapshot过程中又有新的写入请求进来的问题。这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令 。父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令

3)子进程创建RDB文件:RDB会先写到临时文件,完了再Rename成,这样外部程序对RDB文件的备份和传输过程是安全的。而且即使写新快照的过程中Server被强制关掉了,旧的RDB文件还在。

3)可配置是否进行压缩,压缩方法是字符串的LZF算法,以及将string形式的数字变回int形式存储。
4)动态所有停止RDB保存规则的方法:redis-cli config set save “”

该持久化的主要缺点是定时快照只是代表一段时间内的内存映像,所以系统重启会丢失上次快照与重启之间所有的数据。


image.png

三、AOF基于语句追加方式

AOF方式实际类似mysql的基于语句的binlog方式,即每条会使Redis内存数据发生改变的命令都会追加到一个log文件中,也就是说这个log文件就是Redis的持久化数据。

1.AOF 工作原理

是将数据也是先存在内存,但是在存储的时候会使用调用fsync来完成对本次写操作的日志记录,这个日志文件其实是一个基于Redis网络交互协议的文本文件。AOF调用fsync也不是说全部都是无阻塞的,在某些系统上可能出现fsync阻塞进程的情况,对于这种情况可以通过配置修改,但默认情况不要修改。AOF最关键的配置就是关于调用fsync追加日志文件的平率,有两种预设频率,always每次记录进来都添加,everysecond 每秒添加一次。

同步命令到 AOF 文件的整个过程可以分为三个阶段:

1、命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
2、缓存追加:AOF 程序根据接收到的命令数据, 然后每隔一定的时间(比如,每隔一秒)将 将协议内容写入AOF 缓冲区中.
3、文件写入保存:当达到 AOF同步条件(例如OS Cache 满了),fsync 函数或者 fdatasync 函数会被调用,将 OS Cache 中的数据写入磁盘文件。


image.png

2.Redis的Rewrite

redis中的数据其实有限的,很多数据可能会自动过期,可能会被用户删除,可能会被redis用缓存清除的算法清理掉。redis中的数据会不断淘汰掉旧的,就一部分常用的数据会被自动保留在redis内存中,所以可能很多之前的已经被清理掉的数据,对应的写日志还停留在AOF中,AOF日志文件就一个,会不断的膨胀,到很大很大。

问题1:AOF文件会变得越来越大

AOF文件通过同步 Redis 服务器所执行的命令, 从而实现了数据库状态的记录, 但是, 这种同步方式会造成一个问题: 随着运行时间的流逝, AOF 文件会变得越来越大。

问题2:AOF 文件的体积就会急速膨胀

另一方面, 有些被频繁操作的键, 对它们所调用的命令可能有成百上千、甚至上万条, 如果这样被频繁操作的键有很多的话, AOF 文件的体积就会急速膨胀, 对 Redis 、甚至整个系统的造成影响。

Redis 需要对 AOF文件自动在后台每隔一定时间做rewrite(重写)操作, 创建一个新的 AOF 文件来代替原有的 AOF 文件, 新 AOF 文件和原有 AOF 文件保存的数据库状态完全一样, 但新 AOF 文件的体积小于等于原有 AOF 文件的体积。

解决方案:AOF的rewrite实现

rewrite操作对于老日志文件中对于Key的多次操作,只保留最终的值的那次操作记录到日志文件中,从而缩小日志文件的大小。

AOF的rewrite流程

(1)超过AOF的阈值,redis fork一个子进程,进行rewrite操作。子进程基于当前内存中的数据,构建日志,开始往一个新的临时的AOF文件中写入日志.
(2)同时开启 AOF 重写缓冲区,保存这段时间内新增的写指令。
(3)之前的 AOF 操作保持,继续正常工:redis主进程,接收到client新的写操作之后,在内存中写入日志,同时新的日志也继续写入旧的AOF文件
(4)(1)中的子进程写完新的日志文件之后,redis主进程将将 AOF 重写缓冲区的指令追加至 AOF' 文件末尾。
(5)最后用 AOF' 替换 AOF 文件。

四、两者比较

1.性能:Snapshot方式的性能是要明显高于AOF方式的,原因有两点:

1)采用二进制方式存储数据,数据文件比较小,加载快速.
2)存储的时候是按照配置中的save策略来存储,每次都是聚合很多数据批量存储,写入的效率很好,而AOF则一般都是工作在实时存储或者准实时模式下。相对来说存储的频率高,效率却偏低。

  1. 对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

2.数据安全:AOF数据安全性高于Snapshot存储,原因:

Snapshot存储是基于累计批量的思想,也就是说在允许的情况下,累计的数据越多那么写入效率也就越高,但数据的累计是靠时间的积累完成的,那么如果在长时间数据不写入RDB,但Redis又遇到了崩溃,那么没有写入的数据就无法恢复了,但是AOF方式偏偏相反,根据AOF配置的存储频率的策略可以做到最少的数据丢失和较高的数据恢复能力。

五、Redis持久化磁盘IO方式及其带来的问题

有Redis线上运维经验的人会发现Redis在物理内存使用比较多,但还没有超过实际物理内存总容量时就会发生不稳定甚至崩溃的问题

Redis的持久化使用了Buffer IO造成的
所谓Buffer IO是指Redis对持久化文件的写入和读取操作都会使用物理内存的Page Cache,而大多数数据库系统会使用Direct IO来绕过这层Page Cache并自行维护一个数据的Cache.
而当Redis的持久化文件过大(尤其是快照文件),并对其进行读写时,磁盘文件中的数据都会被加载到物理内存中作为操作系统对该文件的一层Cache,而这层Cache的数据与Redis内存中管理的数据实际是重复存储的
虽然内核在物理内存紧张时会做Page Cache的剔除工作,但内核很可能认为某块Page Cache更重要,而让你的进程开始Swap ,这时你的系统就会开始出现不稳定或者崩溃了。
我们的经验是当你的Redis物理内存使用超过内存总容量的3/5时就会开始比较危险了。
下图是Redis在读取或者写入快照文件dump.rdb后的内存数据图:

image.png

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