所谓持久化可以简单理解为将内存中的数据保存到硬盘上存储的过程。持久化之后的数据在系统重启或者宕机之后依然可以进行访问,保证了数据的安全性。

​Redis​​​有两种持久化方案,一种是快照方式(​​SNAPSHOTTING​​​),简称​​RDB​​​;一种是只追加模式(​​APPEND ONLY MODE​​),称为AOF。接下来让我们分别了解一下它们的使用与注意事项。


RDB

​RDB​​​为​​Redis DataBase​​​的缩写,是 ​​Redis​​​ 默认的持久化方案。它能够在指定的时间间隔内将内存数据集快照(​​snapshot​​​)写入磁盘,恢复时将快照文件( ​​dump.rdb​​ )读回内存。

面试官:你说你精通Redis,你看过持久化的配置吗?_数据库

我们先来扒一下配置文件中的​​SNAPSHOTTING​​:

配置文件

save  ​​<seconds> <changes>​

在给定的​秒数​内,如果对数据库执行的​写入操作数​达到设定的值,则将数据同步到数据文件。支持多个条件配合,​​Redis​​默认配置文件中提供了三个条件:

save 900 1 //900s内有1个更改
save 300 10 //300s内有10个更改
save 60 10000 //60s内有10000次更改

注意​:若不想用​​RDB​​​方案,可以把 ​​save ""​​的注释打开,上边三个注释掉。

stop-writes-on-bgsave-error yes

当​​bgsave​​​出现错误时,​​Redis​​是否停止执行写命令;


  • 如果为​​yes​​,则当硬盘出现问题时,​​Redis​​将停止接受写入操作,这样我们可以及时发现,避免数据的大量丢失;
  • 如果为​​no​​,则​​Redis​​无视​​bgsave​​的错误继续执行写命令。


如果已经设置了对​​Redis​​​服务器的正确监视和持久性,即采用了其他手段发现和控制数据完整性,可能希望禁用此功能,以便即使在磁盘、权限等方面出现问题时,​​Redis​​仍能正常工作。


注意​:如果后台保存过程将再次开始工作,​​Redis​​将自动允许再次写入。

rdbcompression yes

指定存储到本地数据库时是否​压缩​(​​Redis​​​采用​​LZF​​​压缩)数据,默认为​​yes​​​。如果为了节省​​CPU​​时间,可以关闭该选项,但会导致数据库文件变得巨大。

rdbchecksum yes

从​​RDB​​​版本​​5​​​开始,在存储快照后,还可以使用​​CRC64​​​算法来进行数据校验,​​CRC64​​​校验放在文件的末尾。开启之后,保存和加载​​RDB​​​文件时会增加大约​​10%​​的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

禁用​校验和​创建的​​RDB​​文件的校验和为零,这将告诉加载代码跳过检查。

dbfilename dump.rdb

指定本地数据库文件名,重启之后自动加载进​内存​,手动执行​​save​​ 命令的话即刻生效。


大坑请注意​:​​flushall​​​、​​shutdown​​​命令都会清空并提交至​​dump.rdb​


dir ./

指定本地数据库存放目录。

理论

工作方式


  • 当 ​​Redis​​ 需要保存​​dump.rdb​​文件时,它会调用系统函数​​fork()​​,创建一个子进程(与主进程完全一致);
  • 子进程将数据集写入临时文件​​RDB​​中;
  • 当子进程完成对新 ​​RDB​​ 文件的写入时,​​Redis​​ 用新 ​​RDB​​ 文件替换原来的 ​​RDB​​ 文件,并删除旧的 ​​RDB​​ 文件。

这种工作方式使得 ​​Redis​​​ 可以从写时复制(​​copy-on-write​​)机制中获益。

如何触发RDB快照


  1. 配置文件中默认的快照配置;
  2. 命令​​save​​(阻塞, 只管保存快照,其他的等待)或者是​​bgsave​​(异步)命令,快照同时还可以响应客户端命令;
  3. 执行​​flushall​​ 命令,清空数据库所有数据,意义不大;
  4. 执行​​shutdown​​ 命令,保证服务器正常关闭且不丢失任何数据,意义也不大。

通过RDB文件恢复数据

在实际开发中,一般会考虑到物理机硬盘损坏的情况,所以我们会选择备份​​dump.rdb​​​文件。将备份的​​dump.rdb​​​ 文件拷贝到​​redis​​​的安装目录的​​bin​​​目录下,重启​​redis​​服务即可。

优点


  • ​RDB​​是一个非常紧凑的文件,非常适用于数据集的备份;
  • ​RDB​​是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复;
  • ​Redis​​的主进程不进行​​I/O​​操作,确保了极高的性能;
  • 适合大规模数据的恢复,对于数据的完整性和一致性要求不高的话,​​RDB​​比​​AOF​​方式更加高效。

缺点


  • 在​​Redis​​意外宕机时,你可能会丢失几分钟的数据;
  • ​RDB​​ 需要经常​​fork​​子进程来保存数据集到硬盘上,当数据集比较大的时候,​​fork​​的过程是非常耗时的,可能会导致​​Redis​​在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且​​CPU​​性能不是很好的情况下,这种情况会持续1秒;​​AOF​​也需要​​fork​​,但是可以调节重写日志文件的频率来提高数据集的耐久度。


AOF

为了解决​​RDB​​​方式在宕机时丢失数据过多的问题,从​​1.1​​​ 版本开始,​​Redis​​​增加了一种​​durable​​​的持久化方式:​​AOF​​。

​AOF​​​是​​Append Only File​​​的缩写,默认不开启。​​AOF​​以日志的形式来记录每个写操作,只允许追加文件但不可以改写文件,当服务器重启的时候会重新执行这些命令来恢复原始的数据。

我们再来看一下配置文件中的​​APPEND ONLY MODE​​:

配置文件

appendonly no

默认为关闭状态,改为​​yes​​​打开持久化。​​AOF​​​和​​RDB​​可以同时启用而不会出现问题。

appendfilename "appendonly.aof"

文件默认名称,启动即创建。加载​先于​dump.rdb​​文件

appendfsync

同步策略:系统函数​​fsync()​​​ 告诉操作系统在磁盘上实际写入数据。​​Redis​​支持三种不同的模式

appendfsync always //每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好
appendfsync everysec //默认推荐,异步操作,每秒记录,如果宕机,有1秒内数据丢失
appendfsync no //不同步,只有在操作系统需要时在刷新数据

要想了解接下来的配置内容,先得说一下“日志重写”的原理:

重写

由于​​AOF​​​采用的是将命令追加到文件末尾的方式,所以随着写入命令的不断增加,​​AOF​​​文件的体积会变得越来越大。为避免出现此种情况,新增了重写机制:可以在不打断服务客户端的情况下,对​​AOF​​​文件进行重建(​​rebuild​​)。

重写触发:​ 通过执行​​bgrewriteaof​​​命令,可以生成一个新的​​AOF​​文件,该文件包含重建当前数据集所需的​最少​命令。​​Redis 2.2​​​需手动执行该命令,​​Redis 2.4​​则可以通过修改配置文件的方式自动触发(配置在下边涉及)。

重写原理:


  • ​Redis​​ 执行系统函数​​fork()​​ ,创建一个子进程(与主进程完全一致);
  • 子进程开始将新 ​​AOF​​ 文件的内容写入到临时文件;
  • 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 ​​AOF​​ 文件的末尾,这样即使在重写的中途发生停机,现有的 ​​AOF​​ 文件也是安全的;
  • 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 ​​AOF​​ 文件的末尾。
  • ​Redis​​ 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 ​​AOF​​文件的末尾。

no-appendfsync-on-rewrite no

当我们同时执行主进程的​写操作​和子进程的​重写​操作时,两者都会操作磁盘,而重写往往会涉及到大量的磁盘操作,这样就会造成主进程在写​​aof​​文件的时候出现阻塞的情形。

为了解决这个问题,​​no-appendfsync-on-rewrite​​参数出场了。


  • 如果该参数设置为​​no​​,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题;
  • 如果设置为​​yes​​,这就相当于将​​appendfsync​​设置为​​no​​,这说明并没有执行磁盘操作,只是写入了缓冲区。因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候​​redis​​挂掉,就会丢失数据。丢失多少数据呢?在​​linux​​的操作系统的默认设置下,最多会丢失30s的数据。

因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为​​yes​​​;如果应用系统无法忍受数据丢失,则设置为​​no​​。

auto-aof-rewrite-percentage 100

重写百分比,默认为上次重写后​​aof​​文件大小的一倍。

auto-aof-rewrite-min-size 64mb

重写触发的最小值:64mb。

根据​​auto-aof-rewrite-min-size​​​和​​auto-aof-rewrite-percentage​​​参数确定自动触发时机。​​Redis​​​会记录上次重写时的​​AOF​​​大小,默认配置是当​​AOF​​​文件大小是上次​​rewrite​​​后大小的一倍且文件大于​​64M​​时触发。


大型互联网公司一般都是​​3G​​起步


aof-load-truncated yes

当​​AOF​​​文件被截断时,即​​AOF​​​文件的最后命令不完整,如果此时启动​​Redis​​​,会将​​AOF​​数据加载回内存,此时便会出现问题。


  • yes:加载一个截断的​​AOF​​,​​Redis​​服务器开始发出日志,通知用户该事件;
  • no:服务器将中止并出现错误,拒绝启动。

当我们得知​​AOF​​​文件报错时,可以用以下方法来修复出错的 ​​AOF​​ 文件:


  • 为现有的 ​​AOF​​文件创建一个备份;
  • 使用 ​​Redis​​ 附带的 ​​redis-check-aof​​ 命令,对原来的​​AOF​​文件进行修复;

  • ​redis-check-aof –fix​
  • ​redis-check-aof --fix appendonly.aof​​  修复命令,杀光不符合规范的语法

  • (​可选​)使用 ​​diff -u​​ 对比修复后的 ​​AOF​​文件和原始 ​​AOF​​ 文件的备份,查看两个文件之间的不同之处;
  • 重启 ​​Redis​​服务器,等待服务器载入修复后的 ​​AOF​​文件,并进行数据恢复。

aof-use-rdb-preamble yes

在重写​​AOF​​​文件时,​​Redis​​​能够在​​AOF​​​文件中使用​​RDB​​​前导,以加快重写和恢复速度。启用此选项后,重写的​​AOF​​​文件由两个不同的节组成:​​RDB file​​​、​​AOF tail​

加载​​Redis​​​时,会识别​​AOF​​文件以​Redis​字符串开头,并加载带前缀的​​RDB​​​文件,然后继续加载​​AOF​​尾部。

理论

优点


  • 数据的完整性和一致性更高,​​AOF​​的持久化通过使用不同的策略,最多丢失1秒的数据;
  • ​AOF​​文件是一个只进行追加的日志文件,不需要写入​​seek​​;
  • ​Redis​​可以在 ​​AOF​​文件体积变得过大时,自动地在后台对 ​​AOF​​ 进行重写,重写操作是绝对安全的;
  • ​AOF​​文件记录的写入操作以​​Redis​​协议的格式保存,容易读懂,容易对文件进行分析;

缺点


  • 对于相同的数据集来说,​​AOF​​文件的体积通常要大于​​RDB​​文件的体积;
  • 根据所使用的 ​​fsync​​ 策略,​​AOF​​的速度可能会慢于​​RDB​​ 。


在一般情况下,每秒 ​​fsync​​​ 的性能依然非常高,而关闭 ​​fsync​​​ 可以让 ​​AOF​​​ 的速度和 ​​RDB​​​ 一样快, 即使在高负荷之下也是如此。不过在处理巨大的写入载入时,​​RDB​​​ 可以提供更有保证的最大延迟时间(​​latency​​)。



对比与总结

如何选择使用哪种持久化方式?

一般来说,如果想达到足以媲美 ​​PostgreSQL​​ 的数据安全性,应该同时使用两种持久化功能。

如果非常关心数据,但仍然可以承受数分钟以内的数据丢失,那么可以只使用 ​​RDB​​ 持久化。

由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此​​AOF​​是目前​主流​的持久化方式。

有很多用户都只使用​​AOF​​​持久化,但我们并不推荐这种方式:因为定时生成 ​​RDB​​​ 快照(​​snapshot​​​)非常便于进行数据库备份,并且 ​​RDB​​​ 恢复数据集的速度也要比 ​​AOF​​ 恢复的速度要快。

AOF和RDB之间的相互作用

在版本号大于等于 ​​2.4​​​ 的 ​​Redis​​​ 中,​​BGSAVE​​​ 执行的过程中,不可以执行 ​​BGREWRITEAOF​​​ 。反过来说,在 ​​BGREWRITEAOF​​​ 执行的过程中,也不可以执行 ​​BGSAVE​​​。这可以防止两个 ​​Redis​​​ 后台进程同时对磁盘进行大量的​​I/O​​ 操作。

如果 ​​BGSAVE​​​ 正在执行,并且用户显示地调用 ​​BGREWRITEAOF​​​ 命令,那么服务器将向用户回复一个 ​​OK​​​ 状态, 并告知用户​​BGREWRITEAOF​​​ 已经被预定执行:一旦 ​​BGSAVE​​​ 执行完毕,​​BGREWRITEAOF​​就会正式开始。

当 ​​Redis​​​ 启动时,如果 ​​RDB​​​持久化和 ​​AOF​​​ 持久化都被打开了, 那么程序会优先使用 ​​AOF​​​ 文件来恢复数据集,因为 ​​AOF​​文件所保存的数据通常是最完整的。

备份redis数据


  • 创建一个定期任务(​​cron job​​),每小时将一个 ​​RDB​​ 文件备份到一个文件夹,并且每天将一个 ​​RDB​​ 文件备份到另一个文件夹;
  • 确保快照的备份都带有相应的日期和时间信息,每次执行定期任务脚本时,使用 ​​find​​ 命令来删除过期的快照;
  • 至少每天一次,将 ​​RDB​​ 备份到你的数据中心之外,或者至少是备份到你运行 ​​Redis​​ 服务器的物理机器之外。

性能建议

在实际应用时,因为​​RDB​​​文件只用作后备用途,建议只在​​slave​​​上持久化​​RDB​​​文件,而且只需要15分钟备份一次就够了,只保留​​save 900 1​​这条规则。

如果开启​​AOF​​​,好处是在最恶劣情况下也只会丢失不超过2秒数据,启动脚本较简单只​​load​​​自己的​​AOF​​​文件就可以了。代价一是带来了持续的​​IO​​​,二是​​AOF rewrite​​​的最后将​​rewrite​​过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。

只要硬盘许可,应该尽量减少​​AOF rewrite​​​的频率,​​AOF​​​重写的基础大小默认值​​64M​​​太小了,可以设置到​​5G​​以上。默认超过原大小的100%时重写可以改到适当的数值。

如果不开启​​AOF​​​,仅靠​​Master-Slave Replication​​​实现高可用性也可以。能省掉一大笔​​IO​​​,也减少了​​rewrite​​​时带来的系统波动。代价是如果​​Master/Slave​​​同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个​​Master/Slave​​​中的​​RDB​​文件,载入较新的那个。

有道无术,术可成;有术无道,止于术

欢迎大家关注​Java之道​公众号

面试官:你说你精通Redis,你看过持久化的配置吗?_mysql_02