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