Redis 日志篇:无畏宕机快速恢复的杀手锏(下)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 接上文。

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


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


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


写前与写后日志对比


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


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


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


image.png


日志格式


当 Redis 接受到 「set key MageByte」命令将数据写到内存后,Redis 会按照如下格式写入 AOF 文件。


  • 「*3」:表示当前指令分为三个部分,每个部分都是 「$ + 数字」开头,紧跟后面是该部分具体的「指令、键、值」。


  • 「数字」:表示这部分的命令、键、值多占用的字节大小。比如 「$3」表示这部分包含 3 个字节,也就是 「set」指令。


image.png


65 哥:为什么 Redis 使用写后日志这种方式呢?


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


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


65 哥:那有了 AOF 就万无一失了么?


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


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


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


写回策略


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


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


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


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


  • always:同步写回,写指令执行完毕立马将 aof_buf缓冲区中的内容刷写到 AOF 文件。


  • everysec:每秒写回,写指令执行完,日志只会写到 AOF 文件缓冲区,每隔一秒就把缓冲区内容同步到磁盘。


  • no: 操作系统控制,写执行执行完毕,把日志写到 AOF 文件内存缓冲区,由操作系统决定何时刷写到磁盘。


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


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


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


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


65 哥:那我该如何选择策略呢?


我们可以根据系统对高性能和高可靠性的要求,来选择写回策略。总结一下:想要获得高性能,就选择 No 策略;如果想要得到高可靠性保证,就选择 Always 策略;如果允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择 Everysec 策略。


优缺点


优点:执行成功才记录日志,避免了指令语法检查开销。同时,不会阻塞当前「写」指令。


缺点:由于 AOF 记录的是一个个指令内容,具体格式请看上面的日志格式。故障恢复的时候需要执行每一个指令,如果日志文件太大,整个恢复过程就会非常缓慢。


另外文件系统对文件大小也有限制,不能保存过大文件,文件变大,追加效率也会变低。


日志过大:AOF 重写机制


65 哥:AOF 日志文件过大着怎么办?


AOF 写前日志,记录的是每个「写」指令操作。不会像 RDB 全量快照导致性能损耗,但是执行速度没有 RDB 快,同时日志文件过大也会造成性能问题,对于唯快不破的 Redis 这个真男人来说,绝对不能忍受日志过大导致的问题。


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


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


65 哥:为啥 AOF 重写机制能缩小日志文件呢?


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


如下所示:

三条 LPUSH 指令,经过 AOF 重写后生成一条,对于多次修改的场景,缩减效果更加明显。


image.png


65 哥:重写后 AOF 日志变小,最后把整个数据库最新数据的操作日志刷写到磁盘了。重写会不会阻塞主线程呢?


「码哥」上文说了,AOF 日志是主线程写回的,AOF 重写的过程实际上后台子进程 bgrewriteaof 完成,防止阻塞主线程。


重写过程


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


总的来说,一共出现 两个日志,一次拷内存数据拷贝,分别是旧的 AOF 日志和新的 AOF 重写日志和 Redis 数据拷贝


Redis 会将重写过程中的接收到的「写」指令操作同时记录到旧的 AOF 缓冲区和 AOF 重写缓冲区,这样重写日志也保存最新的操作。等到拷贝数据的所有操作记录重写完成后,重写缓冲区记录的最新操作也会写到新的 AOF 文件中。


每次 AOF 重写时,Redis 会先执行一个内存拷贝,用于遍历数据生成重写记录;使用两个日志保证在重写过程中,新写入的数据不会丢失,并且保持数据一致性。


image.png


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


这个问题问得好,有以下两个原因:


  1. 一个原因是父子进程写同一个文件必然会产生竞争问题,控制竞争就意味着会影响父进程的性能。


  1. 如果 AOF 重写过程中失败了,那么原本的 AOF 文件相当于被污染了,无法做恢复使用。所以 Redis AOF 重写一个新文件,重写失败的话,直接删除这个文件就好了,不会对原先的 AOF 文件产生影响。等重写完成之后,直接替换旧文件即可。


Redis 4.0 混合日志模型


重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。


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


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


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


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


总结


Redis 设计了 bgsave 和写时复制,尽可能避免执行快照期间对读写指令的影响,频繁快照会给磁盘带来压力以及 fork 阻塞主线程。


Redis 设计了两大杀手锏实现了宕机快速恢复,数据不丢失。


避免日志过大,提供了 AOF 重写机制,根据数据库的数据最新状态,生成数据的写操作作为新日志,并且通过后台完成不阻塞主线程。


综合 AOF 和 RDB 在 Redis 4.0 提供了新的持久化策略,混合日志模型。在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。


最后,关于 AOF 和 RDB 的选择问题,「码 哥 字 节」有三点建议:


  • 数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择;


  • 如果允许分钟级别的数据丢失,可以只使用 RDB;


  • 如果只用 AOF,优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡。


经过两篇 Redis 系列文章,读者朋友们对 Redis 应该有一个全局认识。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
NoSQL 算法 安全
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
188 0
|
6天前
|
缓存 NoSQL 安全
深入理解redis持久化—AOF日志
深入理解redis持久化—AOF日志
12 0
|
1月前
|
NoSQL 算法 关系型数据库
Redis宕机了,如何恢复数据
Redis持久化是为了防止服务宕机导致内存中数据丢失。主要有两种策略:RDB(快照)和AOF(仅追加文件)。RDB在指定时间间隔生成数据集快照,保存到磁盘,通过fork子进程实现,避免阻塞主线程,但可能丢失部分数据。AOF记录每次写操作,实时写回磁盘,提供秒级数据丢失保障,可通过不同同步策略平衡性能和安全性。AOF文件可定期重写以优化空间效率。混合使用RDB和AOF能在数据安全性和恢复速度上找到平衡。
|
12天前
|
NoSQL Redis
Redis主从结构,主库宕机,解决
Redis主从结构,主库宕机,解决
14 0
|
1月前
|
存储 运维 监控
|
1月前
|
监控 NoSQL 算法
宕机后,Redis如何实现快速恢复
宕机后,Redis如何实现快速恢复
62 2
|
1月前
|
存储 监控 NoSQL
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
72 0
|
1月前
|
存储 缓存 NoSQL
Redis内存数据满了导致宕机
Redis内存数据满了导致宕机
26 0
|
21天前
|
SQL 监控 关系型数据库
|
16天前
|
SQL 数据采集 DataWorks
DataWorks产品使用合集之pyodps的线程限制是什么意思
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。