一文让你明白Redis持久化

网上虽然已经有很多类似的介绍了,但我还是自己总结归纳了一下,自认为内容和细节都是比较齐全的。

文章篇幅有 4k 多字,货有点干,断断续续写了好几天,希望对大家有帮助。不出意外地话,今后会陆续更新 Redis 相关的文章,和大家一起学习。

好了,下面开始回归正文:

Redis 一共有 2 种持久化方式,分别是 RDB 和 AOF,下面我来详细介绍两种方式在各个过程所做的事情,特点等等。

1、RDB 持久化

RDB 持久化是 Redis 默认的持久化方式。

它所生成的 RDB 文件是一个压缩的二进制文件,通过该文件可以还原生成 RDB 文件时的数据库状态

PS:数据库状态是指 Redis 服务器的非空数据库以及他们键值对的统称

1.1 RDB 文件的创建

有两个命令可以生成 RDB 文件,一个是 SAVE、另一个是 BGSAVE。

两者的区别在于:前者会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止。

而在服务器进程阻塞期间,服务器是不能处理任何命令请求的。

后者则不会阻塞服务器进程,因为是通过 fork 一个子进程,并让其去创建 RDB 文件,而服务器进程(父进程)继续则继续处理命令请求。

当写完数据库状态后,新 RDB 文件就会原子地替换旧的 RDB 文件。

此处小提问:如果在执行 BGSAVE 期间,客户端发送 SAVE、BGSAVE 或 BGREWRITEAOF 命令给服务端,服务端会如何处理呢?

答案:在执行 BGSAVE 期间,上述三个命令都不会被执行。

详细原因:前两个会被直接拒绝,原因是为了避免父子进程同时执行两个 rdbSave 调用,防止产生竞争条件。

而 BGREWRITEAOF 命令则是会被延迟到 BGSAVE 命令执行之后再执行。

但如果是 BGREWRITEAOF 命令正在执行,此时客户端发送 BGSAVE 命令则会被拒绝。

因为 BGREWRITEAOF 和 BGSAVE 都是由子进程执行的,所以在操作方面没有冲突的地方,不能同时执行的原因是性能上的考虑——并发出两个子进程,并且这两个子进程都会同时执行大量 io(磁盘写入)操作

1.2 RDB 文件的载入

RDB 文件的载入是在服务器启动时自动执行的,所以没有用于载入的命令,期间阻塞主进程。

只要没有开启 AOF 持久化功能,在启动时检测到有 RDB 文件,就会自动载入。

当服务器有开启 AOF 持久化功能时,服务器将会优先使用 AOF 文件来还原数据库状态。原因是 AOF 文件的更新频率通常比 RDB 文件的更新频率高。

**1.3 自动间隔性保存 **

对于 RDB 持久化而言,我们一般都会使用 BGSAVE 来持久化,毕竟它不会阻塞服务器进程。

在 Redis 的配置文件,有提供设置服务器每隔多久时间来执行 BGSAVE 命令。

Redis 默认是如下配置:

save 900 1 // 900 秒内,对数据库至少修改 1 次。下面同理

save 300 10

save 60 10000

只要满足其中一种情况,服务器就会执行 BGSAVE 命令。

2、AOF 持久化

我们从上面的介绍知道,RDB 持久化通过保存数据库状态来持久化。而 AOF 与之不同,它是通过保存对数据库的写命令来记录数据库状态。

比如执行了 set key 123,Redis 就会将这条写命令保存到 AOF 文件中。

在服务器下次启动时,就可以通过载入和执行 AOF 文件中保存的命令,来还原服务器关闭前的数据库状态了。

总体流程和 RDB 持久化一样 —— 都是创建一个 xxx 文件、在服务器下次启动时就载入这个文件来还原数据

那么,AOF 持久化具体是怎么实现的呢?

2.1 AOF 持久化实现

AOF 持久化功能的实现可以分为 3 个步骤:命令追加、文件写入、文件同步

其中命令追加很好理解,就是将写命令追加到 AOF 缓冲区的末尾。

那文件写入和文件同步怎么理解呢?刚开始我也一脸懵逼,终于在网上找到了答案,参考见文末,有兴趣的读者可以去看看。

先不卖关子了,简单一句话解释就是:前者是缓冲区内容写到 AOF 文件,后者是将 AOF 文件保存到磁盘。

ok,明白什么意思之后,我们稍微详细看下这两个东西是什么鬼。

在《Redis设计与实现》中提到,Redis 服务器进程就是一个事件循环,这个循环中的文件事件(socket 的可读可写事件)负责接收客户端的命令请求,以及向客户端发送命令结果。

因为服务器在处理文件事件时,可能会发生写操作,使得一些内容会被追加到 AOF 缓冲区末尾。所以,在服务器每次结束一个事件循环之前 ,都会调用 flushAppendOnlyFile 方法。

这个方法执行以下两个工作:

  • WRITE:根据条件,将缓冲区内容写入到 AOF 文件。

  • SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。

两个步骤都需要根据一定的条件来执行,而这些条件由 Redis 配置文件中的 appendfsync 选项来决定的,一共有三个选择:

1. appendfsync always:每执行一个命令保存一次

2. appendfsync everysec(默认,推荐):每一秒钟保存一次

3. appendfsync no:不保存

下面说下三个的区别:

  • appendfsync always:每次执行完一个命令之后, WRITE 和 SAVE 都会被执行

  • appendfsync everysec:SAVE 原则上每隔一秒钟就会执行一次。

  • appendfsync no:每次执行完一个命令之后, WRITE 会执行,SAVE 都会被忽略,只会在以下任意一种情况中被执行:

  • Redis 被关闭

  • AOF 功能被关闭

  • 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行。完成依赖 OS 的写入,一般为 30 秒左右一次)

而对于操作特性来分析的话,则是如下情况:

image

既然 AOF 持久化是通过保存写命令到文件的,那随着时间的推移,这个 AOF 文件记录的内容就越来越多,文件体积也就越来越大,对其进行数据还原的时间也就越来越久。

针对这个问题,Redis 提供了 AOF 文件重写功能。

2.2 AOF 重写

通过该功能来创建一个新的 AOF 文件来代替旧文件。并且两个文件所保存的数据库状态一样,但新文件不会包含任何冗余命令,所以新文件要比旧文件小得多。

而为什么新文件不会包含任何冗余命令呢?

那是因为这个重写功能是通过读取服务器当前的数据库状态来实现的。虽然叫做「重写」,但实际上并没有对旧文件进行任何读取修改。

比如旧文件保存了对某个 key 有 4 个 set 命令,经过重写之后,新文件只会记录最后一次对该 key 的 set 命令。因此说新文件不会包含任何冗余命令

因为重写涉及到大量 IO 操作,所以 Redis 是用子进程来实现这个功能的,否则将会阻塞主进程。该子进程拥有父进程的数据副本,可以避免在使用锁的情况下,保证数据的安全性。

那么这里又会存在一个问题,子进程在重写过程中,服务器还在继续处理命令请求,新命令可能会对数据库进行修改,这会导致当前数据库状态和重写后的 AOF 文件,所保存的数据库状态不一致。

为了解决这个问题,Redis 设置了一个 AOF 重写缓冲区。在子进程执行 AOF 重写期间,主进程需要执行以下三个步骤:

1、执行客户端的请求命令

2、将执行后的写命令追加到 AOF 缓冲区

3、将执行后的写命令追加到 AOF 重写缓冲区

当子进程结束重写后,会向主进程发送一个信号,主进程接收到之后会调用信号处理函数执行以下步骤:

1、将 AOF 重写缓冲区内容写入新的 AOF 文件中。此时新文件所保存的数据库状态就和当前数据库状态一致了

2、对新文件进行改名,原子地覆盖现有 AOF 文件,完成新旧文件的替换。

当函数执行完成后,主进程就继续处理客户端命令。

因此,在整个 AOF 重写过程中,只有在执行信号处理函数时才会阻塞主进程,其他时候都不会阻塞。

3、选择持久化方案的官方建议

到目前为止,Redis 的两种持久化方式就介绍得差不多了。可能你会有疑惑,在实际项目中,我到底要选择哪种持久化方案呢?下面,我贴下官方建议:

通常,如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。如果你可以接受灾难带来的几分钟的数据丢失,那么你可以仅使用 RDB。

很多用户仅使用了 AOF,但是我们建议,既然 RDB 可以时不时的给数据做个完整的快照,并且提供更快的重启,所以最好还是也使用 RDB。

在数据恢复方面:

RDB 的启动时间会更短,原因有两个:

1. RDB 文件中每一条数据只有一条记录,不会像 AOF 日志那样可能有一条数据的多次操作记录。所以每条数据只需要写一次就行了。

2. RDB 文件的存储格式和 Redis 数据在内存中的编码格式是一致的,不需要再进行数据编码工作,所以在 CPU 消耗上要远小于 AOF 日志的加载。

注意:上面说了 RDB 快照的持久化,在进行快照的时候(save),fork 出来进行 dump 操作的子进程会占用与父进程一样的内存,真正的 copy-on-write,对性能的影响和内存的耗用都是比较大的。

比如机器 8G 内存,Redis 已经使用了 6G 内存,这时 save 的话会再生成 6G,变成 12G,大于系统的 8G。这时候会发生交换;要是虚拟内存不够则会崩溃,导致数据丢失。所以在用 redis 的时候一定对系统内存做好容量规划。

目前,通常的设计思路是利用复制(Replication)机制来弥补 aof、snapshot 性能上的不足,达到了数据可持久化。即 Master 上 Snapshot 和 AOF 都不做,来保证 Master 的读写性能,而 Slave 上则同时开启 Snapshot 和 AOF 来进行持久化,保证数据的安全性。

坦白讲,我也不知道有多少人能坚持看到这里,文章知识点有点干和杂,这里我帮大家简单总结一下,做个回顾:

  • RDB 持久化是 Redis 默认持久化方式,通过保存数据库键值对来记录状态来持久化,由 SAVE 和 BGSAVE 命令来创建 RDB 文件。前者阻塞 Redis 主进程,后者不会。

  • RDB 可以在配置文件设置每隔多久时间来执行 BGSAVE 命令

  • AOF 通过追加写命令来保存当前数据库状态。其持久化功能的实现可以分为 3 个步骤:命令追加(到 AOF 缓冲区)、文件写入(缓冲区内容写到 AOF 文件)、文件同步(AOF 文件保存磁盘)

  • 其中文件同步和保存可以通过配置文件的 appendfsync 选项来决定

  • 为了解决 AOF 文件越来越大的问题,Redis 提供了 AOF 重写功能,并且不会阻塞主进程。

  • 为了解决 AOF 重写过程中,新 AOF 文件所保存的数据库状态和当前数据库状态可能不一致的问题,Redis 引入了 AOF 重写缓冲区,用于保存子进程在重写 AOF 文件期间产生的新的写命令。

  • 最后是官方对于两种持久化方式选择的一些建议

参考:

《redis设计与实现》

https://www.cnblogs.com/zhoujinyi/archive/2013/05/26/3098508.html

https://redisbook.readthedocs.io/en/latest/internal/aof.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • 本文是一篇比较全面的介绍Redis持久化的文章,篇幅有 4k 多字,十分干货。 Redis 一共有 2 种持久化方...
    Java高级架构狮阅读 530评论 0 0
  • 网上虽然已经有很多类似的介绍了,但我还是自己总结归纳了一下,自认为内容和细节都是比较齐全的。 文章篇幅有 4k 多...
    不只Java阅读 261评论 0 12
  • 一、Redis高可用概述 在介绍Redis高可用之前,先说明一下在Redis的语境中高可用的含义。 我们知道,在w...
    空语阅读 1,593评论 0 2
  • 企业级redis集群架构的特点 海量数据 高并发 高可用 要达到高可用,持久化是不可减少的,持久化主要是做灾难恢复...
    lucode阅读 2,192评论 0 7
  • 素琴闲置久,竹下理七音。 弦轸飞思意,鹦哥俯首闻。 元夕泣折柳,清客等归人。 新岁得良聚,陶然慰别情。
    客居维扬阅读 386评论 8 11