04 | AOF日志:宕机了,Redis如何避免数据丢失?
04 | AOF日志:宕机了,Redis如何避免数据丢失?
讲述:蒋德钧
时长14:40大小13.45M
AOF 日志是如何实现的?
三种写回策略
日志文件太大了怎么办?
AOF 重写会阻塞吗?
小结
每课一问
赞 208
提建议
精选留言(177)
- Kaito置顶2020-08-12问题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时是建议关掉的。
共 99+ 条评论1205 - 注定非凡2020-08-141,作者讲了什么? 本章讲了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配置,但现在要学习,争取将来可以直接操作展开共 3 条评论107
- 天天向上2020-08-29什么时候会触发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的留言
92 - GEEKBANG_30367602020-08-29为了减小aof文件的体量,可以手动发送“bgrewriteaof”指令,通过子进程生成更小体积的aof,然后替换掉旧的、大体量的aof文件。 也可以配置自动触发 1)auto-aof-rewrite-percentage 100 2)auto-aof-rewrite-min-size 64mb 这两个配置项的意思是,在aof文件体量超过64mb,且比上次重写后的体量增加了100%时自动触发重写。我们可以修改这些参数达到自己的实际要求展开67
- 徐鹏2020-08-12有几个问题想请教一下: 1、文中多处提到bgrewriteaof 子进程,这个有点迷糊,主线程fork出来的bgrewriteaof是子线程还是子进程? 2、AOF重写会拷贝一份完整的内存数据,这个会导致内存占用直接翻倍吗? 3、如果一个key设置了过期时间,在利用AOF文件恢复数据时,key已经过期了这个是如何处理的呢?展开共 12 条评论46
- Darren2020-08-12AOF工作原理: 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. 至于课后问题,看了 @与路同飞 童鞋的答案,没有更好的答案,就不回答了展开共 6 条评论40
- 曾轼麟2020-08-12问题一:在AOF重写期间,Redis运行的命令会被积累在缓冲区,待AOF重写结束后会进行回放,在高并发情况下缓冲区积累可能会很大,这样就会导致阻塞,Redis后来通过Linux管道技术让aof期间就能同时进行回放,这样aof重写结束后只需要回放少量剩余的数据即可 问题二:对于任何文件系统都是不推荐并发修改文件的,例如hadoop的租约机制,Redis也是这样,避免重写发生故障,导致文件格式错乱最后aof文件损坏无法使用,所以Redis的做法是同时写两份文件,最后通过修改文件名的方式,保证文件切换的原子性 这里需要纠正一下老师前面的口误,就是Redis是通过使用操作系统的fork()方式创建进程,不是线程,也由于这个原因,主进程和fork出来的子进程的资源是不共享的,所以也出现Redis使用pipe管道技术来同步主子进程的aof增量数据展开共 4 条评论18
- D2020-08-12AOF 是什么的缩写, 还是说就是这个名字?
作者回复: AOF的全称是Append Only File,表示文件只能追加写。 Redis记日志时,就是用追加写文件的方式记录写命令操作的。
共 4 条评论15 - 脱缰的野马__2020-08-12文章前面说到redo log日志记录的是修改后的数据,但是在丁奇老师的MySQL实战中讲解的是redo log记录是对数据的操作记录,修改后的数据是保存在内存的change buffer中的共 3 条评论13
- Archer302020-08-12问题1回答:如果子进程写入事件过长,并且这段事件,会导致AOF重写日志,积累过多,当新的AOF文件完成后,还是需要写入大量AOF重写日志里的内容,可能会导致阻塞。 问题2回答:我觉得评论区里的大部分回答 防止锁竞争 ,应该是把问题理解错了,父子两个进程本来就没有需要竞争的数据,老师所指的两个日志应该是“AOF缓冲区”和"AOF重写缓冲区",而不是磁盘上的AOF文件,之所有另外有一个"AOF重写缓冲区",是因为重写期间,主进程AOF还在继续工作,还是会同步到旧的AOF文件中,同步成功后,“AOF缓冲区”会被清除,会被清除,会被清除!展开共 4 条评论9
- 扩散性百万咸面包2020-08-12图是不是画错了。为什么主线程和AOF重写缓冲连起来了呢?不是应该bgrewriteaof来写吗?
作者回复: 主线程收到写命令后,会把这个写命令写入AOF重写缓冲区,这是由主线程来写的,所以连线在一起了:) bgrewriteaof主要是写新日志的。
共 3 条评论9 - 悟空聊架构2021-04-23Always,everysec,No,这三种模式就是 CAP 理论的体现。共 3 条评论8
- ruier2020-08-12有个Django写的Redis管理小系统,有兴趣的朋友可以看一看,名字叫repoll https://github.com/NaNShaner/repoll7
- Daiver2020-10-10AOF重写过程,主线程接收到了新的请求,并将日志写入缓冲区,如果宕机了,缓冲区的内容还是会丢失的。子进程在写日志时,重写缓冲区也是可能会丢失的,为啥说AOF日志还是齐全的,怎么可以用于恢复呢?共 1 条评论6
- Java垒墙工程师2020-09-03试听完了,彻底入坑6
- 杨小羊快跑2020-09-23开启AOF,有可能导致Redis hang住。日志里也有体现:Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis 主要原因:Linux规定执行write(2)时,如果对同一个文件正在执行fdatasync(2)将kernel buffer写入物理磁盘,或者有system wide sync在执行,write(2)会被Block住,整个Redis被Block住。(注:Redis处理完每个事件后都会调用write(2)将变化写入kernel的buffer)展开共 2 条评论5
- 与路同飞2020-08-121.子线程重新AOF日志完成时会向主线程发送信号处理函数,会完成 (1)将AOF重写缓冲区的内容写入到新的AOF文件中。(2)将新的AOF文件改名,原子地替换现有的AOF文件。完成以后才会重新处理客户端请求。 2.不共享AOF本身的日志是防止锁竞争,类似于redis rehash。共 6 条评论5
- 随机漫步的傻瓜2021-09-23感觉老师没有刻意把进程和线程分的特别清楚4
- CityAnimal2021-01-12笔记打卡 * [ ] 原理 * [ ] 写后日志 * [ ] 好处: * [ ] 避免出现记录错误命令的情况 * [ ] 不会阻塞当前的写操作 * [ ] 风险 * [ ] 执行完命令后宕机 => 数据丢失 * [ ] 给下一个操作带来阻塞风险 * [ ] AOF 日志也是在主线程中执行的 * [ ] 内容:收到的每一条命令,以文本形式保存 * [ ] 命令:set testkey testvalue * [ ] aof: *3\n$3\nset\n$7\ntestkey\n$9\ntestvalue * [ ] *3 : 当前命令有三个部分 * [ ] 每部分都是由“$+数字”开头,后面紧跟着具体的命令、键或值 * [ ] 写回策略 <= 配置项 appendfsync * [ ] Always(同步写回) * [ ] 基本不丢数据 * [ ] 影响主线程性能 * [ ] Everysec(每秒写回) * [ ] 性能适中 * [ ] 如果发生宕机,上一秒内未落盘的命令操作仍然会丢失 * [ ] No(操作系统控制的写回) * [ ] 性能好 * [ ] 一旦宕机前AOF 记录没有落盘,对应的数据就丢失了 * [ ] AOF 文件过大 * [ ] 性能问题 * [ ] 文件系统对文件大小有限制 * [ ] 追加命令记录效率低 * [ ] 故障恢复非常缓慢 * [ ] 方案:AOF 重写机制 * [ ] 原理:根据redis的现状创建一个新的 AOF 文件 * [ ] !!! 非常耗时 (不阻塞主线程) * [ ] 由后台子进程 bgrewriteaof 来完成的 * [ ] 一个拷贝 * [ ] 主线程 fork 出 bgrewriteaof 子进程,并将内存拷贝给 bgrewriteaof * [ ] 两处日志 * [ ] 正在使用的 AOF 日志 * [ ] 新的 AOF 重写日志展开4
- Anony2020-08-12上节说redis单线程处理网络IO和键值对的读写,持久化是由额外线程处理的。那AOF由额外线程处理的话,为什么会影响主线程呢?和主线程之间是什么关系?共 3 条评论4