Redis大集群擴容性能優(yōu)化實踐
時間:2021-10-18 16:13:15
手機看文章
掃描二維碼
隨時隨地手機看文章
[導讀]作者:vivo互聯(lián)網(wǎng)數(shù)據(jù)庫團隊—YuanJianwei一、背景在現(xiàn)網(wǎng)環(huán)境,一些使用Redis集群的業(yè)務(wù)隨著業(yè)務(wù)量的上漲,往往需要進行節(jié)點擴容操作。之前有了解到運維同學對一些節(jié)點數(shù)比較大的Redis集群進行擴容操作后,業(yè)務(wù)側(cè)反映集群性能下降,具體表現(xiàn)在訪問時延增長明顯。某些業(yè)務(wù)對R...
作者:vivo互聯(lián)網(wǎng)數(shù)據(jù)庫團隊—Yuan Jianwei
一、背景
在現(xiàn)網(wǎng)環(huán)境,一些使用Redis集群的業(yè)務(wù)隨著業(yè)務(wù)量的上漲,往往需要進行節(jié)點擴容操作。
之前有了解到運維同學對一些節(jié)點數(shù)比較大的Redis集群進行擴容操作后,業(yè)務(wù)側(cè)反映集群性能下降,具體表現(xiàn)在訪問時延增長明顯。
某些業(yè)務(wù)對Redis集群訪問時延比較敏感,例如現(xiàn)網(wǎng)環(huán)境對模型實時讀取,或者一些業(yè)務(wù)依賴讀取Redis集群的同步流程,會影響業(yè)務(wù)的實時流程時延。業(yè)務(wù)側(cè)可能無法接受。
為了找到這個問題的根因,我們對某一次的Redis集群遷移操作后的集群性能下降問題進行排查。
1.1 問題描述
這一次具體的Redis集群問題的場景是:某一個Redis集群進行過擴容操作。業(yè)務(wù)側(cè)使用Hiredis-vip進行Redis集群訪問,進行MGET操作。
業(yè)務(wù)側(cè)感知到訪問Redis集群的時延變高。
1.2 現(xiàn)網(wǎng)環(huán)境說明
- 目前現(xiàn)網(wǎng)環(huán)境部署的Redis版本多數(shù)是3.x或者4.x版本;
- 業(yè)務(wù)訪問Redis集群的客戶端品類繁多,較多的使用Jedis。本次問題排查的業(yè)務(wù)使用客戶端Hiredis-vip進行訪問;
- Redis集群的節(jié)點數(shù)比較大,規(guī)模是100 ;
- 集群之前存在擴容操作。
1.3 觀察現(xiàn)象
因為時延變高,我們從幾個方面進行排查:
- 帶寬是否打滿;
- CPU是否占用過高;
- OPS是否很高;
通過簡單的監(jiān)控排查,帶寬負載不高。但是發(fā)現(xiàn)CPU表現(xiàn)異常:

1.3.1 對比ops和CPU負載
觀察業(yè)務(wù)反饋使用的MGET和CPU負載,我們找到了對應(yīng)的監(jiān)控曲線。
從時間上分析,MGET和CPU負載高并沒有直接關(guān)聯(lián)。業(yè)務(wù)側(cè)反饋的是MGET的時延普遍增高。此處看到MGET的OPS和CPU負載是錯峰的。

此處可以暫時確定業(yè)務(wù)請求和CPU負載暫時沒有直接關(guān)系,但是從曲線上可以看出:在同一個時間軸上,業(yè)務(wù)請求和cpu負載存在錯峰的情況,兩者間應(yīng)該有間接關(guān)系。
1.3.2 對比Cluster指令OPS和CPU負載
由于之前有運維側(cè)同事有反饋集群進行過擴容操作,必然存在slot的遷移。
考慮到業(yè)務(wù)的客戶端一般都會使用緩存存放Redis集群的slot拓撲信息,因此懷疑Cluster指令會和CPU負載存在一定聯(lián)系。
我們找到了當中確實有一些聯(lián)系:

此處可以明顯看到:某個實例在執(zhí)行Cluster指令的時候,CPU的使用會明顯上漲。
根據(jù)上述現(xiàn)象,大致可以進行一個簡單的聚焦:
- 業(yè)務(wù)側(cè)執(zhí)行MGET,因為一些原因執(zhí)行了Cluster指令;
- Cluster指令因為一些原因?qū)е翪PU占用較高影響其他操作;
- 懷疑Cluster指令是性能瓶頸。
同時,引申幾個需要關(guān)注的問題:
- 為什么會有較多的Cluster指令被執(zhí)行?
- 為什么Cluster指令執(zhí)行的時候CPU資源比較高?
- 為什么節(jié)點規(guī)模大的集群遷移slot操作容易“中招”?
二、問題排查
2.1 Redis熱點排查
我們對一臺現(xiàn)場出現(xiàn)了CPU負載高的Redis實例使用perf top進行簡單的分析:

從上圖可以看出來,函數(shù)(ClusterReplyMultiBulkSlots)占用的CPU資源高達 51.84%,存在異常。
2.1.1 ClusterReplyMultiBulkSlots實現(xiàn)原理
我們對clusterReplyMultiBulkSlots函數(shù)進行分析:
void clusterReplyMultiBulkSlots(client *c) {
/* Format: 1) 1) start slot
* 2) end slot
* 3) 1) master IP
* 2) master port
* 3) node ID
* 4) 1) replica IP
* 2) replica port
* 3) node ID
* ... continued until done
*/
int num_masters = 0;
void *slot_replylen = addDeferredMultiBulkLength(c);
dictEntry *de;
dictIterator *di = dictGetSafeIterator(server.cluster->nodes);
while((de = dictNext(di)) != NULL) {
/*注意:此處是對當前Redis節(jié)點記錄的集群所有主節(jié)點都進行了遍歷*/
clusterNode *node = dictGetVal(de);
int j = 0, start = -1;
/* Skip slaves (that are iterated when producing the output of their
* master) and masters not serving any slot. */
/*跳過備節(jié)點。備節(jié)點的信息會從主節(jié)點側(cè)獲取。*/
if (!nodeIsMaster(node) || node->numslots == 0) continue;
for (j = 0; j < CLUSTER_SLOTS; j ) {
/*注意:此處是對當前節(jié)點中記錄的所有slot進行了遍歷*/
int bit, i;
/*確認當前節(jié)點是不是占有循環(huán)終端的slot*/
if ((bit = clusterNodeGetSlotBit(node,j)) != 0) {
if (start == -1) start = j;
}
/*簡單分析,此處的邏輯大概就是找出連續(xù)的區(qū)間,是的話放到返回中;不是的話繼續(xù)往下遞歸slot。
如果是開始的話,開始一個連續(xù)區(qū)間,直到和當前的不連續(xù)。*/
if (start != -1