为什么redis不能保证100%数据不丢失

为什么redis不能保证100%数据不丢失

起因

  前段时间面试被问到,Redis能否保证100%数据不丢失,我回答不能。面试官又问,为什么呢?我一时语塞,慌忙回答因为异步写盘。
  随后我在百度上搜了搜,发现很多博客都讲,将appendfsync值设置为always就可以了。这回答让我对《redis设计与实现》产生了怀疑。难道新版本的redis能够保证数据100%不丢失?!
  搜不到答案,自己找答案好了,于是开始阅读redis源码。

准备环境

  1. 下载source insight4.0软件,阅读c代码很方便。
  2. 下载redis4源码,解压,源码位于src下
  3. 附上redis源码结构

阅读源码

  1. 按照知乎上的源码结构,并未找到redis.c,应该是server.c
  2. 打开server.c,找到main函数为什么redis不能保证100%数据不丢失
    main函数最后,有调用事件循环的函数
    为什么redis不能保证100%数据不丢失
    我们进入aeMain函数看下为什么redis不能保证100%数据不丢失
      可以看到,是一个while循环,我们把这个循环叫做事件循环。在循环中做了两件事情,分别是执行beforesleepaeProcessEvents方法。
      重点关注下beforesleep方法,具体的执行函数在server.c中被设置进来aeSetBeforeSleepProc(server.el,beforeSleep);。所以到server.c中搜索beforeSleep方法。为什么redis不能保证100%数据不丢失
    注释写的很详细,在函数末尾,有flushAppendOnlyFile(0);的调用,会将aof buffer写到磁盘上。看一看flushAppendOnlyFile方法:
    为什么redis不能保证100%数据不丢失
      我们读一读函数注释,大体意思就是:先把写命令追加到aof buffer中,下一次进入事件循环循环后,再将buffer写到磁盘上。
      结合while循环处方法的调用顺序,可以看出确实是这样的。那么也就是说,这次写到磁盘上的内容是上一个事件循环产生的。看下flushAppendOnlyFile完整的代码:
    为什么redis不能保证100%数据不丢失为什么redis不能保证100%数据不丢失
      注释很详细,在方法最后部分,有if (server.aof_fsync == AOF_FSYNC_ALWAYS)的判断,如果条件符合,会使用fdatasync()的方法来写磁盘。
      到这里,我们可以肯定,redis即使在配制appendfsync=always的策略下,还是会丢失一个事件循环的数据,与《redis设计与实现》中的相关描述一致。