本篇文章給大家帶來了關于redis的相關知識,其中主要介紹了關于redis變慢的原因及排查方法的相關問題,下面一起來看一下,希望對大家有幫助。
推薦學習:redis
原因1:實例內存達到上限
排查思路
? ? ? ? 如果你的 redis 實例設置了內存上限 maxmemory,那么也有可能導致 Redis 變慢。
? ? ? ? 當我們把 Redis 當做純緩存使用時,通常會給這個實例設置一個內存上限 maxmemory,然后設置一個數據淘汰策略。而當實例的內存達到了 maxmemory 后,你可能會發現,在此之后每次寫入新數據,操作延遲變大了。
導致變慢的原因?
當 Redis 內存達到 maxmemory 后,每次寫入新的數據之前,Redis 必須先從實例中踢出一部分數據,讓整個實例的內存維持在 maxmemory 之下,然后才能把新數據寫進來。
這個踢出舊數據的邏輯也是需要消耗時間的,而具體耗時的長短,要取決于你配置的淘汰策略:
- allkeys-lru:不管 key 是否設置了過期,淘汰最近最少訪問的 key
- volatile-lru:只淘汰最近最少訪問、并設置了過期時間的 key
- allkeys-random:不管 key 是否設置了過期,隨機淘汰 key
- volatile-random:只隨機淘汰設置了過期時間的 key
- allkeys-ttl:不管 key 是否設置了過期,淘汰即將過期的 key
- noeviction:不淘汰任何 key,實例內存達到 maxmeory 后,再寫入新數據直接返回錯誤
- allkeys-lfu:不管 key 是否設置了過期,淘汰訪問頻率最低的 key(4.0+版本支持)
- volatile-lfu:只淘汰訪問頻率最低、并設置了過期時間 key(4.0+版本支持)
? ? ? ? 具體使用哪種策略,我們需要根據具體的業務場景來配置。一般最常使用的是 allkeys-lru / volatile-lru 淘汰策略,它們的處理邏輯是,每次從實例中隨機取出一批 key(這個數量可配置),然后淘汰一個最少訪問的 key,之后把剩下的 key 暫存到一個池子中,繼續隨機取一批 key,并與之前池子中的 key 比較,再淘汰一個最少訪問的 key。以此往復,直到實例內存降到 maxmemory 之下。
? ? ? ? 需要注意的是,Redis 的淘汰數據的邏輯與刪除過期 key 的一樣,也是在命令真正執行之前執行的,也就是說它也會增加我們操作 Redis 的延遲,而且,寫 OPS 越高,延遲也會越明顯。
另外,如果此時你的 Redis 實例中還存儲了 bigkey,那么在淘汰刪除 bigkey 釋放內存時,也會耗時比較久。
看到了么?bigkey 的危害到處都是,這也是前面我提醒你盡量不存儲 bigkey 的原因。
解決方案
- 避免存儲 bigkey,降低釋放內存的耗時
- 淘汰策略改為隨機淘汰,隨機淘汰比 LRU 要快很多(視業務情況調整)
- 拆分實例,把淘汰 key 的壓力分攤到多個實例上
- 如果使用的是 Redis 4.0 以上版本,開啟 layz-free 機制,把淘汰 key 釋放內存的操作放到后臺線程中執行(配置 lazyfree-lazy-eviction = yes)
原因2:開啟內存大頁
排查思路
- 我們都知道,應用程序向操作系統申請內存時,是按內存頁進行申請的,而常規的內存頁大小是 4KB。
- linux 內核從 2.6.38 開始,支持了內存大頁機制,該機制允許應用程序以 2MB 大小為單位,向操作系統申請內存。
- 應用程序每次向操作系統申請的內存單位變大了,但這也意味著申請內存的耗時變長。
導致變慢的原因
- 當 Redis 在執行后臺 RDB 和 AOF rewrite 時,采用 fork 子進程的方式來處理。但主進程 fork 子進程后,此時的主進程依舊是可以接收寫請求的,而進來的寫請求,會采用 copy On Write(寫時復制)的方式操作內存數據。
- 也就是說,主進程一旦有數據需要修改,Redis 并不會直接修改現有內存中的數據,而是先將這塊內存數據拷貝出來,再修改這塊新內存的數據,這就是所謂的「寫時復制」。
- 寫時復制你也可以理解成,誰需要發生寫操作,誰就需要先拷貝,再修改。
- 這樣做的好處是,父進程有任何寫操作,并不會影響子進程的數據持久化(子進程只持久化 fork 這一瞬間整個實例中的所有數據即可,不關心新的數據變更,因為子進程只需要一份內存快照,然后持久化到磁盤上)。
- 但是請注意,主進程在拷貝內存數據時,這個階段就涉及到新內存的申請,如果此時操作系統開啟了內存大頁,那么在此期間,客戶端即便只修改 10B 的數據,Redis 在申請內存時也會以 2MB 為單位向操作系統申請,申請內存的耗時變長,進而導致每個寫請求的延遲增加,影響到 Redis 性能。
- 同樣地,如果這個寫請求操作的是一個 bigkey,那主進程在拷貝這個 bigkey 內存塊時,一次申請的內存會更大,時間也會更久。可見,bigkey 在這里又一次影響到了性能。
解決方案
關閉內存大頁機制。
首先,你需要查看 Redis 機器是否開啟了內存大頁:
$ cat /sys/kernel/mm/transparent_hugepage/enabled [always] madvise never
如果輸出選項是 always,就表示目前開啟了內存大頁機制,我們需要關掉它:
$ echo never > /sys/kernel/mm/transparent_hugepage/enabled
其實,操作系統提供的內存大頁機制,其優勢是,可以在一定程序上降低應用程序申請內存的次數。
但是對于 Redis 這種對性能和延遲極其敏感的數據庫來說,我們希望 Redis 在每次申請內存時,耗時盡量短,所以我不建議你在 Redis 機器上開啟這個機制。
原因3:使用Swap
排查思路
如果你發現 Redis 突然變得非常慢,每次的操作耗時都達到了幾百毫秒甚至秒級,那此時你就需要檢查 Redis 是否使用到了 Swap,在這種情況下 Redis 基本上已經無法提供高性能的服務了。
導致變慢的原因?
什么是 Swap?為什么使用 Swap 會導致 Redis 的性能下降?
如果你對操作系統有些了解,就會知道操作系統為了緩解內存不足對應用程序的影響,允許把一部分內存中的數據換到磁盤上,以達到應用程序對內存使用的緩沖,這些內存數據被換到磁盤上的區域,就是 Swap。
問題就在于,當內存中的數據被換到磁盤上后,Redis 再訪問這些數據時,就需要從磁盤上讀取,訪問磁盤的速度要比訪問內存慢幾百倍!尤其是針對 Redis 這種對性能要求極高、性能極其敏感的數據庫來說,這個操作延時是無法接受的。
此時,你需要檢查 Redis 機器的內存使用情況,確認是否存在使用了 Swap。你可以通過以下方式來查看 Redis 進程是否使用到了 Swap:
# 先找到 Redis 的進程 ID $ ps -aux | grep redis-server # 查看 Redis Swap 使用情況 $ cat /proc/$pid/smaps | egrep '^(Swap|Size)'
輸出結果如下
Size: 1256 kB Swap: 0 kB Size: 4 kB Swap: 0 kB Size: 132 kB Swap: 0 kB Size: 63488 kB Swap: 0 kB Size: 132 kB Swap: 0 kB Size: 65404 kB Swap: 0 kB Size: 1921024 kB Swap: 0 kB ...
????????這個結果會列出 Redis 進程的內存使用情況。
????????每一行 Size 表示 Redis 所用的一塊內存大小,Size 下面的 Swap 就表示這塊 Size 大小的內存,有多少數據已經被換到磁盤上了,如果這兩個值相等,說明這塊內存的數據都已經完全被換到磁盤上了。
????????如果只是少量數據被換到磁盤上,例如每一塊 Swap 占對應 Size 的比例很小,那影響并不是很大。如果是幾百兆甚至上 GB 的內存被換到了磁盤上,那么你就需要警惕了,這種情況 Redis 的性能肯定會急劇下降。
解決方案
- 增加機器的內存,讓 Redis 有足夠的內存可以使用
- 整理內存空間,釋放出足夠的內存供 Redis 使用,然后釋放 Redis 的 Swap,讓 Redis 重新使用內存
????????釋放 Redis 的 Swap 過程通常要重啟實例,為了避免重啟實例對業務的影響,一般會先進行主從切換,然后釋放舊主節點的 Swap,重啟舊主節點實例,待從庫數據同步完成后,再進行主從切換即可。
????????可見,當 Redis 使用到 Swap 后,此時的 Redis 性能基本已達不到高性能的要求(你可以理解為武功被廢),所以你也需要提前預防這種情況。
????????預防的辦法就是,你需要對 Redis 機器的內存和 Swap 使用情況進行監控,在內存不足或使用到 Swap 時報警出來,及時處理。
原因4:網絡帶寬過載?
排查思路
????????如果以上產生性能問題的場景,你都規避掉了,而且 Redis 也穩定運行了很長時間,但在某個時間點之后開始,操作 Redis 突然開始變慢了,而且一直持續下去,這種情況又是什么原因導致?
????????此時你需要排查一下 Redis 機器的網絡帶寬是否過載,是否存在某個實例把整個機器的網路帶寬占滿的情況。
導致變慢的原因
????????網絡帶寬過載的情況下,服務器在 TCP 層和網絡層就會出現數據包發送延遲、丟包等情況。
????????Redis 的高性能,除了操作內存之外,就在于網絡 IO 了,如果網絡 IO 存在瓶頸,那么也會嚴重影響 Redis 的性能。
解決方案
- 及時確認占滿網絡帶寬 Redis 實例,如果屬于正常的業務訪問,那就需要及時擴容或遷移實例了,避免因為這個實例流量過大,影響這個機器的其他實例。
- 運維層面,你需要對 Redis 機器的各項指標增加監控,包括網絡流量,在網絡流量達到一定閾值時提前報警,及時確認和擴容。
原因5:其他原因
1) 頻繁短連接
????????你的業務應用,應該使用長連接操作 Redis,避免頻繁的短連接。
????????頻繁的短連接會導致 Redis 大量時間耗費在連接的建立和釋放上,TCP 的三次握手和四次揮手同樣也會增加訪問延遲。
2) 運維監控
????????前面我也提到了,要想提前預知 Redis 變慢的情況發生,必不可少的就是做好完善的監控。
????????監控其實就是對采集 Redis 的各項運行時指標,通常的做法是監控程序定時采集 Redis 的 INFO 信息,然后根據 INFO 信息中的狀態數據做數據展示和報警。
????????這里我需要提醒你的是,在寫一些監控腳本,或使用開源的監控組件時,也不能掉以輕心。
????????在寫監控腳本訪問 Redis 時,盡量采用長連接的方式采集狀態信息,避免頻繁短連接。同時,你還要注意控制訪問 Redis 的頻率,避免影響到業務請求。
????????在使用一些開源的監控組件時,最好了解一下這些組件的實現原理,以及正確配置這些組件,防止出現監控組件發生 bug,導致短時大量操作 Redis,影響 Redis 性能的情況發生。
????????我們當時就發生過,dba 在使用一些開源組件時,因為配置和使用問題,導致監控程序頻繁地與 Redis 建立和斷開連接,導致 Redis 響應變慢。
3)其它程序爭搶資源
????????最后需要提醒你的是,你的 Redis 機器最好專項專用,只用來部署 Redis 實例,不要部署其他應用程序,盡量給 Redis 提供一個相對「安靜」的環境,避免其它程序占用 CPU、內存、磁盤資源,導致分配給 Redis 的資源不足而受到影響。
推薦學習:redis