感覺Redis變慢了,這些可能的原因你查了沒 ?
前言
本期繼續分享關于Redis的知識,讓你掌握在Redis變慢后不會慌張,冷靜下來分析問題,為了方便閱讀,文章分為上下兩篇!
Redis 作為一款業內使用率最高的內存數據庫,其擁有非常高的性能,單節點的QPS壓測能達到18萬以上。但也正因此如此,當應用訪問 Redis 時,如果發現響應延遲變大時就會給業務帶來非常大的影響。
比如在日常使用Redis時,肯定或多或少都遇到過下面這種問題:
圖片
大部分兄弟面對這種訪問變慢問題的排查就會一頭霧水,不知道從哪里下手才好,因為不理解 Redis 的架構體系、核心功能的實現原理甚至一些命令的使用限制等。
今天就可能引起Redis變慢的原因一一分析,上篇看完后你將會形成一個比較完整的排查思路方案!
圖片
Redis真的變慢了嗎?
當我們遇到服務響應比較慢時,往往需要先排查內部原因,先弄清楚是不是Redis服務導致的,我們大部分系統可能涉及較長的鏈路和多服務、比如同一個接口會調用Mysql、MQ、Redis等其他三方組件和服務。
圖片
因此需要確定是不是訪問Redis服務變慢進而拖慢了整個服務的響應變慢,那就是先自查!??
- ? 應用服務訪問Redis的請求,記錄下每次請求的響應延時,對比是否響應變長
- ? 是否其他節點存在同樣問題
假設我們確定了是Redis這條鏈路的問題!(如果不是Redis問題,文章就寫不下去啦?。」?,這里同樣存在兩種可能 ??
- ? 業務端請求到Redis服務網絡是否存在問題,存在網絡延遲情況
- ? Redis服務端本身出現問題,那需要進一步排查
redis命令執行過程-簡約版.png
正常來說網絡存在問題的可能性還是比較小的,因為如果存在網絡問題,那么其他服務同樣都會發生網絡延遲情況,如果你想了解網絡對 Redis 性能的影響,可以用 iPerf 這樣的工具,測量從 Redis 客戶端到服務器端的網絡延遲,如果這個延遲有幾十毫秒甚至是幾百毫秒,就說明,Redis 運行的網絡環境中很可能有大流量的其他應用程序在運行。
好,現在就剩下確定請求Redis的服務響應耗時變長了,也是文章的要講的焦點問題,分析Redis變慢的原因,先查看Redis的響應延遲,可以對Redis 進行基準性能測試。
基準測試
基準性能就是指 Redis 在一臺負載正常的機器上,其最大的響應延遲和平均響應延遲分別是多少
但是這又不能把別人或者官方的測試結果作為參考的指標,因為在不同的軟硬件環境下,它的性能表現差別特別大,不同主頻型號的CPU、不同的SSD硬盤,都會極大影響Redis的性能表現。
那該以什么標準來認定Redis變慢呢?
????一般來說,如果你觀察到的 Redis 運行時延遲是其基線性能的 2 倍及以上,就可以認定 Redis 變慢了
比如:執行以下命令,就可以測試出這個實例 60 秒內的最大響應延遲
[root@VM-12-10-opencloudos ~]# redis-cli --intrinsic-latency 60
Max latency so far: 1 microseconds.
Max latency so far: 16 microseconds.
Max latency so far: 17 microseconds.
Max latency so far: 392 microseconds.
Max latency so far: 397 microseconds.
Max latency so far: 638 microseconds.
Max latency so far: 1869 microseconds.
Max latency so far: 2149 microseconds.
Max latency so far: 3026 microseconds.
Max latency so far: 6022 microseconds.
992399678 total runs (avg latency: 0.0605 microseconds / 60.46 nanoseconds per run).
Worst run took 99604x longer than the average latency.
可以看到,此時的基線性能已經達到了 6.022ms,如果響應延時為12ms,那么基本可以認定為Redis變慢了,當然我測試的機器性能比較差,你們可以用自己的機器試試
注意:這個命令只在Redis所在的服務器上運行,避免網絡對基線性能的影響,只考慮服務端軟硬件環境的影響
到這里已經確定了是Redis服務變慢,那么是哪里變慢了呢,接下來將進行更詳細的說明
Redis性能影響要素
分析可能影響因素很重要,是判斷Redis性能的來源,如下圖:
圖片
在排除了網絡因素之后,可以歸納為Redis自身命令操作、文件系統和操作系統三個大因素可能導致Redis性能存在問題。
接下來的文章將圍繞這幾個要素出發排查和解決性能影響問題
Redis性能問題分析
慢日志分析
圖片
日志是個好東西,分析Mysql是否變慢我們可以通過查看慢日志的,同樣的分析Redis慢,同樣可以先看是否也存在慢日志 slowlog,這是基礎和直觀的方式。
Redis 提供的慢日志命令的統計功能,記錄了有哪些命令在執行時耗時比較長,快速定位問題。
需要配置的參數:
- ? slowlog-log-slower-than 配置對執行時間大于多少微秒(microsecond, 1秒=10^6微秒) 的命令進行記錄。線上可以設置為1000微秒,也就是1毫秒。
- ? slowlog-max-len 設置最大考驗記錄多少條記錄。slow log 本身是一個先進先出(FIFO) 隊列,當隊列大小超過該配置的值時,最舊的一條日志將被刪除。線上可以設置為1000以上。
配置如下:
//命令執行耗時超過 10 毫秒,記錄慢日志
CONFIG SET slowlog-log-slower-than 10000
//只保留最近 500 條慢日志
CONFIG SET slowlog-max-len 500
我們看下慢日志如何查詢
127.0.0.1:6379> SLOWLOG get 3
1) (integer) 32693 # 慢日志ID
2) (integer) 1593763337 # 執行時間戳
3) (integer) 5299 # 執行耗時(微秒)
4) 1) "LRANGE" # 具體執行的命令和參數
2) "user_list:2000"
3) "0"
4) "-1
注意慢日志功能比較粗糙簡單,沒有持久化記錄能力,都是記錄在內存中,沒有持久化到文件中,所以一般都是設置保留有限的慢命令條數,如果慢命令比較多,會存在不能全部記錄的情況
常見集中導致Redis變慢不合理的命令使用方式:
- ? 獲取Redis中的key時,避免使用keys *
- ? 高頻使用了 O(N) 及以上復雜度的命令,例如:SUNION、SORT、ZUNIONSTORE、ZINTERSTORE 聚合類命令
- ? O(N) 復雜度的命令,但 N 的值非常大,比如:hgetall、smembers、lrange、zrange等命令
這種情況下我們可以將復雜的聚合放在業務端處理,并且每次盡量少獲取大量數據
BigKey問題
圖片
分析慢日志時發現很多請求并不是復雜度高的命令,都是一些del、set、hset等的低復雜度命令,那么就要評估是否寫入了大key,也就是BigKey。
Bigkey 是指當 Redis 的字符串類型過大,非字符串類型元素過多 (hash,list,set等存儲中value值過多)
bigkey會帶來如下問題:
圖片
此時我們可以回想和檢查業務代碼,查看是否存在寫入bigkey的情況,評估好單個key的數據大小,避免存在過大數據。
除了代碼自查之外,可以使用命令查,如下:
[root@VM-12-10-opencloudos ~]# redis-cli -h 127.0.0.1 -p 6379 --bigkeys
-------- summary -------
Sampled 15 keys in the keyspace!
Total key length in bytes is 162 (avg len 10.80)
//最大的數據值
Biggest string found 'page4:20230921' has 12304 bytes
Biggest list found 'article:100' has 8 items
Biggest set found 'union:65:67' has 7 members
Biggest hash found 'page2:20230921' has 2 fields
Biggest zset found 'likeTopList' has 2 members
//平均值
6 strings with 24778 bytes (40.00% of keys, avg size 4129.67)
1 lists with 8 items (06.67% of keys, avg size 8.00)
6 sets with 24 members (40.00% of keys, avg size 4.00)
1 hashs with 2 fields (06.67% of keys, avg size 2.00)
1 zsets with 2 members (06.67% of keys, avg size 2.00)
--i 參數,降低掃描的執行速度,比如 --i 0.1 表示 100 毫秒執行一次,降低掃描過程中對 Redis運行實例的影響。
--bigkeys命令原理解析
Redis 在內部執行了 SCAN 命令,遍歷整個實例中所有的 key 然后針對 key 的類型,分別執行 STRLEN、LLEN、HLEN、SCARD、ZCARD 命令,來獲取 String 類型的長度、容器類型(List、Hash、Set、ZSet)的元素個數
面對bigkey問題,我們可以這些方面下手去處理:
- 1. 對大Key進行拆分
- 2. 優化使用刪除Key的命令,可使用異步刪除 unlink 命令刪除緩存
- 3. 盡量不寫入大Key
- 4. 合理使用批處理命令
key集中過期
不知道大家是否遇到過,在某個時間點Redis突然出現一波延時,而且報慢的,有時候超時還有時間規律
如果出現這種情況,就需要考慮是否存在大量key集中過期的情況,因為大量的key在某個固定時間點集中過期,在這個時間點訪問Redis時,就有可能導致延遲增加。
圖片
如果出現了這種情況,那么需要從兩個方面排查一下:
- ? 業務邏輯是否有定時任務的腳本程序,定期操作key
- ? Redis的Key數量出現集中過期清理
程序層面這個我們自己排查就好了,這里主要看下為什么Key數量集中過期,集中過期為啥造成了Redis訪問變慢
Redis的Key過期策略是怎樣的?
被動過期:只有應用發起訪問某個key 時,才判斷這個key是否已過期,如果已過期,則從Redis中刪除
主動過期:在Redis 內部維護了一個定時任務,默認每隔 100 毫秒(1秒10次)從全局的過期哈希表中隨機取出 20 個 key,判斷然后刪除其中過期的 key,如果過期 key 的比例超過了 25%,則繼續重復此過程,直到過期 key 的比例下降到 25% 以下,或者這次任務的執行耗時超過了 25 毫秒,才會退出循環
注意,這個主動過期 key 的定時任務,是在 Redis 主線程中執行的
這也是我們主要關注的問題 【主動過期清理】,那為什么會導致Redis延時呢?
因為主動過期是在Redis 主線程中執行的,也就意味著會阻塞正常的請求命令。
進一步說就是如果在執行主動過期的過程中,出現了需要大量刪除過期 key 的請求,那么此時應用程序在訪問 Redis 時,必須要等待這個過期任務執行結束,Redis 才可以繼續處理新請求,這也就是為什么此時訪問Redis會突然出現延遲。
即使刪除過期key是耗時的,也不會記錄在slowlog慢日志中哦!
這里大家估計又有疑惑了,這不是慢了嗎?
別急,這是因為slowlog記錄的是Redis服務端在命令執行前后計算每條命令的執行時長,而過期清理的時候Redis是登錄狀態,還不能處理客戶端發過來的請求,也就是在命令執行之前進行的。
這種情況我們可以這樣處理:
- ? 業務Key設置過期時間時,加上一個隨機過期時間段,比如1分鐘
- ? 通過執行info命令獲取過期Key數量【expired_keys】的統計值
- ? Redis 4.0以上版本,開啟 lazy-free 機制,把釋放內存的操作放到后臺線程中執行,避免阻塞主線程
預估內存不足
我們知道服務器的內存是有限的,這個是既定事實,而且使用Redis時都會配置當前實例可用的最大內存maxmemory和數據自動淘汰策略
maxmemory : 默認為0 不限制。
//獲取maxmemory配置的大小
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "0" //默認值是0
//可以在redis.conf中配置
maxmemory 1024mb
當使用的內存達到了 maxmemory 后,即使配置了自動淘汰策略,仍然會在之后每次寫入新數據時,操作延遲都會變長。
原因在于,當 Redis 內存達到 maxmemory 后,每次寫入新的數據之前,Redis 必須先從實例中踢出一部分數據,讓整個實例的內存維持在 maxmemory 之下,然后才能把新數據寫進來。
圖片
Redis的常用 allkeys-lru / volatile-lru 的淘汰策略
volatile-lru :利用LRU算法移除設置過過期時間的key
allkeys-lru :利用LRU算法移除任何key (和上一個相比,刪除的key包括設置過期時間和不設置過期時間的)
Redis采用近似LRU算法,實現邏輯是什么樣的?
1:每次從實例中隨機選擇一個key (樣本集),并從樣本集中挑選最長時間未使用的 key 淘汰,剩下的放入待淘汰池
2:再次隨機獲取一批樣本集,并與第一步池子的key比較,進而進行淘汰最少訪問的key,剩下的放入待淘汰池
3:循環往復上面兩個操作步驟,直到實例內存降到maxmemory值為止
假如我們淘汰策略刪除的是 bigkey,那么耗時還更久,可想而知 bigkey對Redis的危害應該很大
不過針對內存不足問題,我們也可以進行一個優化措施:
1:避免存儲 bigkey,降低釋放內存的耗時
2:合理預估內存占用,避免達到內存的使用上限
- ? 根據寫入Key的類型、數量及平均大小計算預估
- ? 寫入一小部分比例的真實業務數據,然后進行預估
3:Redis 4.0 及以上版本,開啟 layz-free 機制,把淘汰 key 釋放內存的操作放到后臺線程中執行
實際請求量超預期
一個系統處理請求是有上限的。Redis雖然處理速度很快,但是也有上限。因此在流量暴增的時候,會比較快達到Redis的處理瓶頸,這個時候整個系統也會變慢,出現slowlog等。
這個現象也比較好觀察,可以看看實例的cpu情況,如果持續100%,基本可以判定達到處理上限了。
這種情況最好我們要結合云監控,對CPU使用率、訪問的QPS進行監控,發現系統瓶頸,看是否進行擴容和調整。
ok,關于Redis變慢問題的上半部分就分享到這里了,下期將繼續更新其他可能導致Redis變慢的情況。