Redis 数据持久化

Redis 中,有两种方式将数据持久化到磁盘。一种称之为快照 (Spapshotting), 将某一时刻存在数据写道磁盘。另外一种称之为AOF 仅追加文件 (append-only file), 它是将到来的所有写命令写到磁盘上。这两种方式可以一起使用,也可以单独使用,或者不适用持久化。

选择配置数据持久话的原因还是为了在 Redis,Redis 所在的主机硬件等发生故障时进行恢复,可以将持久化的文件在远程主机进行备份,故障时进行恢复。而且,如果 Redis 中的数据时大数据集上的聚合分析结果,没有备份,重新计算的代价可能时无法接受的。

通过快照将数据持久化到磁盘

在 Redis 中,可以通过创建一个快照,创建一个内部中数据的某个时刻的副本,这些副本可以被备份,复制到其它服务器,创建一个 Redis 服务器的副本,或者留着待以后重启使用。

基本的配置选项

1
2
3
4
5
6
save 60 1000 # 创建快照的频率。如果自上一次创建快照后的 60s 内有 1000 此写,就开启新的快照
stop-writes-on-bgsave-error no # 当创建快照时,发生异常是否暂停所有的写命令
rdbcompression yes # 是否进行压缩
dbfilename dump.rdb # 快照文件名

dir ./ # 快照文件保存的目录

自上一次快照完成后,直到下一次快照开始执行,这个期间,如果 Redis 崩溃,系统或硬件故障,期间的写数据就会丢失。

五种初始化快照的方法

  1. 任何的 Redis 客户端都可以通过调用 BGSAVE 命令来初始化快照(创建快照)。在所有支持 BGSAVE 的平台上,Redis 会进行进程的 fork, 子进程会将快照写到磁盘,而父进程仍然可以用于响应来自客户端的命令。

    当一个进程 fork 时,底层的操作系统会创建该进程的一个副本。在 Unix 和类 Unix 的系统上,复制进程会被优化:最开始,子进程和父进程时共享所有内存的。当父进程或子进程开始写内存,内存将不再共享。

  2. Redis 的客户端可以通过调用 SAVE 命令来初始化快照,这将导致 Redis 停止对任何命令的响应,直到快照完成。不经常使用,但是如果对于该这种等待 ok 或者没有足够的内存执行 BGSAVE 操作,可以使用。

  3. 如果 Redis 配置了 save 选项,如 save 60 10000, 如果自上一次快照成功后的 60s 内,已经有 10000 个写操作,那么 Redis 会自动触发一个 BGSAVE 操作。可以配置多个 save 行,任意一条规则满足,就会触发 BGSAVE 操作。

  4. 当 Redis 接受到 SHUTDOWN 终止命令时,或者接受到标准的 TERM 信号,Redis 会执行 SAVE 操作,阻塞任何客户端执行任何命令,然后再终止。

  5. 当一个 Redis 服务器连接到另一个 Reids 服务器,并且发起了 SYNC 同步命令开始复制(replication), Master Redis 服务器将会开始一个 BGSAVE 操作,如果当前没有一个正在进行中的 BGSAVE 操作或者最近完成了。

只追加写文件持久化 (Append-only file persistence)

只追加写文件通过将修改写到文件的末尾,保存了数据发生修改时的记录。这样,任何人都有可以通过从头到尾,重放 (replay) 只追加写日志文件就可以恢复整个数据集。可以通过设置配置项 appendonly yes 开启。

文件同步

当写文件到磁盘上时,至少发生了三件事:首先是写缓存,当调用 file.write() 或其它语言中等价的命令时会执行该操作。当数据存在缓冲区中时,操作系统可以将这些数据再未来的某个时刻写到磁盘。可以通过调用 file.flush(), 要求操作系统将数据写到磁盘上,但这只是发给操作系统的一个请求,并不会立即执行。由于数据实际时不在磁盘上的直到操作系统将它写到磁盘上,我们可以告诉操作系统同步文件到磁盘上,这将导致阻塞 (block),直到同步完成。当同步完成时,我们可以确定数据此时是在磁盘上的,并且如果系统故障,可以稍后读取进行恢复。

appendfsync always

如果设置了 appendfsync always, Redis 的每次写将会导致一个磁盘的写过程,如果 Reid 崩溃了,这可以极大地减少数据丢失。然而,因为每次都有写磁盘这个过程,整体的性能会受限于此磁盘的性能。

appendfsync eversec

作为保持数据安全和保持高写入性能之间的合理折衷,我们还可以设置 appendfsync everysec。 此配置将每秒同步一次仅追加日志文件。 对于大多数常见用途,与不使用任何类型的持久性相比,我们可能不会发现每秒同步到磁盘的显着性能损失。 通过每秒同步到磁盘,如果系统崩溃,我们最多可能会丢失一秒钟已在 Redis 中写入或更新的数据。 此外,在磁盘无法跟上正在发生的写入量的情况下,Redis 会优雅地减速以适应驱动器的最大写入速率。

appendfsync no

Redis 并不会显示地执行任何文件同步,而是将这一切交给操作系统。这种情况下应该没有性能损失,但如果系统以某种方式崩溃,我们将丢失未知和不可预测的数据量。 如果我们使用的硬盘驱动器对于我们的写入负载来说不够快,Redis 会运行良好,直到将数据写入磁盘的缓冲区被填满,此时 Redis 会因为被阻止写入而变得非常慢。 通常不鼓励使用此配置选项。

重写/压缩 AOF

AOF 看起来比较完美,即能将数据损失降低到 1s,又可以最小化数据持久化到磁盘上地时间。但是问题是,Redis 的每次写命令,都会生成一条日志记录,随着时间的推移,AOF 日志文件的大小会不断增长,可能会导致磁盘空间耗尽。但更常见的问题是,在 Redis 重启时,由于需要按顺序执行 AOF 中的每条命令,处理较大的文件,需要花费更长的时间

此时可以使用 BGREWRITEAOF, 它会通过移除冗余的命令,来尽可能地使得 AOF 变得更小一点。和 BGSAVE 命令一样,这个命令也会执行一次 fork 过程,由子进程完成 AOF 的重写,所以关于 fork 的时间,内存使用的问题,同样也适用于 BGREWRITEAOF。而且更糟的时,当 AOF 被重写,操作系统需要删除几十 G 的 AOF 文件时,会使的操作系统中断几秒。

当启用 AOF , 并且满足以下两个配置项时,Redis 会启动一次 BGREWRITEAOF, 如果 AOF 重写的频率过高,可以适当增加 auto-aof-rewritepercentage, 但是可能会导致 Redis 启动需要花费更多的时间

1
2
3
4
5
# 当 AOF 至少比 Redis 上次完成重写时的 AOF 大 100% 时
auto-aof-rewritepercentage 100

# 当 AOF 的大小至少为 64m 字节时
auto-aof-rewrite-min-size 64mb

复制 Replication