当前位置:首页 > 问答 > 正文

Redis怎么用来让数据不丢失,持久化那些事儿聊聊

关于Redis怎么保证数据不丢失,也就是它的持久化机制,这事儿咱们可以聊得简单点,你把它想象成一个特别爱记笔记的人,他有两种记笔记的习惯,一种是把所有事情原原本本、一字不落地抄下来(这叫RDB),另一种是只记下自己做了哪些改动,像写日记一样(这叫AOF),Redis就是靠这两种方法来确保万一自己“忘事”(服务器断电或崩溃),还能靠笔记把事情想起来。

第一种方法:RDB(快照)

你可以把RDB理解成“拍照片”,想象一下,Redis在某个时间点,咔嚓一声,给内存里所有的数据拍一张完整的快照,然后把这张照片存到一个文件里(默认叫dump.rdb),以后如果Redis重启,直接把这照片读进内存,数据就恢复原样了。

那什么时候会拍这张照片呢?Redis提供了几种方式:

  1. 自动拍: 你可以在配置文件里设置规则,900秒内至少有1个键被改动”就拍一张,或者“300秒内至少有10个键被改动”拍一张,这就像你规定自己“每工作一小时就总结一次”。
  2. 手动拍: 你可以在命令行里输入 SAVE 命令,但这条命令会阻塞所有其他请求,直到照片拍完,生产环境基本不用,另一个命令是 BGSAVE,这个比较友好,Redis会“偷偷地”在后台fork一个子进程去拍照片,主进程还能继续处理你的读写请求,不影响服务。

RDB的优点和缺点:

  • 优点:
    • 恢复速度快: 因为恢复时直接加载一个文件,就像把一本厚厚的书直接搬进脑子里,非常快。
    • 文件紧凑: RDB文件是压缩过的二进制文件,占用的磁盘空间小。
  • 缺点:
    • 可能会丢数据: 这是最要命的问题,如果刚拍完照片,下一秒服务器就宕机了,那么从上次拍照到宕机之间的所有数据改动就全丢了,所以RDB的持久化级别是以“可能丢失几分钟数据”为代价的。
    • 如果数据量太大,拍照可能影响性能: 虽然BGSAVE是后台操作,但fork子进程的瞬间会阻塞,而且如果数据量极大,写磁盘的I/O操作也可能对性能产生一些影响。

第二种方法:AOF(追加日志)

AOF的方式就完全不同了,它不拍照片,而是“写日记”,Redis会把每一个会修改数据的写命令(比如SET, LPUSH, DEL等)都记录到一个日志文件的末尾,这个日志文件就是一个“操作流水账”,当Redis重启时,它会把这个流水账从头到尾重新执行一遍,这样就能精确地重建出内存数据。

Redis怎么用来让数据不丢失,持久化那些事儿聊聊

AOF听起来比RDB靠谱,因为它记录的是每一个操作,那它怎么保证不丢数据呢?这取决于它“写日记”的认真程度,也就是我们常说的“刷盘策略”:

  1. 总是偷懒(appendfsync no): Redis把写命令写到操作系统内核的缓存里就完事了,让操作系统自己决定什么时候真正写到磁盘上,这样性能最好,但万一系统崩溃,内核缓存里的数据就没了,可能丢一大段数据。
  2. 每秒一次(appendfsync everysec): Redis默认的策略,每秒强制把内核缓存里的日记刷到磁盘一次,这样即使宕机,最多也就丢一秒的数据,在性能和安全性之间是个不错的折中。
  3. 每次都写(appendfsync always): 每执行一个写命令,就立刻强制刷盘,这是最安全的方式,基本不会丢数据(除非磁盘本身坏了),但也是性能最差的,因为写磁盘的速度远慢于写内存。

AOF的优点和缺点:

  • 优点:
    • 数据更安全: 特别是用 always 策略时,数据丢失的风险极低。everysec策略也最多丢一秒数据。
    • 可读性强: AOF文件是纯文本格式,里面记录着所有命令,万一出问题,你甚至可以用文本编辑器打开它,进行人工修复(不过要非常小心)。
  • 缺点:
    • 文件体积大: 日记文件会随着时间推移变得非常大,比如你对一个键连续修改100次,RDB只记录最终值,而AOF会记录100条命令。
    • 恢复速度慢: 重启时重新执行所有命令,如果日志文件很大,这个恢复过程会非常漫长。

混合持久化:取长补短

既然两种方法各有优劣,那能不能结合起来用呢?当然可以!Redis 4.0之后引入了混合持久化(在AOF功能开启的前提下)。

Redis怎么用来让数据不丢失,持久化那些事儿聊聊

它的工作原理是这样的:Redis在定期做AOF重写(一种压缩AOF日志的机制,把多条命令合并成最终结果的命令)时,不再是单纯地生成新的AOF日志,而是先把当前内存的数据快照(以RDB格式)写入新的AOF文件的开头,然后再把重写开始后产生的新增写命令(以AOF格式)追加到文件后面。

这样一来,这个新的AOF文件就变成了“前半部分是RDB快照,后半部分是增量AOF日志”,当Redis重启恢复时:

  1. 先加载文件开头的RDB快照内容,这非常快,能把大部分数据先恢复。
  2. 然后再重放文件后面那一小段AOF日志,来恢复快照之后的最新数据。

这种方式既享受了RDB快速加载的优点,又享受了AOF尽可能少丢失数据的优点,是目前非常推荐的一种持久化策略。

总结一下

回到最初的问题“Redis怎么用来让数据不丢失?”:

  • 如果你能容忍丢失几分钟数据,追求最快的重启速度,可以只用RDB。
  • 如果你一点数据都不能丢,并且能接受性能上的一些损失,可以只用AOF,并设置为 appendfsync always
  • 对于绝大多数场景,推荐的方案是同时开启RDB和AOF(或者直接开启混合持久化),并使用AOF的默认 everysec 刷盘策略,这样既能保证最多只丢一秒数据,重启时利用混合持久化又能快速恢复,是一个比较完美的平衡点。 参考了Redis官方文档关于Persistence的章节,以及《Redis设计与实现》一书中对RDB和AOF机制的详细解读。)