作者:vivo 網際網路資料庫團隊- Du Ting
在Redis運維過程中,由於Bigkey 的存在,會影響業務程式的響應速度,嚴重的還會造成可用性損失,DBA也一直和業務開發方強調 Bigkey 的規避方法以及危害。
在Redis運維過程中,由於Bigkey的存在,會影響業務程式的響應速度,嚴重的還會造成可用性損失,DBA也一直和業務開發方強調 Bigkey 的規避方法以及危害,但是Bigkey一直沒有完全避免。全網Redis叢集有2200個以上,範例數量達到4.5萬以上,在當前階段進行一次全網 Bigkey檢查,估計需要以年為時間單位,非常耗時。我們需要新的思路去解決Bigkey問題。
在Redis中,一個字串型別最大可以到512MB,一個二級資料結構(比如hash、list、set、zset等)可以儲存大約40億個(2^32-1)個元素,但實際上不會達到這麼大的值,一般情況下如果達到下面的情況,就可以認為它是Bigkey了。
我們遇到的Bigkey一般都是由於程式設計不當或者對於資料規模預料不清楚造成的,比如以下的情況。
這三種情況,都是我們實際運維中遇到的,需要謹慎使用,合理優化。
我們在運維中,遇到Bigkey的情況下,會導致一些問題,會觸發監控報警,嚴重的還會影響Redis範例可用性,進而影響業務可用性,在需要水平擴容時候,可能導致水平擴容失敗。
記憶體空間不均勻會不利於叢集對記憶體的統一管理,有資料丟失風險。下圖中的三個節點是同屬於一個叢集,它們的key的數量比較接近,但記憶體容量相差比較多,存在Bigkey的範例佔用的記憶體多了4G以上了。
可以使用使用Daas平臺「工具集-操作項管理」,選擇對應的slave範例執行分析,找出具體的Bigkey。
Redis是單執行緒工作的,通俗點講就是同一時間只能處理一個Redis的存取命令,操作Bigkey的命令通常比較耗時,這段時間Redis不能處理其他命令,其他命令只能阻塞等待,這樣會造成使用者端阻塞,導致使用者端存取超時,更嚴重的會造成master-slave的故障切換。造成阻塞的操作不僅僅是業務程式的存取,還有key的自動過期的刪除、del刪除命令,對於Bigkey,這些操作也需要謹慎使用。
超時阻塞案例
我們遇到一個這樣超時阻塞的案例,業務方反映程式存取Redis叢集出現超時現象,hkeys存取Redis的平均響應時間在200毫秒左右,最大響應時間達到了500毫秒以上,如下圖。
hkeys是獲取所有雜湊表中的欄位的命令,分析應該是叢集中某些範例存在hash型別的Bigkey,導致hkeys命令執行時間過長,發生了阻塞現象。
1.使用Daas平臺「服務監控-資料庫範例監控」,選擇master節點,選擇Redis響應時間監控指標「redis.instance.latency.max」,如下圖所示,從監控圖中我們可以看到
(1)正常情況下,該範例的響應時間在0.1毫秒左右。
(2)監控指標上面有很多突刺,該範例的響應時間到了70毫秒左右,最大到了100毫秒左右,這種情況就是該範例會有100毫秒都在處理Bigkey的存取命令,不能處理其他命令。
通過檢視監控指標,驗證了我們分析是正確的,是這些監控指標的突刺造成了hkeys命令的響應時間比較大,我們找到了具體的master範例,然後使用master範例的slave去分析下Bigkey情況。
2.使用Daas平臺「工具集-操作項管理」,選擇slave範例執行分析,分析結果如下圖,有一個hash型別key有12102218個fields。
3. 和業務溝通,這個Bigkey是連續存放了30天的業務資料了,建議根據二次hash方式拆分成多個key,也可把30天的資料根據分鐘級別拆分成多個key,把每個key的元素數量控制在5000以內,目前業務正在排期優化中。優化後,監控指標的響應時間的突刺就會消失了。
Bigkey的value比較大,也意味著每次獲取要產生的網路流量較大,假設一個Bigkey為10MB,使用者端每秒存取量為100,那麼每秒產生1000MB的流量,對於普通的千兆網路卡(按照位元組算是128MB/s)的伺服器來說簡直是滅頂之災。而且我們現在的Redis伺服器是採用單機多範例的方式來部署Redis範例的,也就是說一個Bigkey可能會對同一個伺服器上的其他Redis叢集範例造成影響,影響到其他的業務。
我們在運維中經常做的變更操作是水平擴容,就是增加Redis叢集的節點數量來達到擴容的目的,這個水平擴容操作就會涉及到key的遷移,把原範例上的key遷移到新擴容的範例上。當要對key進行遷移時,是通過migrate命令來完成的,migrate實際上是通過dump + restore + del三個命令組合成原子命令完成,它在執行的時候會阻塞進行遷移的兩個範例,直到以下任意結果發生才會釋放:遷移成功,遷移失敗,等待超時。如果key的遷移過程中遇到Bigkey,會長時間阻塞進行遷移的兩個範例,可能造成使用者端阻塞,導致使用者端存取超時;也可能遷移時間太長,造成遷移超時導致遷移失敗,水平擴容失敗。
遷移失敗案例
我們也遇到過一些因為Bigkey擴容遷移失敗的案例,如下圖所示,是一個Redis叢集水平擴容的工單,需要進行key的遷移,當工單執行到60%的時候,遷移失敗了。
1. 進入工單找到失敗的範例,使用失敗範例的slave節點,在Daas平臺的「工具集-操作項管理」進行Bigkey分析。
2. 經過分析找出了hash型別的Bigkey有8421874個fields,正是這個Bigkey導致遷移時間太長,超過了遷移時間限制,導致工單失敗了。
3.和業務溝通,這些key是記錄使用者存取系統的某個功能模組的ip地址的,存取該功能模組的所有ip都會記錄到給key裡面,隨著時間的積累,這個key變的越來越大。同樣是採用拆分的方式進行優化,可以考慮按照時間日期維度來拆分,就是一段時間段的存取ip記錄到一個key中。
4.Bigkey優化後,擴容的工單可以重試,完成叢集擴容操作。
Bigkey首先需要重源頭治理,防止Bigkey的產生;其次是需要能夠及時的發現,發現後及時處理。分析Bigkey的方法不少,這裡介紹兩種比較常用的方法,也是Daas平臺分析Bigkey使用的兩種方式,分別是Bigkeys命令分析法、RDB檔案分析法。
Redis4.0及以上版本提供了--Bigkeys命令,可以分析出範例中每種資料結構的top 1的Bigkey,同時給出了每種資料型別的鍵值個數以及平均大小。執行--Bigkeys命令時候需要注意以下幾點:
Daas平臺整合了基於原生--Bigkeys程式碼實現的查詢Bigkey的方式,這個方式的缺點是隻能計算每種資料結構的top1,如果有些資料結構有比較多的Bigkey,是查詢不出來的。該方式相對比較安全,已經開放出來給業務開發同學使用。
藉助開源的工具,比如rdb-tools,分析Redis範例的RDB檔案,找出其中的Bigkey,這種方式需要生成RDB檔案,需要注意以下幾點:
Daas平臺整合了基於RDB檔案分析程式碼實現的查詢Bigkey的方式,可以根據實際需求自定義填寫N,分析的top N個Bigkey。該方式相對有一定風險,只有DBA有許可權執行分析。
通過巡檢,可以暴露出隱患,提前解決,避免故障的發生,進行全網Bigkey的巡檢,是避免Bigkey故障的比較好的方法。由於全網Redis範例數量非常大,分析的速度比較慢,使用當前的分析方法很難完成。為了解決這個問題,儲存研發組分散式資料庫同學計劃開發一個高效的RDB解析工具,然後通過大規模解析RDB檔案來分析Bigkey,可以提高分析速度,實現Bigkey的巡檢。
優化Bigkey的原則就是string減少字串長度,list、hash、set、zset等減少元素數量。當我們知道哪些key是Bigkey時,可以把單個key拆分成多個key,比如以下拆分方式可以參考。
我們全網Redis叢集有2200以上,範例數量達到4.5萬以上,有的比較大的叢集的範例數量達到了1000以上,前面提到的兩種Bigkey分析工具還都是範例維度分析,對於範例數量比較大的叢集,進行全叢集分析也是比較耗時的,為了提高分析效率,從以下幾個方面進行優化:
目前情況,我們有一些Bigkey的發現是被動的,一些是在水平擴容時候發現的,由於Bigkey的存在導致擴容失敗了,嚴重的還觸發了master-slave的故障切換,這個時候可能已經造成業務程式存取超時,導致了可用性下降。
我們分析了Daas平臺的水平擴容時遷移key的過程及影響引數,內容如下:
(1)【cluster-node-timeout】:控制叢集的節點切換引數,master堵塞超過cluster-node-timeout/2這個時間,就會主觀判定該節點下線pfail狀態,如果遷移Bigkey阻塞時間超過cluster-node-timeout/2,就可能會導致master-slave發生切換。
(2)【migrate timeout】:控制遷移io的超時時間,超過這個時間遷移沒有完成,遷移就會中斷。
(3)【遷移重試周期】:遷移的重試周期是由水平擴容的節點數決定的,比如一個叢集擴容10個節點,遷移失敗後的重試周期就是10次。
(4)【一個遷移重試周期內的重試次數】:在一個起遷移重試周期內,會有3次重試遷移,每一次的migrate timeout的時間分別是10秒、20秒、30秒,每次重試之間無間隔。
比如一個叢集擴容10個節點,遷移時候遇到一個Bigkey,第一次遷移的migrate timeout是10秒,10秒後沒有完成遷移,就會設定migrate timeout為20秒重試,如果再次失敗,會設定migrate timeout為30秒重試,如果還是失敗,程式會遷移其他新9個的節點,但是每次在遷移其他新的節點之前還會分別設定migrate timeout為10秒、20秒、30秒重試遷移那個遷移失敗的Bigkey。這個重試過程,每個重試周期阻塞(10+20+30)秒,會重試10個週期,共阻塞600秒。其實後面的9個重試周期都是無用的,每次重試之間沒有間隔,會連續阻塞了Redis範例。
(5)【遷移失敗紀錄檔】:遷移失敗後,記錄的紀錄檔沒有包括遷移節點、solt、key資訊,不能根據紀錄檔立即定位到問題key。
我們對這個遷移過程做了優化,具體如下:
(1)【cluster-node-timeout】:預設是60秒,在遷移之前設定為15分鐘,防止由於遷移Bigkey阻塞導致master-slave故障切換。
(2)【migrate timeout】:為了最大限度減少範例阻塞時間,每次重試的超時時間都是10秒,3次重試之間間隔30秒,這樣最多隻會連續阻塞Redis範例10秒。
(3)【重試次數】:遷移失敗後,只重試3次(重試是為了避免網路抖動等原因造成的遷移失敗),每次重試間隔30秒,重試3次後都失敗了,會暫停遷移,紀錄檔記錄下Bigkey,去掉了其他節點遷移的重試。
(4)【優化紀錄檔記錄】:遷移失敗紀錄檔記錄遷移節點、solt、key資訊,可以立即定位到問題節點及key。
本文通過對Bigkey的分析,重點介紹了在運維中對bigkey問題的處理思路、解決方式。首先是需要從源頭治理,防止Bigkey形成,DBA應該加強對業務開發同學bigkey相關問題的宣導;其次是需要具備及時發現的能力,這個也是我們現在的不足之處。我們後面會從Bigkey巡檢、Bigkey分析工具的這兩個方面,提高Bigkey發現能力。
參考資料: