起因
前段時間面試被問到,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設計與實現》中的相關描述一致。