谈谈Redis中两种常用持久化机制RDB和AOF
- 一、Redis持久化机制介绍
- 二、RDB持久化
-
- I、触发机制
-
- 1、save触发方式(手动触发)
- 2、bgsave触发方式(手动触发)
- 3、自动触发
-
- save m n的实现原理
- II、RDB文件
- III、优势和劣势
-
- RDB优势
- RDB劣势
- 三、AOF持久化
-
- I、触发机制
-
- 1、bgrewriteaof触发(手动触发)
- 2、自动触发
- II、AOF工作原理
- III. AOF缓冲区同步文件策略
-
- 1、appendfsync no 不保存
- 2、appendfsync everysec 每一秒钟保存一次
- 3、appendfsync always 每执行一个命令保存一次
- IV、AOF优势和劣势
-
- AOF优势
- AOF劣势
- 四、RDB和AOF到底该如何选择
对于我们的企业来说,一个企业级别的redis架构持久化的操作是必不可少的!
那么我们今天就来学习一下redis的两种持久化方式吧!
一、Redis持久化机制介绍
RDB(Redis DataBase)和AOF(Append Only File)两种持久化机制的介绍:
- RDB持久化机制,其实就是把数据以快照的形式保存在磁盘上。什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
- AOF机制对每条写入命令作为日志,以append-only的模式写入一个日志文件中,在redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集。
- 通过RDB或AOF,都可以将redis内存中的数据给持久化到磁盘上,然后可以将这些数据备份到别的地方,比如说阿里云,华为云等云服务。此时如果redis挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动redis,redis就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。
注意 1:如果RDB和AOF两种持久化机制同时启用,那么在redis重启的时候,会使用AOF来重新构建数据,因为AOF中的数据更加完整(优先加载)
注意 2:如果我们想要redis仅仅作为纯内存的缓存来用,那么可以禁止RDB和AOF的持久化机制
二、RDB持久化
I、触发机制
既然RDB机制是通过把某个时刻的所有数据生成一个快照来保存,那么就应该存在触发机制,来实现这个过程。对于RDB来说,触发分为手动触发(save、bgsave)和自动触发两种,我们分别来看一下
1、save触发方式(手动触发)
该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下:
执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。
语法:
redis 127.0.0.1:6379> SAVE #启动save指令
2、bgsave触发方式(手动触发)
执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体流程如下:
具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。
语法:
redis 127.0.0.1:6379> BGSAVE #启动bgsave后台服务
3、自动触发
自动触发是由我们的配置文件来完成的。在redis.conf配置文件中,里面有如下配置,我们可以去设置:
-
save: 这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。
默认如下配置:
#表示900 秒内如果至少有 1 个 key 的值变化,则保存save 900 1#表示300 秒内如果至少有 10 个 key 的值变化,则保存save 300 10#表示60 秒内如果至少有 10000 个 key 的值变化,则保存save 60 10000
不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。
- stop-writes-on-bgsave-error : 默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
- rdbcompression : 默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
- rdbchecksum : 默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
- dbfilename : 设置快照的文件名,默认是 dump.rdb
- dir: 设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。
我们可以修改这些配置来实现我们想要的效果。因为第三种方式是配置的,所以我们对前两种进行一个对比:
save m n的实现原理
是将数据先存储在内存,然后当数据累计达到某些设定的伐值的时候,就会触发一次DUMP操作,将变化的数据一次性写入数据文件(RDB文件)。
RDB实际是在Redis内部一个定时器事件,每隔固定时间去检查当前数据发生的改变次数与时间是否满足配置的持久化触发的条件,如果满足则通过操作系统fork调用来创建出一个子进程,这个子进程默认会与父进程共享相同的地址空间,这时就可以通过子进程来遍历整个内存来进行存储操作,而主进程则仍然可以提供服务,当有写入时由操作系统按照内存页(page)为单位来进行copy-on-write保证父子进程之间不会互相影响。
具体实现:是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。
redis.c文件的周期性函数serverCron:
/* This is our timer interrupt, called server.hz times per second.
* Here is where we do a number of things that need to be done asynchronously.
* For instance:
*
* - Active expired keys collection (it is also performed in a lazy way on
* lookup).
* - Software watchdog.
* - Update some statistic.
* - Incremental rehashing of the DBs hash tables.
* - Triggering BGSAVE / AOF rewrite, and handling of terminated children.
* - Clients timeout of different kinds.
* - Replication reconnection.
* - Many more...
*
* Everything directly called here will be called server.hz times per second,
* so in order to throttle execution of things we want to do less frequently
* a macro is used: run_with_period(milliseconds) { .... }
*/
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
erverCron是Redis服务器的周期性操作函数,默认每隔100ms执行一次;该函数对服务器的状态进行维护,其中一项工作就是检查 save m n 配置的条件是否满足,如果满足就执行bgsave。
dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty重新置为0。
例如,如果Redis执行了set mykey helloworld,则dirty值会+1;如果执行了sadd myset v1 v2 v3,则dirty值会+3;注意dirty记录的是服务器进行了多少次修改,而不是客户端执行了多少修改数据的命令。
astsave时间戳也是Redis服务器维持的一个状态,记录的是上一次成功执行save/bgsave的时间。 =
save m n的原理如下:每隔100ms,执行serverCron函数;在serverCron函数中,遍历save m n配置的保存条件,只要有一个条件满足,就进行bgsave。对于每一个save m n条件,只有下面两条同时满足时才算满足:
(1)当前时间-lastsave > m
(2)dirty >= n
save m n 执行日志
其他自动触发机制
除了save m n 以外,还有一些其他情况会触发bgsave:
在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点
执行shutdown命令时,自动执行rdb持久化。
II、RDB文件
RDB文件是经过压缩的二进制文件,下面介绍关于RDB文件的一些细节。
存储路径: RDB文件的存储路径既可以在启动前配置,也可以通过命令动态设定。
配置: dir配置指定目录,dbfilename指定文件名。默认是Redis根目录下的dump.rdb文件。
动态设定: Redis启动后也可以动态修改RDB存储路径,在磁盘损害或空间不足时非常有用;执行命令为config set dir {newdir}和config set dbfilename {newFileName}
- REDIS:常量,保存着”REDIS”5个字符。
- db_version:RDB文件的版本号,注意不是Redis的版本号。
- SELECTDB 0 pairs:表示一个完整的数据库(0号数据库),同理SELECTDB 3 pairs表示完整的3号数据库;只有当数据库中有键值对时,RDB文件中才会有该数据库的信息(上图所示的Redis中只有0号和3号数据库有键值对);如果Redis中所有的数据库都没有键值对,则这一部分直接省略。其中:SELECTDB是一个常量,代表后面跟着的是数据库号码;0和3是数据库号码;pairs则存储了具体的键值对信息,包括key、value值,及其数据类型、内部编码、过期时间、压缩信息等等。
- EOF:常量,标志RDB文件正文内容结束。
- check_sum:前面所有内容的校验和;Redis在载入RBD文件时,会计算前面的校验和并与check_sum值比较,判断文件是否损坏。
Redis默认采用LZF算法对RDB文件进行压缩。虽然压缩耗时,但是可以大大减小RDB文件的体积,因此压缩默认开启;可以通过命令关闭:
config set rdbcompression no
需要注意的是,RDB文件的压缩并不是针对整个文件进行的,而是对数据库中的字符串进行的,且只有在字符串达到一定长度(20字节)时才会进行。
III、优势和劣势
RDB优势
- 备份策略方便: 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
- 灾难恢复方便: RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
- 性能最大化: 对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
- 启动效率高: 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。
RDB劣势
- 不能实时持久化: 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
- 影响性能: 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
- 版本兼容RDB格式问题
三、AOF持久化
I、触发机制
AOF方式实际类似mysql的基于语句的binlog方式,即每条会使Redis内存数据发生改变的命令都会追加到一个log文件中,也就是说这个log文件就是Redis的持久化数据。AOF触发也分为手动触发(bgrewriteaof)和自动触发两种。
1、bgrewriteaof触发(手动触发)
使用bgrewriteaof命令:Redis主进程fork子进程来执行AOF重写,这个子进程创建新的AOF文件来存储重写结果,防止影响旧文件。因为fork采用了写时复制机制,子进程不能访问在其被创建出来之后产生的新数据。Redis使用“AOF重写缓冲区”保存这部分新数据,最后父进程将AOF重写缓冲区的数据写入新的AOF文件中然后使用新AOF文件替换老文件。
127.0.0.1:6379> bgrewriteoaf
OK
2、自动触发
和RDB一样,配置在redis.conf文件里:
- appendonly: 是否打开AOF持久化功能,appendonly默认是 no, 改成appendonly yes。
- appendfilename: AOF文件名称
- appendfsync: 同步频率
- auto-aof-rewrite-min-size: 如果文件大小小于此值不会触发AOF,默认64MB
- auto-aof-rewrite-percentage: Redis记录最近的一次AOF操作的文件大小,如果当前AOF文件大小增长超过这个百分比则触发一次重写,默认100
启用AOF:
appendonly yes
关闭aof:
- 命令行关闭: 关闭aof的命令:config set appendfsync no
- 配置文件: 将appendonly设置为no,默认是 appendonly no )
II、AOF工作原理
是将数据也是先存在内存,但是在存储的时候会使用调用fsync来完成对本次写操作的日志记录,这个日志文件其实是一个基于Redis网络交互协议的文本文件。AOF调用fsync也不是说全部都是无阻塞的,在某些系统上可能出现fsync阻塞进程的情况,对于这种情况可以通过配置修改,但默认情况不要修改。AOF最关键的配置就是关于调用fsync追加日志文件的平率,有两种预设频率,always每次记录进来都添加,everysecond 每秒添加一次。
同步命令到 AOF 文件的整个过程可以分为三个阶段:
- 命令写入:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
- 追加AOF缓存:AOF 程序根据接收到的命令数据, 然后每隔一定的时间(比如每隔一秒)将协议内容写入AOF 缓冲区中。
- 文件写入保存:当达到 AOF同步条件,fsync 函数或者 fdatasync 函数会被调用,将 OS Cache 中的数据写入磁盘文件。
随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
III. AOF缓冲区同步文件策略
Redis 目前支持三种 AOF 保存模式,Redis的配置appendfsync 中存在三种同步方式,它们分别是:
- AOF_FSYNC_NO : 不保存。
- AOF_FSYNC_EVERYSEC : 每一秒钟保存一次、该策略为AOF的缺省策略。
- AOF_FSYNC_ALWAYS : 每执行一个命令保存一次
对应redis配置:
- appendfsync no: 写入aof文件,不等待磁盘同步。
- appendfsync always: 总是写入aof文件,并完成磁盘同步。
- appendfsync everysec: 每一秒写入aof文件,并完成磁盘同步。
从持久化角度讲,always是最安全的。从效率上讲,no是最快的。而redis默认设置进行了折中,选择了everysec。合情合理。
1、appendfsync no 不保存
命令写入aof缓冲区后,在写入系统缓冲区直接返回(系统调用write写入os cache),然后有专门线程每秒执行写入磁盘(阻塞,系统调用fsync)后返回。当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。
在这种模式下, 每次调用
flushAppendOnlyFile
函数,write(写入os cache)都会被执行, 但save(fsync)会被略过。
在这种模式下, SAVE 只会在以下任意一种情况中被执行:
- Redis 被关闭
- AOF 功能被关闭
- 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
这三种情况下的 SAVE 操作都会引起 Redis 主进程阻塞。
2、appendfsync everysec 每一秒钟保存一次
在这种模式中, SAVE原则上每隔一秒钟就会执行一次, 因为SAVE操作是由后台子线程调用的, 所以它不会引起服务器主进程阻塞。 注意, 在上一句的说明里面使用了词语“原则上”, 在实际运行中, Redis主进程在这种模式下对
fsync/fdatasync
的调用并不是每秒一次, 它和调用
flushAppendOnlyFile
函数时 Redis 所处的状态有关。
每当
flushAppendOnlyFile
函数被调用时, 可能会出现以下四种情况:
根据以上说明可以知道, 在“每一秒钟保存一次”模式下, 如果在情况 1 中发生故障停机, 那么用户最多损失小于 2 秒内所产生的所有数据。
如果在情况 2 中发生故障停机, 那么用户损失的数据是可以超过 2 秒的。
Redis 官网上所说的, AOF 在“每一秒钟保存一次”时发生故障, 只丢失 1 秒钟数据的说法, 实际上并不准确。
3、appendfsync always 每执行一个命令保存一次
在这种模式下,每次执行完一个命令之后, WRITE 和 SAVE 都会被执行。
另外,因为 SAVE 是由 Redis 主进程执行的,所以在 SAVE 执行期间,主进程会被阻塞,不能接受命令请求。
IV、AOF优势和劣势
AOF优势
- 数据安全性: 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。
- 数据一致性: 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。
AOF劣势
- 恢复速度慢: 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
- 性能低: 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。
四、RDB和AOF到底该如何选择
选择的话,两者加一起才更好。因为两个持久化机制你明白了,剩下的就是看自己的需求了,需求不同选择的也不一定,但是通常都是结合使用。有一张图可供总结: