Redis的数据持久化功能详解(3)—— AOF持久化实践

AOF持久化会记录Redis服务器收到的每个写入操作,当Redis服务器重新启动时,这些写入操作就会被重新执行,这样便能重新建立原先的数据集。默认存储在Redis安装目录的data子目录中的appendonly.aof文件中。

一、环境描述

  1. 主机配置
    CPU:单核
    内存:2 GB
    IP:10.24.16.87

  2. 操作系统
    CentOS 6.6 x86_64 Minimal

  3. Redis版本
    Redis server v=3.2.4 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=431d8e4a684c794b

  4. Redis安装方式
    按照《在CentOS上安装Redis缓存系统

二、相关命令

在Redis客户端能够运行的AOF持久化相关的命令,只有BGREWRITEAOF命令。

运行BGREWRITEAOF命令之后,Redis会启动一个只增文件(AOF)的重写进程。重写进程将会为当前的只增文件创建一个精简优化的版本。

如果BGREWRITEAOF运行失败,Redis不会丢失任何数据,因为老的AOF文件并没有受任何影响。

如果没有任何后台进程正在执行数据持久化,那么只有Redis会触发重写操作。具体而言:

  • 如果一个Redis子进程正在磁盘上创建快照(RDB持久化),那么Redis会对AOF重写操作进行调度,只有当保存数据和生成RDB文件的子进程运行结束时,才会启动AOF重写操作。在这种情况下,BGREWRITEAOF命令仍然会立即返回一个OK代码,但同时也会返回相应的提示信息。从Redis 2.6开始,你可以通过INFO命令来检查Redis是否对某个AOF重写操作进行调度。。

  • 如果Redis正在执行一次AOF重写操作,那么BGREWRITEAOF命令会返回一条错误信息,同时Redis也不会调度执行这个命令请求的AOF重写操作。

从Redis 2.4开始,Redis会自动触发AOF重写操作,但是你也可以随时使用BGREWRITEAOF命令,手动触发一次重写操作。

如果想要了解更详细的信息,你可以参考《Redis的数据持久化功能详解(1)—— 两种持久化方式》。

BGREWRITEAOF命令会立即返回一个简单字符串,也就是OK代码。

三、相关配置选项

1. AOF功能开关

在默认情况下,Redis会异步地将内存中的数据持久化在磁盘上。这种模式足以应对很多应用场景。但是,当Redis的进程意外停止运行,或者Redis服务器突然断电时,很有可能导致Redis丢失几分钟之内写入的数据(取决于已配置的保存点)。

只增文件是另一种持久化模式,它可以提供更好的耐用性。例如,如果使用默认的fsync()函数调用策略,那么当发生某些突发事件(例如,服务器断电或者Redis进程运行出错)时,Redis只会丢失一秒之内写入的数据。

Redis可以同时启用AOF持久化和RDB持久化,而不会发生任何问题。

如果启用Redis持久化,那么Redis会在启动时加载AOF文件,因为这个文件能够为Redis提供更好的耐用性。

如果想要了解更详细的信息,你可以参考《Redis的数据持久化功能详解(1)—— 两种持久化方式》。

这个配置项的可能取值有yesno,默认值如下所示:

  1. appendonly no

2. AOF文件名

这个配置项可以指定只增文件的名称,默认值如下所示:

  1. appendfilename "appendonly.aof"

3. fsync调用策略

实际上,fsync()系统调用会告诉操作系统将数据写入磁盘,而不是在输出缓冲中等待更多的数据。有些操作系统会立即将数据写入磁盘,而另一些操作系统只是尝试尽快将数据写入磁盘。

Redis支持三种不同的调用策略:

  • no:不调用fsync()函数,由操作系统负责将缓冲数据写入磁盘。这种策略速度较快。
  • always:每次写入只增日志文件之后,都会调用fsync()函数。这种策略速度很慢,但是最安全。
  • everysec:每秒只调用一次fsync()函数。这种策略在速度和安全性之间作出妥协。

这个配置项的默认值为everysec,通常,它会在速度和数据安全性之间作出正确的妥协。这个配置项的取值完全取决于应用场景的需求:如果需要更好的性能,那么可以将其设置为no,这样便由操作系统负责将缓冲数据写入至磁盘(但是,这种调用策略和默认的RDB持久化一样,在遭受灾害时都会丢失一段时间之内写入的数据);或者相反,如果需要更好的数据安全性,那么可以将其设置为always,但是速度会被拖慢。

如果不能确定应用场景的需求,那么就使用everysec

这个配置项的默认值如下所示:

  1. appendfsync everysec

4. 重写期间的fsync策略

如果将AOF持久化的fsync()调用策略设置为alwayseverysec,那么当某个后台进程(在后台保存RDB文件或重写AOF日志)正在进行数据持久化时,它会对磁盘执行大量的I/O操作,在某些Linux配置方案中,fsync()系统调用有可能会导致Redis阻塞很长时间。注意,目前还没有针对这个问题的解决方法,因为即使在另一个不同的线程中执行fsync()系统调用,也会阻塞同步的write(2)系统调用。

为了减轻这个问题的影响,Redis可以使用no-appendfsync-on-rewrite配置项。当后台正在执行BGSAVEBGREWRITEAOF命令时,这个配置项能够防止在主进程中调用fsync()函数。

这就意味着,当另一个子进程正在进行数据持久化时,Redis的耐用性和appendfsync none的一样。实际上,这还意味着,在最糟糕的情况下,Redis可能会丢失最多30秒之内写入的数据。

如果你碰到Redis服务延迟的现象,那么请将这个配置项设为yes。否则,请将这个配置项保持为no,从耐用性的角度来看,这是最安全的选择。

这个配置项的默认值如下所示:

  1. no-appendfsync-on-rewrite no

5. 重写触发条件

这个配置项可以控制只增文件的自动重写操作。

当AOF日志的增量达到指定的百分比时,Redis会暗中调用BGREWRITEAOF命令,这样便能自动重写AOF日志文件。

在最近一次重写操作结束之后,Redis会记住AOF文件的大小(从Redis重新启动至今,如果一直没有发生过任何重写操作,那么便会使用Redis启动时的AOF文件的大小)。

Redis会将这个基准文件大小和当前文件大小进行对比。如果当前文件尺寸大于指定的百分比,那么便会触发AOF重写操作。你还需要为等待重写的AOF文件指定一个最小文件尺寸。当AOF文件大小的增量达到指定的百分比,但是文件大小还没有达到指定的最小尺寸时,Redis就不会重写AOF文件。

如果将百分比设置为0,那么便会禁用自动重写AOF文件的功能。

这个配置项的默认值如下所示:

  1. auto-aof-rewrite-percentage 100
  2. auto-aof-rewrite-min-size 64mb

6. 被截断AOF文件的加载开关

在Redis的启动过程中,当AOF文件的数据被加载至内存时,Redis可能会发现这个AOF文件的末尾已经被截断了。

当运行Redis的系统突然崩溃或宕机时,尤其当系统挂载ext4文件系统,并且没有设置data=ordered选项时,这种问题就很有可能发生(但是,当Redis自身意外崩溃或异常中止,但操作系统仍然工作正常时,这种问题就不会发生)。

如果Redis发现AOF文件在末尾被截断了,那么Redis有两种选择:既可以退出运行,并且返回错误信息;又可以加载尽可能多的数据(默认行为),然后正常启动。aof-load-truncated配置项可以控制这种行为。

如果将aof-load-truncated设置为yes,当Redis加载一个被截断的AOF文件时,Redis会发送一条日志,将这个事件告知用户。

如果将这个配置项设置为no,那么Redis会中止运行,并且会返回一条错误信息,拒绝再次启动。当这个配置项被设置为no时,在重启Redis服务器之前,用户需要使用redis-check-aof工具修复这个被截断的AOF文件。

注意,如果Redis在启动时发现AOF文件在中间发生损坏,那么Redis仍然会退出运行,并且返回一条错误信息。只有当Redis尝试从AOF文件中读取尽可能多的数据时,才会用到这个配置项,但是它不能保证读取数据的完整性。

这个配置项的默认值如下所示:

  1. aof-load-truncated yes

四、AOF持久化实践

下面将通过实际操作演示AOF持久化的使用方法,本文不会修改redis.conf配置文件,而是使用redis-cli config set命令,临时修改Redis的配置,重新启动主机或服务之后,Redis的配置就会还原。

1. 启用AOF持久化

Redis默认关闭AOF持久化,在Shell中运行以下命令:

  1. redis-cli config set appendonly yes

此时,Redis便启用AOF持久化了。

2. 指定AOF文件名

AOF文件的默认存放目录为/usr/local/Redis/data,默认名称为appendonly.aof。在Shell中运行以下命令:

  1. redis-cli config set dir /root/Downloads
  2. redis-cli config set appendfilename redislog.aof

此时,AOF文件的存放目录变为/root/Downloads,文件名变为redislog.aof

注意
在Redis的源码中,config getconfig set命令并没有为appendfilename配置项提供支持,编译安装之后,也就无法在Redis运行时获取或修改AOF文件的名称。请参考《如何使Redis能够在运行时获取和修改AOF文件名》,修改源码后再编译安装,即可在Redis运行时获取和修改AOF文件名了。

3. 确认fsync策略

在Shell中运行以下命令,确认fsync调用策略。appendfsync配置项应当为everysecno-appendfsync-on-rewrite配置项应当为no,如下图所示:

检查fsync策略

上述配置表明,Redis会每秒调用一次fsync(),并且在重写AOF文件时,仍然会遵循这个策略。

4. 指定重写触发条件

在Shell中运行以下命令:

  1. redis-cli config set auto-aof-rewrite-percentage 70
  2. redis-cli config set auto-aof-rewrite-min-size 134217728

此时,Redis会在AOF文件大小超过128 MB,并且文件大小增长率大于70%时,自动执行AOF重写操作。

5. 导入测试数据

使用《Redis的数据持久化功能详解(2)—— RDB持久化实践》中的Java程序,生成500W条测试数据,然后通过管道导入Redis服务器。

在Shell中运行以下命令,查看测试数据占用的内存大小,如下图所示:

测试数据大小

从上图中可以看出,共导入了500W条测试数据,占用522.55MB的Redis内存空间。

6. 手动执行AOF相关命令

在Shell中运行以下命令,手动触发重写AOF文件的操作:

  1. redis-cli bgrewriteaof

运行上述命令之后,Redis会返回一个提示信息,然后在后台启动一个专门负责重写AOF文件的子进程,如下图所示:

调用BGREWRITEAOF命令

由上图可知,负责重写AOF文件的子进程的名称为redis-aof-rewrite,重写完成之后,这个进程便会结束运行。

查看生成的AOF文件的大小,会发现AOF日志确实比RDB快照要大一些,如下图所示:

AOF日志和RDB快照的大小

查看AOF文件的数据格式,会发现文件中的每条日志都遵循Redis的通信协议:

AOF日志格式

7. 由RDB快照生成AOF日志

首先,重启Redis服务,恢复Redis的默认配置:

  1. service redis_6379 restart

此时,Redis应当不会包含任何测试数据,如下图所示:

检查Redis数据条数

停止Redis服务,然后将先前生成的RDB文件拷贝至Redis默认的数据目录:

  1. service redis_6379 stop
  2. cp -a /root/Downloads/dump.rdb /usr/local/Redis/data/

再次启动Redis服务,此时会自动将dump.rdb文件的数据恢复至Redis的内存。运行PING命令,如果返回值为PONG,那么就表示数据恢复已经完成,如下图所示:

恢复Redis数据库

启用AOF持久化,然后关闭RDB持久化:

  1. redis-cli config set appendonly yes
  2. redis-cli config set save ""

此时,会自动触发AOF重写操作,重写完成之后,便会在Redis默认的数据目录中生成AOF文件,如下图所示:

从RDB快照得到AOF日志