1.RDB & AOF 简介
Redis 提供了两种持久化策略
-
RDB 持久化机制,会在一段时间内生成指定时间点的数据集快照(snapshot)
-
AOF 持久化机制,记录 server 端收到的每一条写命令,当 server 重启时会进行重放以此来重建之前的数据集。AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加(append)到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite) ,使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
-
如果你仅使用 Redis 作为缓存加速访问,你可以关闭这两个持久化设置
-
你也可以同时开启这两个持久化设置,但是在这种情况下,Redis 重启时会使用 AOF 文件来重建数据集,因为 AOF 文件保存的数据往往更加完整
2. 详解 RDB
2.1 RDB 创建与载入
Redis 提供了 SAVE 和 BGSAVE 两个命令来生成 RDB 文件,区别是前者是阻塞的,后者是后台 fork 子进程进行不会阻塞主进程处理命令请求。载入 RDB 文件不需要手工运行,而是 server 端自动进行,只要启动时检测到 RDB 文件存在 server 端便会载入 RDB 文件重建数据集。当然上面简介中已经提到,如果 同时存在 AOF 的话会优先使用 AOF 重建数据集因为其保存的数据更完整。
2.2 RDB 相关配置
- SAVE POINT save <seconds> <changes>
你可以配置保存点(save point),Redis 如果每 N 秒数据发生了 M 次改变就保存快照文件。
- stop-writes-on-bgsave-error yes | no
如果 Redis 执行 RDB 持久化失败(常见于操作系统内存不足),那么 Redis 将不再接受 client 写入数据的请求。当然在实践中,我们通常会将 stop-writes-on-bgsave-error 设置为 false,同时让监控系统在 Redis 执行 RDB 持久化失败时发送告警,以便介入解决,而不是粗暴地拒绝 client 的写入请求。
- rdbcompression yes | no
当生成 RDB 文件时,Redis 会判断字符串长度 >=20字节则压缩,否则不压缩存储,默认 Redis 会采用 LZF 算法进行数据压缩。
- rdbchecksum yes | no
从版本5的 RDB 的开始,一个 CRC64 的校验码会放在文件的末尾。这样更能保证文件的完整性,但是在保存或者加载文件时会损失一定的性能(大概10%)。如果想追求更高的性能,可以把它禁用掉,这样文件在写入校验码时会用 0 替代,加载的时候看到 0 就会直接跳过校验。
2.3 RDB 的优点
-
RDB文件是一个很简洁的单文件,它保存了某个时间点的 Redis 数据集,很适合用于做备份。你可以设定一个时间点对 RDB 文件进行归档,这样就能在需要的时候很轻易的把数据恢复到不同的版本。
-
基于上面所描述的特性,RDB 文件很适合用于灾备,因为单文件可以很方便地传输到另外的数据中心。
-
RDB的性能很好,需要进行持久化时,主进程会 fork 一个子进程出来,然后把持久化的工作交给子进程,自己不会有相关的 I/O 操作。
-
比起 AOF,在数据量比较大的情况下,RDB的启动速度更快。
2.4 RDB 的缺点
-
RD B容易造成数据的丢失,当你希望在 Redis 停止工作时尽量减少数据丢失的话,那 RDB 不适用。假设每5分钟保存一次快照,如果Redis因为某些原因不能正常工作,那么从上次产生快照到 Redis 出现问题这段时间的数据就会丢失了。你可以通过配置不同的 save point 来减轻数据丢失的程度,但是越紧凑的 save point 会越频繁地触发 RDB 生成操作,从而对 Redis 性能产生影响
-
RDB 使用 fork 子进程进行数据的持久化,如果数据比较大的话可能就会花费点时间,造成 Redis 停止服务几毫秒,如果数据量很大且 CPU 性能不是很好的时候,停止服务的时间甚至会到一秒。AOF 也需要 fork 但是你可以自己调整 rewrite 的频率,它不会造成数据丢失。在 Linux 系统中,fork 会拷贝进程的 page table。随着进程占用的内存越大,进程的 page table 也会越大,那么 fork 也会占用更多的时间。 如果 Redis 占用的内存很大 (例如 20 GB),那么在 fork 子进程时,会出现明显的停顿现象(无法处理 client 的请求)。
-
Linux fork 子进程采用的是 copy-on-write 的方式。在 Redis 执行 RDB 持久化期间,如果 client 写入数据很频繁,那么将增加 Redis 占用的内存,最坏情况下,内存的占用将达到原先的两倍。
3. 详解 AOF
3.1 AOF 实现
和 RDB 持久化数据库键值对来记录数据库状态不同,AOF 是通过保存对数据库的写命令集来记录数据库状态的。AOF 持久化实现可以分为命令追加(append)、文件写入(write)、文件同步(fsync) 三个步骤。Append 追加命令到 AOF 缓冲区,Write 将缓冲区的内容写入到程序缓冲区,Fsync 将程序缓冲区的内容写入到文件。
命令追加
当 AOF 持久化功能打开时,server 端每执行完一个写命令,会以协议格式将被执行的写命令追加到 server 端 redisServer 结构体中的 aof_buf 缓冲区末尾。
文件写入与同步
Redis server 进程是一个事件循环(event loop),server 每结束一个事件循环之前都会调用 flushAppendOnlyFile 函数,考虑是否将 aof_buf 缓冲区中的内容吸入和保存到 AOF 文件,而 flushAppendOnlyFile 函数的行为由 appendfsync 选项来控制
appendfsync 值 | flushAppendOnlyFile 行为 |
---|---|
always | 每个事件循环都将 aof_buf 缓冲区中的内容写入 AOF 文件,并且调用 fsync() 将其同步到磁盘。这可以保证最好的数据持久性,但却会给系统带来极大的开销,其效率是三者中最慢的,但同时安全性也是最高的,即使宕机也只丢失一个事件循环中的数据。 |
no | 每个事件循环都将 aof_buf 缓冲区中的内容写入 AOF 文件,但不对其进行同步,何时同步至磁盘会让操作系统决定。这种模式下 AOF 的写入速度最快,不过因其会在系统缓存中积累一段时间的数据,所以同步时间为三者最长。一旦宕机将会丢失自上一次同步 AOF 文件起所有的数据。 |
everysec | 每个事件循环都将 aof_buf 缓冲区中的内容写入 AOF 文件,Redis 还会每秒在子线程中执行一次 fsync()。在实践中,推荐使用这种设置,一定程度上可以保证数据持久性,又不会明显降低 Redis 性能。 |
3.2 AOF 重写
AOF 持久化并不是没有缺点的,Redis 会不断将接收到的写命令追加到 AOF 文件中,导致 AOF 文件越来越大。过大的 AOF 文件会消耗磁盘空间,并且导致 Redis 重启时更加缓慢。为了解决这个问题,在适当情况下,Redis 会对 AOF 文件进行重写,去除文件中冗余的命令,以减小 AOF 文件的体积。
AOF的重写会执行大量的写入操作,Redis是单线程的,所以如果有服务器直接调用重写,服务器就不能处理其他命令了,因此Redis服务器新起了单独一个进程来执行AOF重写。当然也可以通过 BGREWRITEAOF 命令手动重写 AOF 文件。重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。
在子进程执行AOF重写时,服务端接收到客户端的命令之后,先执行客户端发来的命令,然后将执行后的写命令追加到AOF缓冲区中,同时将执行后的写命令追加到AOF重写缓冲区中。 等到子进程完成了重写工作后,会发一个完成的信号给服务器,服务器就将AOF重写缓冲区中的所有内容追加到AOF文件中,然后原子性地覆盖现有的AOF文件。
3.3 AOF 的优点
-
比RDB可靠。你可以制定不同的 fsync 策略:no、everysec 和 always。默认是 everysec。这意味着你最多丢失一秒钟的数据。
-
AOF日志文件是一个纯追加的文件。就算是遇到突然停电的情况,也不会出现日志的定位或者损坏问题。甚至如果因为某些原因(例如磁盘满了)命令只写了一半到日志文件里,我们也可以用 redis-check-aof 这个工具很简单的进行修复。
-
当AOF文件太大时,Redis 会自动在后台进行重写。重写很安全,因为重写是在一个新的文件上进行,同时 Redis 会继续往旧的文件追加数据。新文件上会写入能重建当前数据集的最小操作命令的集合。当新文件重写完,Redis 会把新旧文件进行切换,然后开始把数据写到新文件上。
-
AOF 把操作命令以简单易懂的格式一条接一条的保存在文件里,很容易导出来用于恢复数据。例如我们不小心用 FLUSHALL 命令把所有数据刷掉了,只要文件没有被重写,我们可以把服务停掉,把最后那条命令删掉,然后重启服务,这样就能把被刷掉的数据恢复回来。
3.4 AOF 的缺点
-
在相同的数据集下,AOF 文件的大小一般会比 RDB 文件大。
-
在某些 fsync 策略下,AOF 的速度会比 RDB 慢。通常 fsync 设置为每秒一次就能获得比较高的性能,而在禁止 fsync 的情况下速度可以达到 RDB 的水平。
-
在过去曾经发现一些很罕见的BUG导致使用AOF重建的数据跟原数据不一致的问题。
4. Redis 4.0 混合持久化
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分就是压缩格式不再是 AOF 格式,可读性较差。
5. 参考资料
-
《Redis 设计与实现》黄建宏著