聊一聊Redis持久化開與關
本文轉載自微信公眾號「Redis開發運維實戰」,作者付磊 。轉載本文請聯系Redis開發運維實戰公眾號。
經常能碰到這樣的問題:
- A: Redis開持久化了嗎?
- B: 沒有
- A: 你們為什么不開?數據丟了怎么辦?數據不一致怎么辦?
Redis的持久化功能被夸大和誤解了,這個問題我解釋過無數遍了,早就想寫個說明了,今天“忍不了”,和大家聊聊我的一些認知,歡迎大佬們吐槽。
一、Redis”3“種持久化方式
1. RDB
簡單來說,對Redis做一個快照(利用fork)保存在磁盤上
(1) 優點:
- 結構緊湊體積小,加載速度快(相比AOF)
- 可以做定期備份:例如低峰期(順便搞個數據分析也行)
(2) 缺點:
- 動作大、消耗大:全量操作對于磁盤、CPU、內存等均有消耗
- 無法做到"實時"備份
- 格式多變(Redis 3 4 5 6版本多次修改)
2. AOF
簡單說把Redis的每條寫操作記錄到日志中,例如set hello world
- *3
- $3
- set
- $5
- hello
- $5
- world
簡單說,落盤策略有三種:
- appendfsync always:每次事件循環都進行一次同步操作(主線程)
- appendfsync everysec:每秒進行一次同步操作(另一個線程)
- appendfsync no:由操作系統控制同步操作(操作系統)
(1) 優點:
- RESP標準格式:無版本兼容性問題
- 實時性更高且成本較小
(2) 缺點:
- 體積大:協議 + 明文
- 加載慢:利用fakeclient做回放
- AOF重寫還是動作不小(本文不討論AOF重寫)
3. RDB-AOF混合
持久化文件全量使用RDB,增量使用AOF,保證體積、實時性、加載速度。(Redis 4提供,本文不討論)
二、開AOF性能會差多少?(注:AOF重寫也是資源消耗大頭,這里不討論)
1.測試環境:
- CPU: Intel(R) Xeon(R) Gold 6248 CPU @ 2.50GHz
- 機械磁盤
- Redis版本:4.0.14(未測試Redis 6多線程)
- 壓測工具:redis-benchmark
- AOF策略:appendfsync everysec
2.壓測方法:
測試Redis在”開和關“AOF情況下,在不同size(64字節、128字節、512字節)的OPS和耗時
(1) d=64字節
命令 | ops(開AOF) | ops(關AOF) | 耗時(開AOF) | 耗時(關AOF) |
---|---|---|---|---|
set | 97352 | 121624 | 100.00% <= 0 milliseconds(總:5.14s) | 100.00% <= 0 milliseconds(總:4.11s) |
get | 108979 | 109241 | 100.00% <= 0 milliseconds(總:4.59s) | 100.00% <= 0 milliseconds(總:4.58s) |
incr | 104755 | 113301 | 100.00% <= 0 milliseconds(總:4.77s) | 100.00% <= 0 milliseconds(總:4.41s) |
lpush | 95347 | 110889 | 100.00% <= 0 milliseconds(總:5.24s) | 100.00% <= 0 milliseconds(總:4.51s) |
hset | 97770 | 113791 | 100.00% <= 0 milliseconds(總:5.11s) | 100.00% <= 0 milliseconds(總:4.39s) |
(2) d=128字節
命令 | ops(開AOF) | ops(關AOF) | 耗時(開AOF) | 耗時(關AOF) |
---|---|---|---|---|
set | 108908 | 114077 | 100.00% <= 1 milliseconds 100.00% <= 2 milliseconds (總:4.59s) |
100.00% <= 0 milliseconds(總:4.38s) |
get | 107388 | 111756 | 100.00% <= 1 milliseconds(總:4.66s) | 100.00% <= 0 milliseconds(總:4.47s) |
incr | 105042 | 113430 | 100.00% <= 0 milliseconds(總:4.76s) | 100.00% <= 0 milliseconds(總:4.41s) |
lpush | 103114 | 114025 | 100.00% <= 0 milliseconds(總:4.85s) | 100.00% <= 0 milliseconds(總:4.39s) |
hset | 101440 | 113791 | 100.00% <= 1 milliseconds(總:4.93s) | 100.00% <= 1 milliseconds(總:4.93s) |
(3) d=512字節
命令 | ops(開AOF) | ops(關AOF) | 耗時(開AOF) | 耗時(關AOF) |
---|---|---|---|---|
set | 96581 | 108790 | 99.99% <= 1 milliseconds 99.99% <= 2 milliseconds 99.99% <= 3 milliseconds 99.99% <= 5 milliseconds 100.00% <= 6 milliseconds 100.00% <= 7 milliseconds 100.00% <= 8 milliseconds 100.00% <= 8 milliseconds (總:5.18s) |
100.00% <= 1 milliseconds(總:4.60s) |
get | 107898 | 105374 | 100.00% <= 0 milliseconds(總:4.63s) | 100.00% <= 0 milliseconds(總:4.74s) |
incr | 102438 | 107991 | 100.00% <= 0 milliseconds(總:4.88s) | 100.00% <= 0 milliseconds(總:4.63s) |
lpush | 93231 | 105064 | 99.98% <= 2 milliseconds 99.98% <= 3 milliseconds 99.99% <= 4 milliseconds 99.99% <= 5 milliseconds 99.99% <= 6 milliseconds 100.00% <= 7 milliseconds 100.00% <= 8 milliseconds 100.00% <= 8 milliseconds (總:5.36s) |
100.00% <= 0 milliseconds(總:4.76s) |
hset | 96955 | 108225 | 100.00% <= 6 milliseconds 100.00% <= 8 milliseconds 100.00% <= 9 milliseconds 100.00% <= 9 milliseconds (總:5.16s) |
100.00% <= 0 milliseconds(總:4.62s) |
3. 總結說明:(注意此處沒有考慮AOF重寫,只能更差)
(1) 開啟AOF后,Redis的寫性能下降了8~25%,讀性能未下降(注意此處測試為非讀寫混合場景)
(2) 開啟AOF后,隨著數據量的增加相關讀寫性能會下降。
(3) 開啟AOF后,實際測試中發現單核CPU也會少量上漲。
三、一些問題的討論?
1. aof刷盤策略改為always能保證不丟數據嗎?
(1) 答案
會丟。Redis執行一條寫入命令時,會將數據寫入aof_buf,但寫入aof_buf和刷盤還是存在一次事件時間差。
(2) 原理:
Redis處理命令(server.c)processCommand->call(執行命令),其中包含
- void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc, int flags)
- {
- //寫入到aof_buf中
- if (server.aof_state != AOF_OFF && flags & PROPAGATE_AOF)
- feedAppendOnlyFile(cmd,dbid,argv,argc);
- ......
- }
每次文件事件前的beforesleep(ae.c)
- void aeMain(aeEventLoop *eventLoop) {
- eventLoop->stop = 0;
- while (!eventLoop->stop) {
- if (eventLoop->beforesleep != NULL)
- eventLoop->beforesleep(eventLoop);
- aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
- }
- }
其中beforesleep包含了aof_buf落盤(server.c)
- void beforeSleep(struct aeEventLoop *eventLoop) {
- ......
- //aof落盤
- flushAppendOnlyFile(0);
- ......
- }
flushAppendOnlyFile利用操作系統的write和fsync(加上aof的三種策略)完成落盤:
2.Redis是什么一致性?
最終一致性。客戶端寫主后,不等從寫完。(為什么這樣?Redis設計目標是什么?快!)
3.master節點故障后Redis怎么恢復?
(1) 關閉AOF:B節點晉升成主節點,對外提供服務。A節點恢復后變為slave,依賴全量復制獲取全部數據
(2) 開啟AOF:同上...(只不過A節點全量復制后做一次AOF重寫)
所以看起來此模式下,對于故障恢復持久化沒什么用!
4.我就不想丟理論上最少的數據,怎么辦?
開啟always,不用主從切換,等待A節點恢復,重新加載AOF在提供服務,老哥這個現實嗎???
五、最佳實踐?
1.RDB最佳
(1) 自動save:關、關、關(性能殺手)
(2) save命令:同步,忘記它(除非你一點內存沒有了,還需要RDB)
(3) bgsave命令:備份可以用,請關注fork時間(info stats可查)
(4) 關閉掉:做不到,因為全量復制默認會用。
2.AOF最佳
(1) always不要用(主線程執行、以及IO影響)
(2) everysec、no按需使用,如果僅僅想不丟數據,AOF做不到。
(3) 除非怕主從都掛了,可以考慮。
不要忘記AOF臭名昭著的:
- 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
3.單機多實例:你玩得起嗎?
以一個80 core,500G內存的機器為例子,出于成本考慮至少要部署80~90個* memory(5G)的實例。在此場景下CPU、內存、網絡的開銷基本還能控制,但如果開了AOF,用普通的磁盤,行不行?
- 會有:很多公司會為了做持久化,預留“一半”內存,所以就是40~45個
- 還有:用SSD解決AOF的問題。
4. "旁門左道"用法
(1) RDB:
定期備份(例如低峰期)、數據分析(分析出bigkey、hotkey、idlekey等)(阿里云的做法)
(2) AOF
原生AOF語義太弱,如果想做類似binlog功能可以對Redis內核進行修改,多機房同步會用上。
六、總結
Redis的持久化功能是一個重要功能,但如果想指望它實現“不丟數據”、“一致性”,那可能帶來的就是:低性能、高成本。
有時間整理下Redis3~6 AOF的一些變化,附圖一張:繼續搬磚去了(現在實例已經130萬了。。)