堵塞 redis的噩耗

2021-10-14 10:29:41 字數 2600 閱讀 5600

由於redis的單執行緒模型,如果在主線程中執行了耗時的操作,那麼之後的所有命令將會被堵塞,響應時間將會增加,整個系統的吞吐量會降低。為此我們需要避免導致redis堵塞的操作,那麼redis都有哪些堵塞操作呢?我將redis的堵塞分為內在原因與外在原因。

1、時間複雜度高的操作,如keys、集合的一些聚合操作等。我們可以通過redis的慢查詢日誌,找到執行慢的日誌。

2、bigkey的刪除操作,如下表所示

可以看出刪除乙個百萬級別的bigkey都達到秒級別了。為此刪除bigkey需要使用非同步刪除的unlink命令,但是如果你的redis版本不支援非同步刪除的unlink命令,那麼可以使用scan命令一次刪除一部分。

3、大量key同時過期,它會導致大量的請求穿透到資料庫,同時還可能會導致redis堵塞。redis的過期key的刪除是在redis。redis 鍵值對的 key 可以設定過期時間。預設情況下,redis 每 100 毫秒會刪除一些過期 key,具體的演算法如下:

取樣 active_expire_cycle_lookups_per_loop 個數的 key,並將其中過期的 key 全部刪除;

如果超過 25% 的 key 過期了,則重複刪除的過程,直到過期 key 的比例降至 25% 以下。

active_expire_cycle_lookups_per_loop 是 redis 的乙個引數,預設是 20。這一策略對清除過期 key、釋放記憶體空間很有幫助。但是,如果觸發了上面這個演算法的第二條,redis 就會一直刪除以釋放記憶體空間。注意,刪除操作是阻塞的(redis 4.0 後可以用非同步執行緒機制來減少阻塞影響)。所以,一旦該條件觸發,redis 的執行緒就會一直執行刪除,這樣一來,就沒辦法正常服務其他的鍵值操作了,就會進一步引起其他鍵值操作的延遲增加,redis 就會變慢。

我們可以通過給key設定過期時間時,額外新增乙個比較短的過期時間,比如1分鐘以內的隨機值,避免大量key同時過期。

4、大例項redis,fork建立子程序,雖然redis生成rdb快照與aof的重寫是通過fork子程序來實現的,並且利用了linux的寫時複製技術,但是程序的一些基本資訊與虛擬記憶體到物理記憶體的對映頁表還是需要複製的。這個複製的過程會堵塞主線程,如果redis例項非常大的話,如幾十gb,那麼fork過程將會達到秒級別,從而長時間堵塞主線程。為此我們需要避免大redis例項,一般redis的合理大小是2到4gb。如果你的資料量真的很大的話,應該使用切片集群,將資料分攤到不同的例項。

另外有一點需要注意的,linux的寫時複製技術是以頁為單位複製的,也就是如果主線程只修改了乙個頁的乙個位元組,那麼修改前也會複製一整頁的資料。為此我們需要關閉linux的大頁機制,避免fork子程序執行期間,主程序修改資料時,需要拷貝大量的記憶體資料,減小對主線程的影響。

5、開啟aof且將策略設定為always,那麼redis每收到一條更新命令,就會同步寫盤,一次寫盤操作耗時通常是毫秒級別的,明顯的會影響redis的吞吐量。

6、主從同步中,redis從機清空資料庫與載入rdb快照時,如果你使用的是redis4.0及以上的版本,那麼清空資料庫的時候,是用的非同步執行緒實現的。但是載入rdb快照是同步完成的,在載入完成之前redis就會堵塞住。為此我們一般通過調大replic_backlog緩衝區的大小,避免因為網路閃斷導致的全量複製。

7、淘汰策略,當redis記憶體達到上限後,將會觸發記憶體淘汰,當一次寫入大量的資料時,需要預先淘汰資料。

8、上文中提到的,aof日誌的每秒刷盤,如果aof的刷盤策略配置成everysec時,當redis主線程收到命令時,會將其寫入aof的作業系統緩衝區,後台執行緒每秒呼叫fsync重新整理一次到磁碟。但是需要注意的是如果由於磁碟io繁忙,非同步刷盤時,fsync超過一秒還沒有執行成功,那麼主線程將停止服務。

9、管道與事務過大。

1、與其他占用cpu的服務混合部署,其他服務占用大量的cpu資源。redis是乙個cpu密集型的應用,不建議混合部署

2、繫結cpu優化,由於redis的生成rdb快照與aof重寫是通過fork子程序完成的,同時redis內部還有一些後台子執行緒。繫結cpu不合理也會導致他們互相爭搶資源。關於cpu的優化在我的博文redis 多核cpu與numa架構優化中已經有詳細的描述,這裡不再贅述。

1、大量的併發請求,當存在大量的併發請求時,雖然使用了io多路復用技術,但是io的讀寫都是在主線程中執行,當有大量的併發請求時,單執行緒的io讀寫可能存在效能瓶頸,從而導致請求堵塞。

2、大量的網路連線,這樣有如下的後果:

redis連線拒絕,redis提供了maxclients引數用於控制網路連線的最大個數,當超過了redis會拒絕連線

linux系統資源瓶頸,達到程序可以開啟的檔案描述符個數限制:ulimit -n 65535。linux半連線與全連線佇列溢位,可以通過netstat -s命令檢視連線佇列是否有溢位。

盡量使用長連線代替短連線,設定使用連線池,關於連線池的介紹見我的博文池化技術–如何減少不斷建立資料庫連線的效能損耗

主機記憶體不足,發生swap。那麼如果讀取或者更新的命令的資料被交換到了磁碟,那麼需要將資料所在的頁交換回記憶體,將會顯著的降低redis的效能。為此我們一般需要關閉linux的swap功能,並且設定redis的最大記憶體空間,同時還要預留一定的記憶體空間給作業系統等使用。

php socket 同步非同步堵塞非堵塞的區別

php socket 同步非同步堵塞非堵塞的區別 從accept接受資料開始 同步就是 服務端從客戶端接受完資料 處理 然後傳送給客戶端了 然後再開始接收新的客戶端發來的資料 非同步就是 服務端從客戶端接受完資料 就可以再次繼續接收 非同步處理資料 堵塞就是 服務端堵塞執行緒狀態接收資料 read ...

bzoj 1018 堵塞的交通

傳送門 這是一道好題,不容易想到線段樹和維護的量。分析題目,考慮聯通方式,發現只有3種,我一開始的想法是結構分塊,使用並查集維護,o 1 新增很自然,刪除的話就暴力重建塊內的並查集o sqrt n 查詢的時候仍然考慮並查集維護,求出關鍵點 上下左右4個 和他們之間的聯通性,數量級是o sqrt n ...

2017 9 9 堵塞的交通 思考記錄

三分太難了 於是來寫線段樹 維護四個點之間的連通性 然後就沒了。然後把n定義成bool 狂wa不止 以後資料結構題不寫對拍就不要交!你一定要足夠的相信你的程式很多地方都是錯的 碼 include includeusing namespace std define zuo o 1,l,mid defi...