Redis 持久化
Redis 有两种持久化的方式: 快照 (RDB文件) 和追加式文件 (AOF文件):
- RDB 持久化方式会在一个特定的间隔保存那个时间点的一个数据快照.
- AOF 持久化方式则会记录每一个服务器收到的写操作. 在服务启动时, 这些记录的操作会逐条执行从而重建出原来的数据. 写操作命令记录的格式跟 Redis 协议一致, 以追加的方式进行保存.
- Redis 的持久化是可以禁用的, 就是说你可以让数据的生命周期只存在于服务器的运行时间里.
- 两种方式的持久化是可以同时存在的, 但是当 Redis 重启时, AOF文件会被优先用于重建数据.
RDB
工作原理
- Redis 调用
fork()
, 产生一个子进程. - 子进程把数据写到一个临时的 RDB 文件.
- 当子进程写完新的 RDB 文件后, 把旧的 RDB 文件替换掉.
文件路径和名称
默认 Redis 会把快照文件存储为当前目录下一个名为 dump.rdb
的文件. 要修改文件的存储路径和名称, 可以通过修改配置文件 redis.conf
实现:
# RDB文件名,默认为dump.rdb。 dbfilename dump.rdb # 文件存放的目录,AOF文件同样存放在此目录下。默认为当前工作目录。 dir ./
保存点 (RDB的启用和禁用)
你可以配置保存点, 使 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有变动
如果想禁用快照保存的功能, 可以通过注释掉所有 "save" 配置达到,或者在最后一条 "save" 配置后添加如下的配置:
save ""
错误处理
默认情况下, 如果 Redis 在后台生成快照的时候失败, 那么就会停止接收数据, 目的是让用户能知道数据没有持久化成功.
但是如果你有其他的方式可以监控到 Redis 及其持久化的状态, 那么可以把这个功能禁止掉.
stop-writes-on-bgsave-error yes
数据压缩
默认 Redis 会采用 LZF
对数据进行压缩.
如果你想节省点 CPU 的性能, 你可以把压缩功能禁用掉, 但是数据集就会比没压缩的时候要大.
rdbcompression yes
数据校验
从版本 5 的 RDB 的开始, 一个 CRC64 的校验码会放在文件的末尾. 这样更能保证文件的完整性, 但是在保存或者加载文件时会损失一定的性能 (大概10%).
如果想追求更高的性能, 可以把它禁用掉, 这样文件在写入校验码时会用 0 替代, 加载的时候看到 0 就会直接跳过校验.
rdbchecksum yes
手动生成快照
Redis 提供了两个命令用于手动生成快照.
BGSAVE
BGSAVE
命令使用后台的方式保存 RDB 文件, 调用此命令后, 会立刻返回OK
.
Redis 会产生一个子进程进行快照写入硬盘. 父进程继续处理命令请求.
SAVE
SAVE
命令会使用同步的方式生成 RDB 快照文件, 这意味着在这个过程中会阻塞所有其他客户端的请求.
因此不建议在生产环境使用这个命令.
重点
- 如果用户设置了
save 60 1000
, 那么从 Redis 最近一次创建快照之后开始算起. 当条件被满足时, 就会自动出发BGSAVE
命令. 如果有多个SAVE
配置, 那么当任意一个被满足时, 都会出发一次BGSAVE
命令. - 当 Redis 通过
SHUTDOWN
命令, 来关闭服务时, 或者接收到标准TERM
信号时, 会执行一次SAVE
命令, 阻塞所有客户端, 并且执行完毕后会关闭 Redis 服务.
这里注意的是 fork
操作会阻塞, 导致 Redis 读写性能下降. 我们可以控制单个 Redis 实例的最大内存, 来尽可能降低 Redis 在 fork
时的事件消耗. 以及上面提到的自动触发的频率减少 fork
次数, 或者使用手动触发, 根据自己的机制来完成持久化.
AOF
快照并不是很可靠. 如果你的电脑突然宕机了, 或者电源断了, 又或者不小心杀掉了进程, 那么最新的数据就会丢失.
而 AOF 文件则提供了一种更为可靠的持久化方式. 每当 Redis 接受到会修改数据集的命令时, 就会把命令追加到 AOF 文件里, 当你重启 Redis 时, AOF 里的命令会被重新执行一次, 重建数据.
启用 AOF
把配置项 appendonly
设为 yes
:
appendonly yes
文件路径和名称
# 文件存放目录,与RDB共用。默认为当前工作目录。 dir ./ # 默认文件名为appendonly.aof appendfilename "appendonly.aof"
可靠性
你可以配置 Redis 调用 fsync
的频率, 有三个选项:
- 每当有新命令追加到 AOF 的时候调用
fsync
. 速度最慢, 最安全. - 每秒
fsync
一次. 速度快 (2.4版本跟快照方式速度差不多), 安全性不错 (最多丢失 1 秒的数据). - 从不
fsync
, 交由系统去处理. 这个方式速度最快, 但是安全性一般.
推荐使用每秒 fsync 一次的方式 (默认的方式), 因为它速度快, 安全性也不错. 相关配置如下:
# appendfsync always appendfsync everysec # appendfsync no
对于增量追加到文件这一步主要的流程是: 命令写入=》追加到 aof_buf =》同步到aof磁盘. 那么这里为什么要先写入 buf 在同步到磁盘呢? 如果实时写入磁盘会带来非常高的磁盘IO, 影响整体性能.
AOF 重写是为了减少 AOF 文件的大小, 随着写操作的不断增加, AOF 文件会越来越大. 例如你递增一个计数器 100 次, 那么最终结果就是数据集里的计数器的值为最终的递增结果, 但是 AOF 文件里却会把这 100 次操作完整的记录下来.
而事实上要恢复这个记录, 只需要 1 个命令就行了, 也就是说 AOF 文件里那 100 条命令其实可以精简为 1 条. 所以 Redis 支持这样一个功能: 在不中断服务的情况下在后台重建 AOF 文件.
对于上图有四个关键点补充一下:
- 在重写期间, 由于主进程依然在响应命令, 为了保证最终备份的完整性; 因此它依然会写入旧的 AOF file 中, 如果重写失败, 能够保证数据不丢失.
- 为了把重写期间响应的写入信息也写入到新的文件中, 因此也会为子进程保留一个 buf, 防止新写的 file 丢失数据.
- 重写是直接把当前内存的数据生成对应命令, 并不需要读取老的 AOF 文件进行分析、命令合并.
- AOF 文件直接采用的文本协议, 主要是兼容性好、追加方便、可读性高可认为修改修复.
性能与实践
通过上面的分析, 我们都知道 RDB 的快照、AOF的重写都需要 fork
, 这是一个重量级操作, 会对 Redis 造成阻塞. 因此为了不影响 Redis 主进程响应, 我们需要尽可能降低阻塞.
- 降低
fork
的频率, 比如可以手动来触发 RDB 生成快照、与AOF重写; - 控制 Redis 最大使用内存, 防止
fork
耗时过长; - 使用更牛逼的硬件;
- 合理配置 Linux 的内存分配策略, 避免因为物理内存不足导致
fork
失败.
在线上我们到底该怎么做? 我提供一些自己的实践经验.
- 如果 Redis 中的数据并不是特别敏感或者可以通过其它方式重写生成数据, 可以关闭持久化;
- 自己制定策略定期检查 Redis 的情况, 然后可以手动触发备份、重写数据;
- 单机如果部署多个实例, 要防止多个机器同时运行持久化、重写操作, 防止出现内存、CPU、IO资源竞争, 让持久化变为串行;
- 可以加入主从机器, 利用一台从机器进行备份处理, 其它机器正常响应客户端的命令;
- RDB 持久化与 AOF 持久化可以同时存在, 配合使用.