首頁 > 軟體

淺談Redis阻塞的9種情況

2023-03-25 06:03:32

前兩天去美團面試的陳同學回來了,看他滿臉洩氣的樣子,準是沒拿到 Offer。

聽了他面試的經過,真替他感到惋惜。究其原因,是被一道面試題攔住了去路:看你簡歷上寫著精通 Redis,請你總結一下 Redis 中存在的阻塞問題吧。

正好阿Q這幾天正在研究 Redis,就順便在這兒給大家做個總結。

命令阻塞

使用不當的命令造成使用者端阻塞:

  • keys * :獲取所有的 key 操作;
  • Hgetall:返回雜湊表中所有的欄位和;
  • smembers:返回集合中的所有成員;

這些命令時間複雜度是O(n),有時候也會全表掃描,隨著n的增大耗時也會越大從而導致使用者端阻塞。

SAVE 阻塞

大家都知道 Redis 在進行 RDB 快照的時候,會呼叫系統函數 fork() ,建立一個子執行緒來完成臨時檔案的寫入,而觸發條件正是組態檔中的 save 設定。

當達到我們的設定時,就會觸發 bgsave 命令建立快照,這種方式是不會阻塞主執行緒的,而手動執行 save 命令會在主執行緒中執行,阻塞主執行緒。

同步持久化

當 Redis 直接記錄 AOF 紀錄檔時,如果有大量的寫操作,並且設定為同步持久化

appendfsync always

即每次發生資料變更會被立即記錄到磁碟,因為寫磁碟比較耗時,效能較差,所以有時會阻塞主執行緒。

AOF 重寫

  • fork 出一條子執行緒來將檔案重寫,在執行 BGREWRITEAOF 命令時,Redis 伺服器會維護一個 AOF 重寫緩衝區,該緩衝區會在子執行緒建立新 AOF 檔案期間,記錄伺服器執行的所有寫命令。
  • 當子執行緒完成建立新 AOF 檔案的工作之後,伺服器會將重寫緩衝區中的所有內容追加到新 AOF 檔案的末尾,使得新的 AOF 檔案儲存的資料庫狀態與現有的資料庫狀態一致。
  • 最後,伺服器用新的 AOF 檔案替換舊的 AOF 檔案,以此來完成 AOF 檔案重寫操作。

阻塞就是出現在第2步的過程中,將緩衝區中新資料寫到新檔案的過程中會產生阻塞

AOF 紀錄檔

AOF 的紀錄檔記錄不像關係型資料庫那樣在執行命令之前記錄紀錄檔(方便故障恢復),而是採用先執行命令後記錄紀錄檔的方式。

原因就是 AOF 記錄紀錄檔是不會對命令進行語法檢查的,這樣就能減少額外的檢查開銷,不會對當前命令的執行產生阻塞,但可能會給下一個操作帶來阻塞風險。

這是因為 AOF 紀錄檔也是在主執行緒中執行的,如果在把紀錄檔檔案寫入磁碟時,磁碟寫壓力大,就會導致寫盤很慢,進而導致後續的操作也無法執行了。

大 Key 問題

大 key 並不是指 key 的值很大,而是 key 對應的 value 很大。

大 key 造成的阻塞問題如下:

  • 使用者端超時阻塞:由於 Redis 執行命令是單執行緒處理,然後在操作大 key 時會比較耗時,那麼就會阻塞 Redis,從使用者端這一視角看,就是很久很久都沒有響應。
  • 引發網路阻塞:每次獲取大 key 產生的網路流量較大,如果一個 key 的大小是 1 MB,每秒存取量為 1000,那麼每秒會產生 1000MB 的流量,這對於普通千兆網路卡的伺服器來說是災難性的。
  • 阻塞工作執行緒:如果使用 del 刪除大 key 時,會阻塞工作執行緒,這樣就沒辦法處理後續的命令。

查詢大 key

當我們在使用 Redis 自帶的 --bigkeys 引數查詢大 key 時,最好選擇在從節點上執行該命令,因為主節點上執行時,會阻塞主節點。

  • 我們還可以使用 SCAN 命令來查詢大 key;

  • 通過分析 RDB 檔案來找出 big key,這種方案的前提是 Redis 採用的是 RDB 持久化。網上有現成的工具:

    • redis-rdb-tools:Python 語言寫的用來分析 Redis 的 RDB 快照檔案用的工具
    • rdb_bigkeys:Go 語言寫的用來分析 Redis 的 RDB 快照檔案用的工具,效能更好。

刪除大 key

刪除操作的本質是要釋放鍵值對佔用的記憶體空間。

釋放記憶體只是第一步,為了更加高效地管理記憶體空間,在應用程式釋放記憶體時,作業系統需要把釋放掉的記憶體塊插入一個空閒記憶體塊的連結串列,以便後續進行管理和再分配。這個過程本身需要一定時間,而且會阻塞當前釋放記憶體的應用程式。

所以,如果一下子釋放了大量記憶體,空閒記憶體塊連結串列操作時間就會增加,相應地就會造成 Redis 主執行緒的阻塞,如果主執行緒發生了阻塞,其他所有請求可能都會超時,超時越來越多,會造成 Redis 連線耗盡,產生各種異常。

刪除大 key 時建議採用分批次刪除和非同步刪除的方式進行。

清空資料庫

清空資料庫和上面 bigkey 刪除也是同樣道理,flushdb、flushall 也涉及到刪除和釋放所有的鍵值對,也是 Redis 的阻塞點。

叢集擴容

Redis 叢集可以進行節點的動態擴容縮容,這一過程目前還處於半自動狀態,需要人工介入。

在擴縮容的時候,需要進行資料遷移。而 Redis 為了保證遷移的一致性,遷移所有操作都是同步操作。

執行遷移時,兩端的 Redis 均會進入時長不等的阻塞狀態,對於小Key,該時間可以忽略不計,但如果一旦 Key 的記憶體使用過大,嚴重的時候會接觸發叢集內的故障轉移,造成不必要的切換。

到此這篇關於淺談Redis阻塞的9種情況的文章就介紹到這了,更多相關Redis 阻塞內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com