Redis 核心技术与实战
蒋德钧
中科院计算所副研究员
81696 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 53 讲
开篇词 (1讲)
实践篇 (28讲)
Redis 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

04 | AOF日志:宕机了,Redis如何避免数据丢失?

一个拷贝,两处日志
多变一功能
根据数据库的现状创建新的AOF文件
控制AOF文件过大带来的性能问题
落盘的时机不在Redis手中
操作系统控制的写回
一秒内未落盘的命令操作仍然会丢失
避免性能开销
每秒写回一次
慢速的落盘操作
基本不丢数据
No
Everysec
Always
不阻塞当前的写操作
避免记录错误命令
AOF重写的重写日志
AOF日志重写的潜在阻塞风险
AOF重写机制避免了日志文件过大的性能问题
三种写回策略体现了trade-off原则
AOF方法保证了数据的可靠性
AOF重写会阻塞吗?
重写机制的实现
重写机制的作用
No
Everysec
Always
三种写回策略
写后日志的好处
记录Redis收到的每一条命令
写后日志
每课一问
小结
AOF重写机制
三种写回策略
AOF日志是如何实现的?
AOF日志

该思维导图由 AI 生成,仅供参考

你好,我是蒋德钧。
如果有人问你:“你会把 Redis 用在什么业务场景下?”我想你大概率会说:“我会把它当作缓存使用,因为它把后端数据库中的数据存储在内存中,然后直接从内存中读取数据,响应速度会非常快。”没错,这确实是 Redis 的一个普遍使用场景,但是,这里也有一个绝对不能忽略的问题:一旦服务器宕机,内存中的数据将全部丢失。
我们很容易想到的一个解决方案是,从后端数据库恢复这些数据,但这种方式存在两个问题:一是,需要频繁访问数据库,会给数据库带来巨大的压力;二是,这些数据是从慢速数据库中读取出来的,性能肯定比不上从 Redis 中读取,导致使用这些数据的应用程序响应变慢。所以,对 Redis 来说,实现数据的持久化,避免从后端数据库中进行恢复,是至关重要的。
目前,Redis 的持久化主要有两大机制,即 AOF(Append Only File)日志和 RDB 快照。在接下来的两节课里,我们就分别学习一下吧。这节课,我们先重点学习下 AOF 日志。

AOF 日志是如何实现的?

说到日志,我们比较熟悉的是数据库的写前日志(Write Ahead Log, WAL),也就是说,在实际写数据前,先把修改的数据记到日志文件中,以便故障时进行恢复。不过,AOF 日志正好相反,它是写后日志,“写后”的意思是 Redis 是先执行命令,把数据写入内存,然后才记录日志,如下图所示:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Redis AOF(Append Only File)日志是一种用于避免数据丢失的持久化机制,通过逐一记录操作命令,保证数据的可靠性。AOF日志采用写后日志方式,避免记录错误命令,同时不会阻塞当前写操作。为解决数据丢失和主线程阻塞风险,AOF提供了三种写回策略:同步写回、每秒写回和操作系统控制的写回。此外,AOF提供了重写机制来控制文件大小,通过后台线程完成,避免了对主线程的阻塞。虽然AOF重写后,日志文件会缩小,但是,要把整个数据库的最新数据的操作日志都写回磁盘,仍然是一个非常耗时的过程。AOF重写过程由后台子进程完成,不会阻塞主线程,保证了数据库性能。总结来说,AOF提供了三种写回策略,体现了系统设计中的取舍原则,即在性能和可靠性保证之间做取舍。同时,AOF重写机制通过后台线程完成,避免了对主线程的阻塞,保证了数据库性能。文章还提出了两个问题,引发读者思考和讨论。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 核心技术与实战》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(190)

  • 最新
  • 精选
  • Kaito
    置顶
    问题1,Redis采用fork子进程重写AOF文件时,潜在的阻塞风险包括:fork子进程 和 AOF重写过程中父进程产生写入的场景,下面依次介绍。 a、fork子进程,fork这个瞬间一定是会阻塞主线程的(注意,fork时并不会一次性拷贝所有内存数据给子进程,老师文章写的是拷贝所有内存数据给子进程,我个人认为是有歧义的),fork采用操作系统提供的写实复制(Copy On Write)机制,就是为了避免一次性拷贝大量内存数据给子进程造成的长时间阻塞问题,但fork子进程需要拷贝进程必要的数据结构,其中有一项就是拷贝内存页表(虚拟内存和物理内存的映射索引表),这个拷贝过程会消耗大量CPU资源,拷贝完成之前整个进程是会阻塞的,阻塞时间取决于整个实例的内存大小,实例越大,内存页表越大,fork阻塞时间越久。拷贝内存页表完成后,子进程与父进程指向相同的内存地址空间,也就是说此时虽然产生了子进程,但是并没有申请与父进程相同的内存大小。那什么时候父子进程才会真正内存分离呢?“写实复制”顾名思义,就是在写发生时,才真正拷贝内存真正的数据,这个过程中,父进程也可能会产生阻塞的风险,就是下面介绍的场景。 b、fork出的子进程指向与父进程相同的内存地址空间,此时子进程就可以执行AOF重写,把内存中的所有数据写入到AOF文件中。但是此时父进程依旧是会有流量写入的,如果父进程操作的是一个已经存在的key,那么这个时候父进程就会真正拷贝这个key对应的内存数据,申请新的内存空间,这样逐渐地,父子进程内存数据开始分离,父子进程逐渐拥有各自独立的内存空间。因为内存分配是以页为单位进行分配的,默认4k,如果父进程此时操作的是一个bigkey,重新申请大块内存耗时会变长,可能会产阻塞风险。另外,如果操作系统开启了内存大页机制(Huge Page,页面大小2M),那么父进程申请内存时阻塞的概率将会大大提高,所以在Redis机器上需要关闭Huge Page机制。Redis每次fork生成RDB或AOF重写完成后,都可以在Redis log中看到父进程重新申请了多大的内存空间。 问题2,AOF重写不复用AOF本身的日志,一个原因是父子进程写同一个文件必然会产生竞争问题,控制竞争就意味着会影响父进程的性能。二是如果AOF重写过程中失败了,那么原本的AOF文件相当于被污染了,无法做恢复使用。所以Redis AOF重写一个新文件,重写失败的话,直接删除这个文件就好了,不会对原先的AOF文件产生影响。等重写完成之后,直接替换旧文件即可。

    作者回复: 非常赞!这个回答一定要置顶! 而且Kaito同学的不少问题回答都非常仔细和精彩,非常值得一读! 这里要谢谢Kaito同学指出的文章中的歧义:fork子进程时,子进程是会拷贝父进程的页表,即虚实映射关系,而不会拷贝物理内存。子进程复制了父进程页表,也能共享访问父进程的内存数据了,此时,类似于有了父进程的所有内存数据。我的描述不太严谨了,非常感谢指出! Kaito同学还提到了Huge page。这个特性大家在使用Redis也要注意。Huge page对提升TLB命中率比较友好,因为在相同的内存容量下,使用huge page可以减少页表项,TLB就可以缓存更多的页表项,能减少TLB miss的开销。 但是,这个机制对于Redis这种喜欢用fork的系统来说,的确不太友好,尤其是在Redis的写入请求比较多的情况下。因为fork后,父进程修改数据采用写时复制,复制的粒度为一个内存页。如果只是修改一个256B的数据,父进程需要读原来的内存页,然后再映射到新的物理地址写入。一读一写会造成读写放大。如果内存页越大(例如2MB的大页),那么读写放大也就越严重,对Redis性能造成影响。 Huge page在实际使用Redis时是建议关掉的。

    2020-08-12
    156
    1314
  • 天天向上
    什么时候会触发AOF 重写呢?

    作者回复: 有两个配置项在控制AOF重写的触发时机: 1. auto-aof-rewrite-min-size: 表示运行AOF重写时文件的最小大小,默认为64MB 2. auto-aof-rewrite-percentage: 这个值的计算方法是:当前AOF文件大小和上一次重写后AOF文件大小的差值,再除以上一次重写后AOF文件大小。也就是当前AOF文件比上一次重写后AOF文件的增量大小,和上一次重写后AOF文件大小的比值。 AOF文件大小同时超出上面这两个配置项时,会触发AOF重写。 也可以看下@GEEKBANG_3036760的留言

    2020-08-29
    3
    102
  • D
    AOF 是什么的缩写, 还是说就是这个名字?

    作者回复: AOF的全称是Append Only File,表示文件只能追加写。 Redis记日志时,就是用追加写文件的方式记录写命令操作的。

    2020-08-12
    5
    15
  • 扩散性百万咸面包
    图是不是画错了。为什么主线程和AOF重写缓冲连起来了呢?不是应该bgrewriteaof来写吗?

    作者回复: 主线程收到写命令后,会把这个写命令写入AOF重写缓冲区,这是由主线程来写的,所以连线在一起了:) bgrewriteaof主要是写新日志的。

    2020-08-12
    3
    10
  • 漂泊者及其影子
    问个问题:redis同时开启RDB和AOF,redis重启基于哪个文件进行数据恢复

    作者回复: 同时开启RDB和AOF时,会优先使用AOF文件进行恢复,因为相比RDB,AOF文件保存的命令操作通常更全些。

    2020-10-30
    4
  • 五角场撸个串
    一口气看了好多节课,感谢老师的用心准备。老师的课从问题出发,从表面到原理,循序渐进,通俗易懂。对比其他有些课程,要么浮于表面,要么晦涩难懂。希望老师能够再出更多课程,谢谢。

    作者回复: 谢谢,一起努力!

    2020-12-10
    1
  • 注定非凡
    1,作者讲了什么? 本章讲了Redis两种持久化机制之一:AOF机制原理 aof日志记录了redis所有增删改的操作,保存在磁盘上,当redis宕机,需要恢复内存中的数据时,可以通过读取aop日志恢复数据,从而避免因redis异常导致的数据丢失 2,作者是怎么把这事给讲明白的? (1)作者先讲述redis宕机会导致内存数据丢失,需要有一种机制在redis重启后恢复数据。 (2)介绍了AOF通过记录每一个对redis数据进行增删改的操作日志,可以实现这种功能 (2)介绍了AOF的运行机制,数据保存机制,以及由此带来的优点和缺点 3,为了讲明白,作者讲了哪些要点,有哪些亮点? (1)亮点:记录操作的时机分为:“写前日志和写后日志”,这个是我之前所不知道的 (2)要点1:AOF是写后日志,这样带来的好处是,记录的所有操作命令都是正确的,不需要额外的语法检查,确保redis重启时能够正确的读取回复数据 (3)要点2:AOF日志写入磁盘是比较影响性能的,为了平衡性能与数据安全,开发了三种机制:①:立即写入②:按秒写入③:系统写入 (4)要点3:AOF日志会变得巨大,所以Redis提供了日志重整的机制,通过读取内存中的数据重新产生一份数据写入日志 4,对于作者所讲,我有哪些发散性的思考? 作者说系统设计“取舍”二字非常重要,这是我之前未曾意识到的。作者讲了fork子进程机制,是Linux系统的一个能力,在刘超的课中讲过,这鼓舞了我继续学习的信心 5,将来有哪些场景,我可以应用上它? 目前还没有机会直接操作生产的redis配置,但现在要学习,争取将来可以直接操作
    2020-08-14
    3
    115
  • GEEKBANG_3036760
    为了减小aof文件的体量,可以手动发送“bgrewriteaof”指令,通过子进程生成更小体积的aof,然后替换掉旧的、大体量的aof文件。 也可以配置自动触发   1)auto-aof-rewrite-percentage 100   2)auto-aof-rewrite-min-size 64mb   这两个配置项的意思是,在aof文件体量超过64mb,且比上次重写后的体量增加了100%时自动触发重写。我们可以修改这些参数达到自己的实际要求
    2020-08-29
    1
    74
  • 徐鹏
    有几个问题想请教一下: 1、文中多处提到bgrewriteaof 子进程,这个有点迷糊,主线程fork出来的bgrewriteaof是子线程还是子进程? 2、AOF重写会拷贝一份完整的内存数据,这个会导致内存占用直接翻倍吗? 3、如果一个key设置了过期时间,在利用AOF文件恢复数据时,key已经过期了这个是如何处理的呢?
    2020-08-12
    13
    49
  • Darren
    AOF工作原理: 1、Redis 执行 fork() ,现在同时拥有父进程和子进程。 2、子进程开始将新 AOF 文件的内容写入到临时文件。 3、对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。 4、当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。 5、搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。 试着讨论下留言童鞋的几个问题 一、其中老师在文中提到:“因为主线程未阻塞,仍然可以处理新来的操作。此时,如果有写操作,第一处日志就是指正在使用的 AOF 日志,Redis 会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。” 这里面说到 “Redis 会把这个操作写到它的缓冲区,这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的”,其实对于有些人有理解起来可能不是那么好理解,因为写入缓冲区为什么还不都是数据; 我的理解其实这个就是写入缓冲区,只不过是由appendfsync策略决定的,所以说的不丢失数据指的是不会因为子进程额外丢失数据。 二、AOF重新只是回拷贝引用(指针),不会拷贝数据本身,因此量其实不大,那写入的时候怎么办呢,写时复制,即新开辟空间保存修改的值,因此需要额外的内存,但绝对不是redis现在占有的2倍。 三、AOF对于过期key不会特殊处理,因为Redis keys过期有两种方式:被动和主动方式。 当一些客户端尝试访问它时,key会被发现并主动的过期。 当然,这样是不够的,因为有些过期的keys,永远不会访问他们。 无论如何,这些keys应该过期,所以定时随机测试设置keys的过期时间。所有这些过期的keys将会从删除。 具体就是Redis每秒10次做的事情: 测试随机的20个keys进行相关过期检测。 删除所有已经过期的keys。 如果有多于25%的keys过期,重复步奏1. 至于课后问题,看了 @与路同飞 童鞋的答案,没有更好的答案,就不回答了
    2020-08-12
    7
    45
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部