起因
前段时间面试被问到,Redis能否保证100%数据不丢失,我回答不能。面试官又问,为什么呢?我一时语塞,慌忙回答因为异步写盘。
随后我在百度上搜了搜,发现很多博客都讲,将appendfsync值设置为always就可以了。这回答让我对《redis设计与实现》产生了怀疑。难道新版本的redis能够保证数据100%不丢失?!
搜不到答案,自己找答案好了,于是开始阅读redis源码。
准备环境
阅读源码
- 按照知乎上的源码结构,并未找到redis.c,应该是server.c
- 打开server.c,找到main函数
main函数最后,有调用事件循环
的函数
我们进入aeMain函数看下
可以看到,是一个while循环,我们把这个循环叫做事件循环
。在循环中做了两件事情,分别是执行beforesleep
和aeProcessEvents
方法。
重点关注下beforesleep方法,具体的执行函数在server.c中被设置进来aeSetBeforeSleepProc(server.el,beforeSleep);
。所以到server.c中搜索beforeSleep
方法。
注释写的很详细,在函数末尾,有flushAppendOnlyFile(0);
的调用,会将aof buffer写到磁盘上。看一看flushAppendOnlyFile方法:
我们读一读函数注释,大体意思就是:先把写命令追加到aof buffer中,下一次进入事件循环循环后,再将buffer写到磁盘上。
结合while循环处方法的调用顺序,可以看出确实是这样的。那么也就是说,这次写到磁盘上的内容是上一个事件循环产生的。看下flushAppendOnlyFile完整的代码:
注释很详细,在方法最后部分,有if (server.aof_fsync == AOF_FSYNC_ALWAYS)
的判断,如果条件符合,会使用fdatasync()
的方法来写磁盘。
到这里,我们可以肯定,redis即使在配制appendfsync=always
的策略下,还是会丢失一个事件循环的数据
,与《redis设计与实现》中的相关描述一致。