微信搜索關注「水滴與銀彈」公衆號,第一時間獲取優質技術乾貨。7年資深後端研發,給你呈現不同的技術視角。程序員
你們好,我是 Kaito。shell
這篇文章,我想和你聊一聊在使用 Redis 時,可能會踩到的「坑」。數據庫
若是你在使用 Redis 時,也遇到過如下這些「詭異」的場景,那很大機率是踩到「坑」了:後端
到底是什麼緣由,致使的這些問題呢?緩存
這篇文章,我就來和你盤點一下,使用 Redis 時可能會踩到「坑」,以及如何去規避。安全
我把這些問題劃分紅了三大部分:微信
致使這些問題的緣由,頗有可能會「顛覆」你的認知,若是你準備好了,那就跟着個人思路開始吧!markdown
這篇文章乾貨不少,但願你能夠耐心讀完。app
首先,咱們來看一下,平時在使用 Redis 時,有哪些常見的命令會遇到「意料以外」的結果。運維
1) 過時時間意外丟失?
你在使用 Redis 時,確定常用 SET 命令,它很是簡單。
SET 除了能夠設置 key-value 以外,還能夠設置 key 的過時時間,就像下面這樣:
127.0.0.1:6379> SET testkey val1 EX 60
OK
127.0.0.1:6379> TTL testkey
(integer) 59
複製代碼
此時若是你想修改 key 的值,但只是單純地使用 SET 命令,而沒有加上「過時時間」的參數,那這個 key 的過時時間將會被「擦除」。
127.0.0.1:6379> SET testkey val2
OK
127.0.0.1:6379> TTL testkey // key永遠不過時了!
(integer) -1
複製代碼
看到了麼?testkey 變成永遠不過時了!
若是你剛剛開始使用 Redis,相信你確定也踩過這個坑。
致使這個問題的緣由在於:SET 命令若是不設置過時時間,那麼 Redis 會自動「擦除」這個 key 的過時時間。
若是你發現 Redis 的內存持續增加,並且不少 key 原來設置了過時時間,後來發現過時時間丟失了,頗有多是由於這個緣由致使的。
這時你的 Redis 中就會存在大量不過時的 key,消耗過多的內存資源。
因此,你在使用 SET 命令時,若是剛開始就設置了過時時間,那麼以後修改這個 key,也務必要加上過時時間的參數,避免過時時間丟失問題。
2) DEL 居然也會阻塞 Redis?
刪除一個 key,你確定會用 DEL 命令,不知道你沒有思考過它的時間複雜度是多少?
O(1)?其實不必定。
若是你有認真閱讀 Redis 的官方文檔,就會發現:刪除一個 key 的耗時,與這個 key 的類型有關。
Redis 官方文檔在介紹 DEL 命令時,是這樣描述的:
也就是說,若是你要刪除的是一個非 String 類型的 key,這個 key 的元素越多,那麼在執行 DEL 時耗時就越久!
爲何會這樣?
緣由在於,刪除這種 key 時,Redis 須要依次釋放每一個元素的內存,元素越多,這個過程就會越耗時。
而這麼長的操做耗時,勢必會阻塞整個 Redis 實例,影響 Redis 的性能。
因此,當你在刪除 List/Hash/Set/ZSet 類型的 key 時,必定要格外注意,不能無腦執行 DEL,而是應該用如下方式刪除:
瞭解了 DEL 對於 List/Hash/Set/ZSet 類型數據的影響,咱們再來分析下,刪除一個 String 類型的 key 會不會有這種問題?
啊?前面不是提到,Redis 官方文檔的描述,刪除 String 類型的 key,時間複雜度是 O(1) 麼?這不會致使 Redis 阻塞吧?
其實這也不必定!
你思考一下,若是這個 key 佔用的內存很是大呢?
例如,這個 key 存儲了 500MB 的數據(很明顯,它是一個 bigkey),那在執行 DEL 時,耗時依舊會變長!
這是由於,Redis 釋放這麼大的內存給操做系統,也是須要時間的,因此操做耗時也會變長。
因此,對於 String 類型來講,你最好也不要存儲過大的數據,不然在刪除它時,也會有性能問題。
此時,你可能會想:Redis 4.0 不是推出了 lazy-free 機制麼?打開這個機制,釋放內存的操做會放到後臺線程中執行,那是否是就不會阻塞主線程了?
這個問題很是好。
真的會是這樣嗎?
這裏我先告訴你結論:即便 Redis 打開了 lazy-free,在刪除一個 String 類型的 bigkey 時,它仍舊是在主線程中處理,而不是放到後臺線程中執行。因此,依舊有阻塞 Redis 的風險!
這是爲何?
這裏先賣一個關子,感興趣的同窗能夠先自行查閱 lazy-free 相關資料尋找答案。:)
其實,關於 lazy-free 的知識點也不少,因爲篇幅緣由,因此我打算後面專門寫一篇文章來說,歡迎持續關注~
3) RANDOMKEY 居然也會阻塞 Redis?
若是你想隨機查看 Redis 中的一個 key,一般會使用 RANDOMKEY 這個命令。
這個命令會從 Redis 中「隨機」取出一個 key。
既然是隨機,那這個執行速度確定很是快吧?
其實否則。
要解釋清楚這個問題,就要結合 Redis 的過時策略來說。
若是你對 Redis 的過時策略有所瞭解,應該知道 Redis 清理過時 key,是採用定時清理 + 懶惰清理 2 種方式結合來作的。
而 RANDOMKEY 在隨機拿出一個 key 後,首先會先檢查這個 key 是否已過時。
若是該 key 已通過期,那麼 Redis 會刪除它,這個過程就是懶惰清理。
但清理完了還不能結束,Redis 還要找出一個「不過時」的 key,返回給客戶端。
此時,Redis 則會繼續隨機拿出一個 key,而後再判斷是它否過時,直到找出一個未過時的 key 返回給客戶端。
整個流程就是這樣的:
但這裏就有一個問題了:若是此時 Redis 中,有大量 key 已通過期,但還將來得及被清理掉,那這個循環就會持續好久才能結束,並且,這個耗時都花費在了清理過時 key + 尋找不過時 key 上。
致使的結果就是,RANDOMKEY 執行耗時變長,影響 Redis 性能。
以上流程,實際上是在 master 上執行的。
若是在 slave 上執行 RANDOMEKY,那麼問題會更嚴重!
爲何?
主要緣由就在於,slave 本身是不會清理過時 key。
那 slave 何時刪除過時 key 呢?
其實,當一個 key 要過時時,master 會先清理刪除它,以後 master 向 slave 發送一個 DEL 命令,告知 slave 也刪除這個 key,以此達到主從庫的數據一致性。
仍是一樣的場景:Redis 中存在大量已過時,但還未被清理的 key,那在 slave 上執行 RANDOMKEY 時,就會發生如下問題:
也就是說,在 slave 上執行 RANDOMKEY,有可能會形成整個 Redis 實例卡死!
是否是沒想到?在 slave 上隨機拿一個 key,居然有可能形成這麼嚴重的後果?
這實際上是 Redis 的一個 Bug,這個 Bug 一直持續到 5.0 才被修復。
修復的解決方案是,在 slave 上執行 RANDOMKEY 時,會先判斷整個實例全部 key 是否都設置了過時時間,若是是,爲了不長時間找不到符合條件的 key,slave 最多隻會在哈希表中尋找 100 次,不管是否能找到,都會退出循環。
這個方案就是增長上了一個最大重試次數,這樣一來,就避免了陷入死循環。
雖然這個方案能夠避免了 slave 陷入死循環、卡死整個實例的問題,可是,在 master 上執行這個命令時,依舊有機率致使耗時變長。
因此,你在使用 RANDOMKEY 時,若是發現 Redis 發生了「抖動」,頗有多是由於這個緣由致使的!
4) O(1) 複雜度的 SETBIT,居然會致使 Redis OOM?
在使用 Redis 的 String 類型時,除了直接寫入一個字符串以外,還能夠把它當作 bitmap 來用。
具體來說就是,咱們能夠把一個 String 類型的 key,拆分紅一個個 bit 來操做,就像下面這樣:
127.0.0.1:6379> SETBIT testkey 10 1
(integer) 1
127.0.0.1:6379> GETBIT testkey 10
(integer) 1
複製代碼
其中,操做的每個 bit 位叫作 offset。
可是,這裏有一個坑,你須要注意起來。
若是這個 key 不存在,或者 key 的內存使用很小,此時你要操做的 offset 很是大,那麼 Redis 就須要分配「更大的內存空間」,這個操做耗時就會變長,影響性能。
因此,當你在使用 SETBIT 時,也必定要注意 offset 的大小,操做過大的 offset 也會引起 Redis 卡頓。
這種類型的 key,也是典型的 bigkey,除了分配內存影響性能以外,在刪除它時,耗時一樣也會變長。
5) 執行 MONITOR 也會致使 Redis OOM?
這個坑你確定據說過不少次了。
當你在執行 MONITOR 命令時,Redis 會把每一條命令寫到客戶端的「輸出緩衝區」中,而後客戶端從這個緩衝區讀取服務端返回的結果。
可是,若是你的 Redis QPS 很高,這將會致使這個輸出緩衝區內存持續增加,佔用 Redis 大量的內存資源,若是剛好你的機器的內存資源不足,那 Redis 實例就會面臨被 OOM 的風險。
因此,你須要謹慎使用 MONITOR,尤爲在 QPS 很高的狀況下。
以上這些問題場景,都是咱們在使用常見命令時發生的,並且,極可能都是「無心」就會觸發的。
下面咱們來看 Redis「數據持久化」都存在哪些坑?
Redis 的數據持久化,分爲 RDB 和 AOF 兩種方式。
其中,RDB 是數據快照,而 AOF 會記錄每個寫命令到日誌文件中。
在數據持久化方面發生問題,主要也集中在這兩大塊,咱們依次來看。
1) master 宕機,slave 數據也丟失了?
若是你的 Redis 採用以下模式部署,就會發生數據丟失的問題:
若是此時 master 宕機,就會致使下面的問題:
看到了麼?在這個場景下,master / slave 的數據就所有丟失了。
這時,業務應用在訪問 Redis 時,發現緩存中沒有任何數據,就會把請求所有打到後端數據庫上,這還會進一步引起「緩存雪崩」,對業務影響很是大。
因此,你必定要避免這種狀況發生,我給你的建議是:
你在配置數據持久化時,要避免這個問題的發生。
2) AOF everysec 真的不會阻塞主線程嗎?
當 Redis 開啓 AOF 時,須要配置 AOF 的刷盤策略。
基於性能和數據安全的平衡,你確定會採用 appendfsync everysec 這種方案。
這種方案的工做模式爲,Redis 的後臺線程每間隔 1 秒,就把 AOF page cache 的數據,刷到磁盤(fsync)上。
這種方案的優點在於,把 AOF 刷盤的耗時操做,放到了後臺線程中去執行,避免了對主線程的影響。
但真的不會影響主線程嗎?
答案是否認的。
其實存在這樣一種場景:Redis 後臺線程在執行 AOF page cache 刷盤(fysnc)時,若是此時磁盤 IO 負載太高,那麼調用 fsync 就會被阻塞住。
此時,主線程仍然接收寫請求進來,那麼此時的主線程會先判斷,上一次後臺線程是否已刷盤成功。
如何判斷呢?
後臺線程在刷盤成功後,都會記錄刷盤的時間。
主線程會根據這個時間來判斷,距離上一次刷盤已通過去多久了。整個流程是這樣的:
經過分析咱們能夠發現,即便你配置的 AOF 刷盤策略是 appendfsync everysec,也依舊會有阻塞主線程的風險。
其實,產生這個問題的重點在於,磁盤 IO 負載太高致使 fynsc 阻塞,進而致使主線程寫 AOF page cache 也發生阻塞。
因此,你必定要保證磁盤有充足的 IO 資源,避免這個問題。
3) AOF everysec 真的只會丟失 1 秒數據?
接着上面的問題繼續分析。
如上所述,這裏咱們須要重點關注上面的步驟 4。
也就是:主線程在寫 AOF page cache 時,會先判斷上一次 fsync 成功的時間,若是距離上次 fysnc 成功在 2 秒內,那麼主線程會直接返回,再也不寫 AOF page cache。
這就意味着,後臺線程在執行 fsync 刷盤時,主線程最多等待 2 秒不會寫 AOF page cache。
若是此時 Redis 發生了宕機,那麼,AOF 文件中丟失是 2 秒的數據,而不是 1 秒!
咱們繼續分析,Redis 主線程爲何要等待 2 秒不寫 AOF page cache 呢?
其實,Redis AOF 配置爲 appendfsync everysec 時,正常來說,後臺線程每隔 1 秒執行一次 fsync 刷盤,若是磁盤資源充足,是不會被阻塞住的。
也就是說,Redis 主線程其實根本不用關心後臺線程是否刷盤成功,只要無腦寫 AOF page cache 便可。
可是,Redis 做者考慮到,若是此時的磁盤 IO 資源比較緊張,那麼後臺線程 fsync 就有機率發生阻塞風險。
因此,Redis 做者在主線程寫 AOF page cache 以前,先檢查一下距離上一次 fsync 成功的時間,若是大於 1 秒沒有成功,那麼主線程此時就能知道,fsync 可能阻塞了。
因此,主線程會等待 2 秒不寫 AOF page cache,其目的在於:
但代價就是,若是此時發生宕機,AOF 丟失的就是 2 秒的數據,而不是 1 秒。
這個方案應該是 Redis 做者對性能和數據安全性的進一步權衡。
不管如何,這裏你只須要知道的是,即便 AOF 配置爲每秒刷盤,在發生上述極端狀況時,AOF 丟失的數據實際上是 2 秒。
4) RDB 和 AOF rewrite 時,Redis 發生 OOM?
最後,咱們來看一下,當 Redis 在執行 RDB 快照和 AOF rewrite 時,會發生的問題。
Redis 在作 RDB 快照和 AOF rewrite 時,會採用建立子進程的方式,把實例中的數據持久化到磁盤上。
建立子進程,會調用操做系統的 fork 函數。
fork 執行完成後,父進程和子進程會同時共享同一分內存數據。
但此時的主進程依舊是能夠接收寫請求的,而進來的寫請求,會採用 Copy On Write(寫時複製)的方式操做內存數據。
也就是說,主進程一旦有數據須要修改,Redis 並不會直接修改現有內存中的數據,而是先將這塊內存數據拷貝出來,再修改這塊新內存的數據,這就是所謂的「寫時複製」。
寫時複製你也能夠理解成,誰須要發生寫操做,誰就先拷貝,再修改。
你應該發現了,若是父進程要修改一個 key,就須要拷貝原有的內存數據,到新內存中,這個過程涉及到了「新內存」的申請。
若是你的業務特色是「寫多讀少」,並且 OPS 很是高,那在 RDB 和 AOF rewrite 期間,就會產生大量的內存拷貝工做。
這會有什麼問題呢?
由於寫請求不少,這會致使 Redis 父進程會申請很是多的內存。在這期間,修改 key 的範圍越廣,新內存的申請就越多。
若是你的機器內存資源不足,這就會致使 Redis 面臨被 OOM 的風險!
這就是你會從 DBA 同窗那裏聽到的,要給 Redis 機器預留內存的緣由。
其目的就是避免在 RDB 和 AOF rewrite 期間,防止 Redis OOM。
以上這些,就是「數據持久化」會遇到的坑,你踩到過幾個?
下面咱們再來看「主從複製」會存在哪些問題。
Redis 爲了保證高可用,提供了主從複製的方式,這樣就能夠保證 Redis 有多個「副本」,當主庫宕機後,咱們依舊有從庫可使用。
在主從同步期間,依舊存在不少坑,咱們依次來看。
1) 主從複製會丟數據嗎?
首先,你須要知道,Redis 的主從複製是採用「異步」的方式進行的。
這就意味着,若是 master 忽然宕機,可能存在有部分數據還未同步到 slave 的狀況發生。
這會致使什麼問題呢?
若是你把 Redis 當作純緩存來使用,那對業務來講沒有什麼影響。
master 未同步到 slave 的數據,業務應用能夠從後端數據庫中從新查詢到。
可是,對於把 Redis 當作數據庫,或是當作分佈式鎖來使用的業務,有可能由於異步複製的問題,致使數據丟失 / 鎖丟失。
關於 Redis 分佈式鎖可靠性的更多細節,這裏先不展開,後面會單獨寫一篇文章詳細剖析這個知識點。這裏你只須要先知道,Redis 主從複製是有機率發生數據丟失的。
2) 一樣命令查詢一個 key,主從庫卻返回不一樣的結果?
不知道你是否思考過這樣一個問題:若是一個 key 已過時,但這個 key 還未被 master 清理,此時在 slave 上查詢這個 key,會返回什麼結果呢?
你認爲是哪種?能夠思考一下。
答案是:不必定。
嗯?爲何會不必定?
這個問題很是有意思,請跟緊個人思路,我會帶你一步步分析其中的緣由。
其實,返回什麼結果,這要取決於如下 3 個因素:
先來看 Redis 版本。
若是你使用的是 Redis 3.2 如下版本,只要這個 key 還未被 master 清理,那麼,在 slave 上查詢這個 key,它會永遠返回 value 給你。
也就是說,即便這個 key 已過時,在 slave 上依舊能夠查詢到這個 key。
// Redis 2.8 版本 在 slave 上執行
127.0.0.1:6479> TTL testkey
(integer) -2 // 已過時
127.0.0.1:6479> GET testkey
"testval" // 還能查詢到!
複製代碼
但若是此時在 master 上查詢這個 key,發現已通過期,就會把它清理掉,而後返回 NULL。
// Redis 2.8 版本 在 master 上執行
127.0.0.1:6379> TTL testkey
(integer) -2
127.0.0.1:6379> GET testkey
(nil)
複製代碼
發現了嗎?在 master 和 slave 上查詢同一個 key,結果居然不同?
其實,slave 應該要與 master 保持一致,key 已過時,就應該給客戶端返回 NULL,而不是還正常返回 key 的值。
爲何會發生這種狀況?
其實這是 Redis 的一個 Bug:3.2 如下版本的 Redis,在 slave 上查詢一個 key 時,並不會判斷這個 key 是否已過時,而是直接無腦返回給客戶端結果。
這個 Bug 在 3.2 版本進行了修復,可是,它修復得「不夠完全」。
什麼叫修復得「不夠完全」?
這就要結合前面提到的,第 2 個影響因素「具體執行的命令」來解釋了。
Redis 3.2 雖然修復了這個 Bug,但卻遺漏了一個命令:EXISTS。
也就是說,一個 key 已過時,在 slave 直接查詢它的數據,例如執行 GET/LRANGE/HGETALL/SMEMBERS/ZRANGE 這類命令時,slave 會返回 NULL。
但若是執行的是 EXISTS,slave 依舊會返回:key 還存在。
// Redis 3.2 版本 在 slave 上執行
127.0.0.1:6479> GET testkey
(nil) // key 已邏輯過時
127.0.0.1:6479> EXISTS testkey
(integer) 1 // 還存在!
複製代碼
緣由在於,EXISTS 與查詢數據的命令,使用的不是同一個方法。
Redis 做者只在查詢數據時增長了過時時間的校驗,但 EXISTS 命令依舊沒有這麼作。
直到 Redis 4.0.11 這個版本,Redis 才真正把這個遺漏的 Bug 徹底修復。
若是你使用的是這個之上的版本,那在 slave 上執行數據查詢或 EXISTS,對於已過時的 key,就都會返回「不存在」了。
這裏咱們先小結一下,slave 查詢過時 key,經歷了 3 個階段:
這裏要特別鳴謝《Redis開發與運維》的做者,付磊。
這個問題我是在他的文章中看到的,感受很是有趣,原來 Redis 以前還存在這樣的 Bug 。隨後我又查閱了相關源碼,並對邏輯進行了梳理,在這裏才寫成文章分享給你們。
雖然已在微信中親自答謝,但在這裏再次表達對他的謝意~
最後,咱們來看影響查詢結果的第 3 個因素:「機器時鐘」。
假設咱們已規避了上面提到的版本 Bug,例如,咱們使用 Redis 5.0 版本,在 slave 查詢一個 key,還會和 master 結果不一樣嗎?
答案是,仍是有可能會的。
這就與 master / slave 的機器時鐘有關了。
不管是 master 仍是 slave,在判斷一個 key 是否過時時,都是基於「本機時鐘」來判斷的。
若是 slave 的機器時鐘比 master 走得「快」,那就會致使,即便這個 key 還未過時,但以 slave 上視角來看,這個 key 其實已通過期了,那客戶端在 slave 上查詢時,就會返回 NULL。
是否是頗有意思?一個小小的過時 key,居然藏匿這麼多貓膩。
若是你也遇到了相似的狀況,就能夠經過上述步驟進行排查,確認是否踩到了這個坑。
3) 主從切換會致使緩存雪崩?
這個問題是上一個問題的延伸。
咱們假設,slave 的機器時鐘比 master 走得「快」,並且是「快不少」。
此時,從 slave 角度來看,Redis 中的數據存在「大量過時」。
若是此時操做「主從切換」,把 slave 提高爲新的 master。
它成爲 master 後,就會開始大量清理過時 key,此時就會致使如下結果:
你看,當 master / slave 機器時鐘嚴重不一致時,對業務的影響很是大!
因此,若是你是 DBA 運維,必定要保證主從庫的機器時鐘一致性,避免發生這些問題。
4) master / slave 大量數據不一致?
還有一種場景,會致使 master / slave 的數據存在大量不一致。
這就涉及到 Redis 的 maxmemory 配置了。
Redis 的 maxmemory 能夠控制整個實例的內存使用上限,超過這個上限,而且配置了淘汰策略,那麼實例就開始淘汰數據。
但這裏有個問題:假設 master / slave 配置的 maxmemory 不同,那此時就會發生數據不一致。
例如,master 配置的 maxmemory 爲 5G,而 slave 的 maxmemory 爲 3G,當 Redis 中的數據超過 3G 時,slave 就會「提早」開始淘汰數據,此時主從庫數據發生不一致。
另外,儘管 master / slave 設置的 maxmemory 相同,若是你要調整它們的上限,也要格外注意,不然也會致使 slave 淘汰數據:
以此方式操做,就避免了 slave 提早超過 maxmemory 的問題。
其實,你能夠思考一下,發生這些問題的關鍵在哪?
其根本緣由在於,slave 超過 maxmemory 後,會「自行」淘汰數據。
若是不讓 slave 本身淘汰數據,那這些問題是否是均可以規避了?
沒錯。
針對這個問題,Redis 官方應該也收到了不少用戶的反饋。在 Redis 5.0 版本,官方終於把這個問題完全解決了!
Redis 5.0 增長了一個配置項:replica-ignore-maxmemory,默認 yes。
這個參數表示,儘管 slave 內存超過了 maxmemory,也不會自行淘汰數據了!
這樣一來,slave 永遠會向 master 看齊,只會老老實實地複製 master 發送過來的數據,不會本身再搞「小動做」。
至此,master / slave 的數據就能夠保證徹底一致了!
若是你使用的剛好是 5.0 版本,就不用擔憂這個問題了。
5) slave 居然會有內存泄露問題?
是的,你沒看錯。
這是怎麼發生的?咱們具體來看一下。
當你在使用 Redis 時,符合如下場景,就會觸發 slave 內存泄露:
這時的 slave 就會發生內存泄露:slave 中的 key,即便到了過時時間,也不會自動清理。
若是你不主動刪除它,那這些 key 就會一直殘留在 slave 內存中,消耗 slave 的內存。
最麻煩的是,你使用命令查詢這些 key,卻還查不到任何結果!
這就 slave 「內存泄露」問題。
這其實也是 Redis 的一個 Bug,Redis 4.0 才修復了這個問題。
解決方案是,在可寫的 slave 上,寫入帶有過時時間 key 時,slave 會「記錄」下來這些 key。
而後 slave 會定時掃描這些 key,若是到達過時時間,則清理之。
若是你的業務須要在 slave 上臨時存儲數據,並且這些 key 也都設置了過時時間,那麼就要注意這個問題了。
你須要確認你的 Redis 版本,若是是 4.0 如下版本,必定要避免踩這個坑。
其實,最好的方案是,制定一個 Redis 使用規範,slave 必須強制設置爲 read-only,不容許寫,這樣不只能夠保證 master / slave 的數據一致性,還避免了 slave 內存泄露問題。
6) 爲何主從全量同步一直失敗?
在主從全量同步時,你可能會遇到同步失敗的問題,具體場景以下:
slave 向 master 發起全量同步請求,master 生成 RDB 後發給 slave,slave 加載 RDB。
因爲 RDB 數據太大,slave 加載耗時也會變得很長。
此時你會發現,slave 加載 RDB 還未完成,master 和 slave 的鏈接卻斷開了,數據同步也失敗了。
以後你又會發現,slave 又發起了全量同步,master 又生成 RDB 發送給 slave。
一樣地,slave 在加載 RDB 時,master / slave 同步又失敗了,以此往復。
這是怎麼回事?
其實,這就是 Redis 的「複製風暴」問題。
什麼是複製風暴?
就像剛纔描述的:主從全量同步失敗,又從新開始同步,以後又同步失敗,以此往復,惡性循環,持續浪費機器資源。
爲何會致使這種問題呢?
若是你的 Redis 有如下特色,就有可能發生這種問題:
主從在全量同步數據時,master 接收到的寫請求,會先寫到主從「複製緩衝區」中,這個緩衝區的「上限」是配置決定的。
當 slave 加載 RDB 太慢時,就會致使 slave 沒法及時讀取「複製緩衝區」的數據,這就引起了複製緩衝區「溢出」。
爲了不內存持續增加,此時的 master 會「強制」斷開 slave 的鏈接,這時全量同步就會失敗。
以後,同步失敗的 slave 又會「從新」發起全量同步,進而又陷入上面描述的問題中,以此往復,惡性循環,這就是所謂的「複製風暴」。
如何解決這個問題呢?我給你如下幾點建議:
若是你也踩到了這個坑,能夠經過這個方案來解決。
好了,總結一下,這篇文章咱們主要講了 Redis 在「命令使用」、「數據持久化」、「主從同步」3 個方面可能存在的「坑」。
怎麼樣?有沒有顛覆你的認知呢?
這篇文章信息量仍是比較大的,若是你如今的思惟已經有些「凌亂」了,別急,我也給你準備好了思惟導圖,方便你更好地理解和記憶。
但願你在使用 Redis 時,能夠提早規避這些坑,讓 Redis 更好地提供服務。
最後,我想和你聊一聊在開發過程當中,關於踩坑的經驗和心得。
其實,接觸任何一個新領域,都會經歷陌生、熟悉、踩坑、吸取經驗、遊刃有餘這幾個階段。
那在踩坑這個階段,如何少踩坑?或者踩坑後如何高效率地排查問題呢?
這裏我總結出了 4 個方面,應該能夠幫助到你:
1) 多看官方文檔 + 配置文件的註釋
必定要多看官方文檔,以及配置文件的註釋說明。其實不少可能存在風險的地方,優秀的軟件都會在文檔和註釋裏提示你的,認真讀一讀,能夠提早規避不少基礎問題。
2) 不放過疑問細節,多思考爲何?
永遠要保持好奇心。遇到問題,掌握剝絲抽繭,逐步定位問題的能力,時刻保持探尋事物問題本質的心態。
3) 勇於提出質疑,源碼不會騙人
若是你以爲一個問題很蹊蹺,多是一個 Bug,要勇於提出質疑。
經過源碼尋找問題的真相,這種方式要好過你看一百篇網上互相抄襲的文章(抄來抄去頗有可能都是錯的)。
4) 沒有完美的軟件,優秀軟件都是一步步迭代出來的
任何優秀的軟件,都是一步步迭代出來的。在迭代過程當中,存在 Bug 很正常,咱們須要抱着正確的心態去看待它。
這些經驗和心得,適用於學習任何領域,但願對你有所幫助。
想看更多硬核技術文章?歡迎關注個人公衆號「水滴與銀彈」。
我是 Kaito,是一個對於技術有思考的資深後端程序員,在個人文章中,我不只會告訴你一個技術點是什麼,還會告訴你爲何這麼作?我還會嘗試把這些思考過程,提煉成通用的方法論,讓你能夠應用在其它領域中,作到觸類旁通。