Redis系列 如何應對Redis變慢

2021-10-10 09:28:25 字數 3339 閱讀 9126

redis變慢的乙個重要原因是主線程阻塞,其他的如過期key的操作,作業系統swap、記憶體大頁也是重要的原因。

在討論如何變慢之前,我們需要知道redis的哪些操作會阻塞主線程。

1. 網路io使用的是多路復用機制,因此不會阻塞

2. o(n)的查詢操作和大批量的刪除需要遍歷全部,可能會阻塞主線程(集合元素全量查詢操作 hgetall、smembers,以及集合的聚合統計操作,例如求交、並和差集)

3. 清空資料庫(例如 flushdb 和 flushall 操作)必然也是乙個潛在的阻塞風險,因為它涉及到刪除和釋放所有的鍵值對

4. 生成rdb快照和aof日誌日誌重寫都可以啟用子執行緒進行操作,因此不會阻塞

5. redis 直接記錄 aof 日誌時,會根據不同的寫回策略對資料做落盤儲存。乙個同步寫磁碟的操作的耗時大約是 1~2ms,如果有大量的寫操作需要記錄在 aof 日誌中,並同步寫回的話,就會阻塞主線程了

6. 從庫在清空當前資料庫後,還需要把 rdb 檔案載入到記憶體,這個過程的快慢和 rdb 檔案的大小密切相關,rdb 檔案越大,載入過程越慢,會阻塞主線程

7. 主庫生成、傳輸rdb檔案,從庫接收rdb檔案都是由子程序來完成的,不會阻塞主線程

8. 向其他例項傳輸雜湊槽資訊,資料遷移時,如果有bigkey的話可能會阻塞

因此其中有5個操作會時候產生阻塞:

非同步子執行緒機制(以上的5點除了第一點需要等待redis返回結果資料的操作不可以非同步執行,其他4點不需要返回結果的都可以非同步執行):

redis 主線程啟動後,會使用作業系統提供的 pthread_create 函式建立 3 個子執行緒,分別由它們負責 aof 日誌寫操作、鍵值對刪除以及檔案關閉的非同步執行。

主線程通過乙個鍊錶形式的任務佇列和子執行緒進行互動。當收到鍵值對刪除和清空資料庫的操作時,主線程會把這個操作封裝成乙個任務,放入到任務佇列中,然後給客戶端返回乙個完成資訊,表明刪除已經完成。

但實際上,這個時候刪除還沒有執行,等到後台子執行緒從任務佇列中讀取任務後,才開始實際刪除鍵值對,並釋放相應的記憶體空間。因此,我們把這種非同步刪除也稱為惰性刪除(lazy free)。此時,刪除或清空操作不會阻塞主線程,這就避免了對主線程的效能影響。

和惰性刪除類似,當 aof 日誌配置成 everysec 選項後,主線程會把 aof 寫日誌操作封裝成乙個任務,也放到任務佇列中。後台子執行緒讀取任務後,開始自行寫入 aof 日誌,這樣主線程就不用一直等待 aof 日誌寫完了。

redis 4.0 後提供了非同步的鍵值對刪除和資料庫清空操作,redis 也提供了新的命令來執行這兩個操作。

其餘4個阻塞操作都可以非同步執行來進行優化,那麼慢查詢應該怎麼做呢?

redis 鍵值對的 key 可以設定過期時間。預設情況下,redis 每 100 毫秒會刪除一些過期 key,具體的演算法如下:

active_expire_cycle_lookups_per_loop 是 redis 的乙個引數,預設是 20,那麼,一秒內基本有 200 個過期 key 會被刪除。這一策略對清除過期 key、釋放記憶體空間很有幫助。如果每秒鐘刪除 200 個過期 key,並不會對 redis 造成太大影響。

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

解決辦法:

你需要根據實際業務的使用需求,決定 expireat 和 expire 的過期時間引數。其次,如果一批 key 的確是同時過期,你還可以在 expireat 和 expire 的過期時間引數上,加上乙個一定大小範圍內的隨機數,這樣,既保證了 key 在乙個鄰近時間範圍內被刪除,又避免了同時過期造成的壓力。

現代作業系統都實現了「虛擬記憶體」這一技術,不但在功能上突破了物理記憶體的限制,使程式可以操縱大於實際物理記憶體的空間,更重要的是,「虛擬記憶體」是隔離每個程序的安全保護網,使每個程序都不受其它程式的干擾。

swap空間的作用可簡單描述為:當系統的物理記憶體不夠用的時候,就需要將物理記憶體中的一部分空間釋放出來,以供當前執行的程式使用。那些被釋放的空間可能來自一些很長時間沒有什麼操作的程式,這些被釋放的空間被臨時儲存到swap空間中,等到那些程式要執行時,再從swap中恢復儲存的資料到記憶體中。這樣,系統總是在物理記憶體不夠時,才進行swap交換。

redis 的操作是直接通過訪問記憶體就能完成,一旦 swap 被觸發了,redis 的請求操作需要等到磁碟資料讀寫完成才行。而且,和我剛才說的 aof 日誌檔案讀寫使用 fsync 執行緒不同,swap 觸發後影響的是 redis 主 io 執行緒,這會極大地增加 redis 的響應時間。

觸發 swap 的原因主要是物理機器記憶體不足,對於 redis 而言,有兩種常見的情況:

解決辦法:

增加機器的記憶體或者使用 redis 集群。

linux 核心從 2.6.38 開始支援記憶體大頁機制,該機制支援 2mb 大小的記憶體頁分配,而常規的記憶體頁分配是按 4kb 的粒度來執行的。

雖然記憶體大頁可以給 redis 帶來記憶體分配方面的收益,但是,不要忘了,redis 為了提供資料可靠性保證,需要將資料做持久化儲存。這個寫入過程由額外的執行緒執行,所以,此時,redis 主線程仍然可以接收客戶端寫請求。客戶端的寫請求可能會修改正在進行持久化的資料。在這一過程中,redis 就會採用寫時複製機制,也就是說,一旦有資料要被修改,redis 並不會直接修改記憶體中的資料,而是將這些資料拷貝乙份,然後再進行修改。

如果採用了記憶體大頁,那麼,即使客戶端請求只修改 100b 的資料,redis 也需要拷貝 2mb 的大頁。相反,如果是常規記憶體頁機制,只用拷貝 4kb。兩者相比,你可以看到,當客戶端請求修改或新寫入資料較多時,記憶體大頁機制將導致大量的拷貝,這就會影響 redis 正常的訪存操作,最終導致效能變慢。

解決方案:

關閉記憶體大頁

檢視記憶體大頁的命令:

grep huge /proc/meminfo

檢查是否關閉

cat /sys/kernel/mm/transparent_hugepage/enabled

關閉記憶體大頁

echo never > /sys/kernel/mm/transparent_hugepage/enabled

echo never > /sys/kernel/mm/transparent_hugepage/defrag

redis系列二 linux下安裝redis

2 通過遠端管理工具,將壓縮包拷貝到linux伺服器中 tar zxvf 命令解壓檔案 3 執行make 對redis解壓後檔案進行編譯 編譯完成之後,可以看到解壓檔案redis 3.0.7 中會有對應的src conf等資料夾,這和windows下安裝解壓的檔案一樣,大部分安裝包都會有對應的類檔案...

Redi學筆記 Redis簡介

易擴充套件 nosql資料庫種類繁多,但是乙個共同的特點都是去掉關聯式資料庫的關係型特性。資料之間無關係,這樣就非常容易擴充套件。也無形之間,在架構的層面上帶來了可擴充套件的能力。高效能 nosql資料庫都具有非常高的讀寫效能,尤其在大資料量下,同樣表現優秀。這得益於它的無關係性,資料庫的結構簡單。...

Spring Boot系列筆記 整合Redis

autowired stringredistemplate stringredistemplate autowired redistemplate redistemplate redistemplate類中提供了redis常見的五種資料型別 stringredistemplate.opsforval...