Redis 有哪些阻塞點以及如何解決

俗話說得好,系統慢不用怕,沒有什麼是加一層緩存解決不了的,一層不行的話,就加兩層。

但加緩存也並不是一勞永逸的,就哪 Redis 來說,還是有很多因素會影響 Redis 性能的,本文就來說一說其中的一些阻塞點,以及如何解決。

轉載信息如下:

作者:芥末拌飯
鏈接:https://juejin.cn/post/7149728938359537701

說到 Redis,大家自然而然的會想到基於內存、單線程執行等。話說回來,Redis 真的只有單線程嗎?這篇文章來說說 Redis 的異步機制。

影響 Redis 性能的因素

我們從 Redis 內部及外部因素總結一下,主要有:

  1. Redis 內部的阻塞式操作

  2. CPU 核和 NUMA 架構的影響

  3. Redis 關鍵系統配置

  4. Redis 內存碎片

  5. Redis 緩衝區

首先說說 Redis 實例的阻塞點:

  1. 客戶端:網絡 IO,鍵值對的增刪改查操作,數據庫操作;

  2. 磁盤:生成 RDB 快照,記錄 AOF 日誌,AOF 日誌重寫;

  3. 主從節點:主庫生成、傳輸 RDB 文件,從庫接收 RDB 文件、清空數據庫、加載 RDB 文件;

  4. 切片集羣實例:向其他實例傳輸哈希槽信息,數據遷移。

客戶端的阻塞

因 Redis 使用了 IO 多路複用機制,能避免主線程一直處於等待狀態,網絡 IO 不是導致 Redis 阻塞的因素。

而鍵值對的增刪改查是主線程的主要工作,複雜度高的操作當然會阻塞 Redis 了。我們去判斷複雜度高不高的標準就是看操作的複雜度是否爲 O(N),也就是否要全表掃描。比如 hgetallsmembers 等操作就屬於複雜度高的了。

然後還要注意的一個點就是數據的刪除。

刪除本質上來說就是對鍵值對的內存空間進行釋放。在釋放內存時,操作系統需要將釋放掉的內存塊插入一個空閒內存塊的鏈表,以便後續管理和再分配。這個過程會阻塞當前釋放內存的應用程序。

如果這個鍵值對數據很大,比如一個 zset 包含大量元素,就會釋放大量的內存。有測試過刪除 100 萬個元素的集合時,刪除時間會達到 2s,要知道 Redis 的響應是毫秒級別的。所以這種 bigkey 的刪除也會成爲 Redis 的阻塞點。

清空數據庫(flushdbflushall)也涉及到刪除和釋放所有的鍵值對,也是 Redis 的阻塞點。

磁盤帶來的阻塞

AOF 重寫和 RDB 快照,Redis 都用了子進程的方式操作,所以不會阻塞主線程。但 Redis 直接記錄 AOF 日誌,若有大量的寫操作,並且配置的是同步寫回的話,就會阻塞主線程了。

主從節點帶來的阻塞

在主從集羣中,主庫生成 RDB 文件,並傳輸給從庫。主從複製過程的創建和傳輸 RDB 都是子進程處理的,不會阻塞主線程。

但是從庫在接收了 RDB 文件後,需要使用 flushdb 命令清空當前數據庫,這又是一個阻塞點。而且,在從庫清空數據庫後,需要將 RDB 文件加載到內存,快慢和 RDB 文件大小相關。加載 RDB 文件又是一個阻塞點。

切片集羣的阻塞

切片集羣的實例在負載均衡或者實例增加刪除時,數據遷移是漸進式操作的,所以不會阻塞主線程。

小結

總結一下,Redis 就有 5 個阻塞點:

  1. 集合全量查詢和聚合操作;

  2. bigkey 刪除;

  3. 清空數據庫;

  4. AOF 日誌同步寫;

  5. 從庫加載 RDB 文件。

異步機制解決阻塞

我們通過異步的方式,去解決可能阻塞的場景。但也不是每個操作都能用異步的方式去解決。如果一個操作能夠異步執行,說明客戶端不需要馬上得到具體值,在 Redis 中描述爲若一個操作能異步執行,就意味着它不是主線程的關鍵路徑的操作。

對於第一個阻塞點,因爲讀操作需要等待數據的返回,所以第一個阻塞點不能異步執行。

第二個阻塞點和第三個阻塞點,因爲刪除不需要返回具體的結果,因此都可以用子線程去異步執行。

第四個阻塞點 “AOF 日誌同步寫”,也可啓動子線程操作,不用讓主線程等待 AOF 日誌的寫完成。

第五個阻塞點 “從庫加載 RDB 文件”,從庫要想對客戶端提供數據存取服務,就必須把 RDB 文件加載完成,不能啓用子進程。

異步是如何進行的

Redis 主線程啓動後,會使用操作系統提供的 pthread_create 函數創建 3 個子線程,分別由它們負責 AOF 日誌寫操作、鍵值對刪除以及文件關閉的異步執行。

異步刪除 lazy-free

lazy-free 機制是 Redis 收到刪除指令後,主線程會將這個操作放入隊列,然後馬上給客戶端返回一個完成信息。實際上刪除還沒執行呢。

lazy-free 是 Redis4.0 之後纔有的功能,需要手動開啓。需要注意的是,即使開啓了 lazy-free,如果直接使用 DEL 命令還是會同步刪除 key,只有使用 UNLINK 命令纔會可能異步刪除 key。

而且 Redis 在刪除一個 key 時,首先會評估刪除的時間成本,如果成本小,也不會異步執行,直接用主線程就完成返回了。

總結

本文總結 Redis 有哪些阻塞點,以及這些阻塞點是否可用異步機制去解決。但我們在使用 Redis 時,還是要避免 bigkey 的使用。


本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/DnsklTRfaENOxKNCb7p22A