Redis持久化机制

我们通常将 Redis 作为缓存使用,提高读取响应性能,一旦 Redis 宕机,内存中的数据全部丢失,假如现在直接访问数据库大量流量打到 MySQL 可能会带来更加严重的问题。

另外慢慢的从数据库读取放到 Redis 性能必然比不过从 Redis 获取快,也会导致响应变慢。

Redis 为了实现无畏宕机快速恢复,设计了两大杀手锏,分别是 AOF(Append Only FIle)日志和 RDB 快照。

RDB 内存快照,让宕机快速恢复

在 Redis 执行「写」指令过程中,内存数据会一直变化。所谓的内存快照,指的就是 Redis 内存中的数据在某一刻的状态数据。

好比时间定格在某一刻,当我们拍照的,通过照片就能把某一刻的瞬间画面完全记录下来。

Redis 跟这个类似,就是把某一刻的数据以文件的形式拍下来,写到磁盘上。这个快照文件叫做 RDB 文件,RDB 就是 Redis DataBase 的缩写。

Redis 通过定时执行 RDB 内存快照,这样就不必每次执行「写」指令都写磁盘,只需要在执行内存快照的时候写磁盘。既保证了唯快不破,还实现了持久化,宕机快速恢复。

在做数据恢复时,直接将 RDB 文件读入内存完成恢复。

生成 RDB 策略

Redis 提供了两个指令用于生成 RDB 文件:

  • save:主线程执行,会阻塞;
  • bgsave:调用 glibc 的函数fork产生一个子进程用于写入 RDB 文件,快照持久化完全交给子进程来处理,父进程继续处理客户端请求,生成 RDB 文件的默认配置

Redis 如何实现一边处理写请求,同时生成 RDB 文件呢?

Redis 使用操作系统的多进程写时复制技术 COW(Copy On Write) 来实现快照持久化,这个机制很有意思,也很少人知道。多进程 COW 也是鉴定程序员知识广度的一个重要指标。

Redis 在持久化时会调用 glibc 的函数fork产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。

子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。这时你可以将父子进程想像成一个连体婴儿,共享身体。

bgsave 子进程可以共享主线程的所有内存数据,读取主线程的数据并写入到 RDB 文件。

在执行 SAVE 命令或者BGSAVE命令创建一个新的 RDB 文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的 RDB 文件中。

当主线程执行写指令修改数据的时候,这个数据就会复制一份副本, bgsave 子进程读取这个副本数据写到 RDB 文件,所以主线程就可以直接修改原来的数据。

写时复制技术保证快照期间数据可修改

这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。

Redis 会使用 bgsave 对当前内存中的所有数据做快照,这个操作是子进程在后台完成的,这就允许主线程同时可以修改数据。

RDB的优缺点

快照的恢复速度快,但是生成 RDB 文件频率不好把握,频率过低宕机丢失的数据就会比较多;太快,又会消耗额外开销。

RDB 采用二进制 + 数据压缩的方式写磁盘,文件体积小,数据恢复速度快。

AOF 写后日志,避免宕机数据丢失

AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。

假设 AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例顺序执行所有的指令,也就是「重放」,来恢复 Redis 当前实例的内存数据结构的状态。

写前与写后日志对比

写前日志(Write Ahead Log, WAL): 在实际写数据之前,将修改的数据写到日志文件中,故障恢复得以保证。

比如 MySQL Innodb 存储引擎 中的 redo log(重做日志)便是记录修改的数据日志,在实际修改数据前先记录修改日志在执行修改数据。

写后日志: 先执行「写」指令请求,将数据写入内存,再记录日志。

AOF 使用写后日志这种方式。写后日志避免了额外的检查开销,不需要对执行的命令进行语法检查。如果使用写前日志的话,就需要先检查语法是否有误,否则日志记录了错误的命令,在使用日志恢复的时候就会出错。

另外,写后才记录日志,不会阻塞当前的「写」指令执行。

“有了 AOF 就万无一失了么?”

假如 Redis 刚执行完指令,还没记录日志宕机了,就有可能丢失这个命令相关的数据。

还有,AOF 避免了当前命令的阻塞,但是可能会给下一个命令带来阻塞的风险。AOF 日志是主线程执行,将日志写入磁盘过程中,如果磁盘压力大就会导致写磁盘很慢,导致后续的「写」指令阻塞。

这两个问题与磁盘写回有关,如果能合理的控制「写」指令执行完后 AOF 日志写回磁盘的时机,问题就迎刃而解。

写回策略

为了提高文件的写入效率,当用户调用 write 函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时保存在一个内存缓冲区里面,等到缓冲区的空间被填满、或者超过了指定的时限之后,才真正地将缓冲区中的数据写入到磁盘里面。

这种做法虽然提高了效率,但也为写入数据带来了安全问题,因为如果计算机发生停机,那么保存在内存缓冲区里面的写入数据将会丢失。

为此,系统提供了fsync和fdatasync两个同步函数,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面,从而确保写入数据的安全性。

Redis 提供的 AOF 配置项appendfsync写回策略直接决定 AOF 持久化功能的效率和安全性。

  • always:同步写回,写指令执行完毕立马将 aof_buf缓冲区中的内容刷写到 AOF 文件。
  • everysec:每秒写回,写指令执行完,日志只会写到 AOF 文件缓冲区,每隔一秒就把缓冲区内容同步到磁盘。
  • no: 操作系统控制,写执行执行完毕,把日志写到 AOF 文件内存缓冲区,由操作系统决定何时刷写到磁盘。

没有两全其美的策略,我们需要在性能和可靠性上做一个取舍。

always同步写回可以做到数据不丢失,但是每个「写」指令都需要写入磁盘,性能最差。

everysec每秒写回,避免了同步写回的性能开销,发生宕机可能有一秒位写入磁盘的数据丢失,在性能和可靠性之间做了折中。

no操作系统控制,执行写指令后就写入 AOF 文件缓冲就可以执行后续的「写」指令,性能最好,但是有可能丢失很多的数据。

日志过大:AOF 重写机制

Redis 设计了一个杀手锏「AOF 重写机制」,Redis 提供了 bgrewriteaof指令用于对 AOF 日志进行瘦身。

其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。

重写机制有「多变一」功能,将旧日志中的多条指令,在重写后就变成了一条指令。

重写过程
和 AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

“AOF 重写也有一个重写日志,为什么它不共享使用 AOF 本身的日志呢?”

1、一个原因是父子进程写同一个文件必然会产生竞争问题,控制竞争就意味着会影响父进程的性能。
2、如果 AOF 重写过程中失败了,那么原本的 AOF 文件相当于被污染了,无法做恢复使用。所以 Redis AOF 重写一个新文件,重写失败的话,直接删除这个文件就好了,不会对原先的 AOF 文件产生影响。等重写完成之后,直接替换旧文件即可。

Redis 4.0 混合日志模型

Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。

于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

所以 RDB 内存快照以稍微慢一点的频率执行,在两次 RDB 快照期间使用 AOF 日志记录期间发生的所有「写」操作。

这样快照就不用频繁的执行,同时由于 AOF 只需要记录两次快照之间发生的「写」指令,不需要记录所有的操作,避免出现文件过大的情况。

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

推荐阅读更多精彩内容