redis 系列17 持久化 AOF
一.概述
除了上篇介绍的rdb持久化功能之外,redis还提供了aof(append only file)持久化功能。与rdb保存数据库中的键值对来记录数据库状态不同,aof是通过保存redis服务器所执行的写命令来记录数据库状态的。aof持久化方式记录每次对服务器写的操作,当服务器启动时,就会通过载入和执行aof文件中保存的命令来还原服务器关闭之前的数据库状态,并在服务器载入aof文件并还原数据库状态时打印日志。
被写入aof文件的所有命令都是纯文本格式,可以直接打开一个aof文件来观察。所有写命令是以追加(append)形式,保存到文件末尾。redis还能对aof文件进行后台重写,使得aof文件的体积不至于过大。
1.1 aof持久化的实现
aof持久化功能的实现可以分为命令追加(append),文件写入,文件同步(sync)三个步骤。
(1) 命令追加
当aof执行处于打开状态时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾。
struct redisserver { //.. //aof 缓冲区 ads aof_buf //.. }
(2) aof文件写入与同步
redis的服务器进程就是一个事件循环(loop),这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复。在服务器每次结束一个事件循环之前,都会调用内部flushappendonlyfile函数,考虑是否需要将aof_buf缓冲区中的内容写入和保存到aof文件里面。flushappendonlyfile函数的行为由服务器配置appendfsync选项的值来决定。appendfsync是指数据同步到磁盘文件(aof)的方式,默认配置是everysec选项。当同步频率是everysec值时,并且距离上次同步aof文件已经超过一秒时,那么服务器会先将aof_buf中的内容写入到aof文件中。
127.0.0.1:6379>config get appendfsync 1) "appendfsync" 2) "everysec"
appendfsync模式 |
对应flushappendonlyfile函数行为 |
no |
当设置appendfsync为no的时候,redis不会主动调用fsync去将aof日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。从效率上讲该模式最快, 但同步到磁盘不及时,是最不安全的选择。 |
everysec (推荐) |
当设置appendfsync为everysec的时候,redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘,从效率上讲该模式足够快(和使用 rdb 持久化差不多),并且当出现故障停机时,数据库也只丢失一秒钟的命令。 |
always |
当设置appendfsync为always时,每一次写操作都会调用一次fsync,这时数据是最安全的,当然,由于每次都会执行fsync,所以其性能也会受到影响,效率上讲该模式最慢的。 |
1.2 aof文件载入与数据还原
因为aof文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍aof文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态。redis读取aof文件并还原数据库状态的详细步骤如下:
(1) 创建一个伪客户端(因为redis的命令只能在客户端上下文中执行),用于载入aof文件时所使用的命令直接来源于aof文件,而不是来自网络连接的命令。
(2) 从aof文件中分析并读出一条写命令。
(3) 使用伪客户端执行被读出的写命令。
(4) 重复执行步骤2和3,直到aof文件中的所有命令都被处理完为止。
比如:服务器首先读入并执行select 0 命令,之后是set msg hello命令,再之后是sadd fruits apple banana cherry命令等等,这些命令都执行完之后,服务器的数据库就被还原到之前的状态了。
1.3 aof重写
因为aof持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器运行时间的流逝,aof文件中的内容会越来越多,文件 的体积也会越来越大,随着aof文件的体积越大,数据还原所需的时间就越多。为了解决aof文件体积膨胀的问题,redis提供了aof文件重写功能。通过该功能,redis服务器可以创建一个新的aof文件来替代现有的aof文件,新旧两个aof文件所保存的数据库状态相同,但新的aof文件不会包含任何浪费空间的冗余命令,所以新aof文件体积比旧的aof文件体积要小得多。
aof文件重写并不需要对现有的aof文件进行任何读取,分析或者写入操作,这个重写功能是通过读取服务器当前的数据库状态来实现的。比如:使用写命令 rpush list "a", rpush list "b", rpush list "c", rpush list "d" 此时必须在aof文件中写入四条命令。重写可以是直接从数据库中读取键list的值,然后用一条rpush list "a","b","c","d"命令来代替。
1.4 aof 后台重写
重写作为一种辅助维护,redis不希望aof重写造成服务器无法处理请求,所以redis决定将aof重写程序放到子进程里执行。对aof 文件进行重写,执行bgrewriteaof命令, redis将生成一个新的 aof 文件,这个文件包含重写当前数据集所需的最少命令。bgrewriteaof后台重写实现步骤如下:
(1) redis执行 fork() ,现在同时拥有父进程和子进程。
(2)子进程开始将新 aof 文件的内容写入到临时文件。
(3)对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 aof 文件的末尾,这样样即使在重写的中途发生停机,现有的 aof 文件也还是安全的。
(4)当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 aof 文件的末尾。
(5)现在redis原子地用新文件替换旧文件,之后所有命令都会直接追加到新 aof 文件的末尾。
1.5 aof优点
(1) 可以使用不同的fsync(同步)策略:无fsync、每秒fsync、每次写的时候fsync。使用默认的每秒fsync策略,redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据。
(2) aof文件是一个只进行追加的日志文件,所以不需要写入seek,某些原因(如:宕机)未执行完整的写入命令,你也可使用redis-check-aof工具修复这些问题。
(3) aof 文件体积变得过大时,自动地在后台对 aof 进行重写.整个重写操作是绝对安全,即使重写过程中发生停机,现有的 aof 文件也不会丢失。一旦新 aof 文件创建完毕,redis就会从旧 aof 文件切换到新 aof 文件,并开始对新 aof 文件进行追加操作。
(4) aof 文件有序地保存了对数据库执行的所有写入操作,这些写入操作以redis协议的格式保存,因此 aof 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。导出(export) aof 文件也非常简单。举个例子:如果你不小心执行了 flushall 命令,但只要 aof 文件未被重写,那么只要停止服务器,移除 aof 文件末尾的 flushall 命令,并重启redis,就可以将数据集恢复到 flushall 执行之前的状态。
1.5 aof缺点
(1) 对于相同的数据集来说,aof 文件的体积通常要大于 rdb 文件的体积。
(2) 根据所使用的fsync(同步)策略,aof 的速度可能会慢于 rdb 。在一般情况下,每秒fsync的性能依然非常高,而关闭fsync可以让 aof 的速度和 rdb 一样快,即使在高负荷之下也是如此。不过在处理巨大的写入载入时,rdb 可以提供更有保证的最大延迟时间(latency)。
1.6 如何选择使用哪种持久化方式
一般来说,如果想达到足以媲美postgresql的数据安全性,应该同时使用两种持久化功能。如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 rdb 持久化。
有很多用户都只使用 aof 持久化,但我们并不推荐这种方式:因为定时生成 rdb 快照(snapshot)非常便于进行数据库备份,并且 rdb 恢复数据集的速度也要比 aof 恢复的速度要快,除此之外,使用 rdb 还可以避免之前提到的 aof 程序的 bug 。注意: 因为以上提到的种种原因,未来可能会将 aof 和 rdb 整合成单个持久化模型。
二. aof持久化配置
redis默认是关闭aof机制,需要在配置文件中打开aof, 注意通过 config set 设置的配置重启redis服务后就会失效,如果要永久有效,需在redis.conf中打开aof功能。脚本如下:
127.0.0.1:6379>config set appendonly yes ok
打开后每当redis执行一个改变数据集的命令时(如:set),这个命令就会被追加到aof文件的末尾,这样当redis重新启动时,程序就可以通过重新执行aof文件中的命令来达到重建数据集的目的。
2.1 aof配置相关选项
选项 |
取值 |
说明 |
appendonly |
no|yes |
是否开启aof机制 |
appendfilename |
"appendonly.aof" |
aof文件名 |
appendfsync |
no|appendfsync|always |
aof持久化同步频率 |
no-appendfsync-on-rewrite |
no | yes |
在日志进行bgrewriteaof时,如果设置为yes表示新写操作不进行同步fsync,只是暂存在缓冲区里,避免造成磁盘io操作冲突,等重写完成后在写入。redis中默认为no
|
auto-aof-rewrite-percentage |
100 |
当前aof文件大小是上次日志重写时的aof文件大小两倍时,发生bgrewriteaof操作。 |
auto-aof-rewrite-min-size |
64mb |
当前aof文件执行bgrewriteaof命令的最小值,避免刚开始启动reids时由于文件尺寸较小导致频繁的bgrewriteaof。 |
aof-load-truncated |
yes |
redis再恢复时,忽略最后一条可能存在问题的指令 |
aof-use-rdb-preamble |
no |
新增rdb-aof混合持久化格式,在开启了这个功能之后,aof重写产生的文件将同时包含rdb格式的内容和aof格式的内容 |
2.2 演示
下面测试aof持久化,aof文件是可识别的纯文本,文件的内容就是一个个的redis标准命令,下面使用两个set命令:
127.0.0.1:6379> set name1 "zs" ok 127.0.0.1:6379> set name2 "ls" ok
下面查看aof文件, 可以发现里面是一个个命令, 上面执行的写命令对应的文件内容如下:
[hsr@xuegod64 redis]$ cat appendonly.aof name1 $2 zs *3 $3 set $5 name2 $2 ls
上一篇: java基础Synchronized关键字之对象锁
下一篇: 使用JSTL的taglib做if判断