Redis大集群擴(kuò)容性能優(yōu)化實(shí)踐
時(shí)間:2021-10-18 16:13:15
手機(jī)看文章
掃描二維碼
隨時(shí)隨地手機(jī)看文章
[導(dǎo)讀]作者:vivo互聯(lián)網(wǎng)數(shù)據(jù)庫團(tuán)隊(duì)—YuanJianwei一、背景在現(xiàn)網(wǎng)環(huán)境,一些使用Redis集群的業(yè)務(wù)隨著業(yè)務(wù)量的上漲,往往需要進(jìn)行節(jié)點(diǎn)擴(kuò)容操作。之前有了解到運(yùn)維同學(xué)對一些節(jié)點(diǎn)數(shù)比較大的Redis集群進(jìn)行擴(kuò)容操作后,業(yè)務(wù)側(cè)反映集群性能下降,具體表現(xiàn)在訪問時(shí)延增長明顯。某些業(yè)務(wù)對R...
作者:vivo互聯(lián)網(wǎng)數(shù)據(jù)庫團(tuán)隊(duì)—Yuan Jianwei
一、背景
在現(xiàn)網(wǎng)環(huán)境,一些使用Redis集群的業(yè)務(wù)隨著業(yè)務(wù)量的上漲,往往需要進(jìn)行節(jié)點(diǎn)擴(kuò)容操作。
之前有了解到運(yùn)維同學(xué)對一些節(jié)點(diǎn)數(shù)比較大的Redis集群進(jìn)行擴(kuò)容操作后,業(yè)務(wù)側(cè)反映集群性能下降,具體表現(xiàn)在訪問時(shí)延增長明顯。
某些業(yè)務(wù)對Redis集群訪問時(shí)延比較敏感,例如現(xiàn)網(wǎng)環(huán)境對模型實(shí)時(shí)讀取,或者一些業(yè)務(wù)依賴讀取Redis集群的同步流程,會影響業(yè)務(wù)的實(shí)時(shí)流程時(shí)延。業(yè)務(wù)側(cè)可能無法接受。
為了找到這個(gè)問題的根因,我們對某一次的Redis集群遷移操作后的集群性能下降問題進(jìn)行排查。
1.1 問題描述
這一次具體的Redis集群問題的場景是:某一個(gè)Redis集群進(jìn)行過擴(kuò)容操作。業(yè)務(wù)側(cè)使用Hiredis-vip進(jìn)行Redis集群訪問,進(jìn)行MGET操作。
業(yè)務(wù)側(cè)感知到訪問Redis集群的時(shí)延變高。
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進(jìn)行訪問;
- Redis集群的節(jié)點(diǎn)數(shù)比較大,規(guī)模是100 ;
- 集群之前存在擴(kuò)容操作。
1.3 觀察現(xiàn)象
因?yàn)闀r(shí)延變高,我們從幾個(gè)方面進(jìn)行排查:
- 帶寬是否打滿;
- CPU是否占用過高;
- OPS是否很高;
通過簡單的監(jiān)控排查,帶寬負(fù)載不高。但是發(fā)現(xiàn)CPU表現(xiàn)異常:
1.3.1 對比ops和CPU負(fù)載
觀察業(yè)務(wù)反饋使用的MGET和CPU負(fù)載,我們找到了對應(yīng)的監(jiān)控曲線。
從時(shí)間上分析,MGET和CPU負(fù)載高并沒有直接關(guān)聯(lián)。業(yè)務(wù)側(cè)反饋的是MGET的時(shí)延普遍增高。此處看到MGET的OPS和CPU負(fù)載是錯(cuò)峰的。
此處可以暫時(shí)確定業(yè)務(wù)請求和CPU負(fù)載暫時(shí)沒有直接關(guān)系,但是從曲線上可以看出:在同一個(gè)時(shí)間軸上,業(yè)務(wù)請求和cpu負(fù)載存在錯(cuò)峰的情況,兩者間應(yīng)該有間接關(guān)系。
1.3.2 對比Cluster指令OPS和CPU負(fù)載
由于之前有運(yùn)維側(cè)同事有反饋集群進(jìn)行過擴(kuò)容操作,必然存在slot的遷移。
考慮到業(yè)務(wù)的客戶端一般都會使用緩存存放Redis集群的slot拓?fù)湫畔ⅲ虼藨岩蒀luster指令會和CPU負(fù)載存在一定聯(lián)系。
我們找到了當(dāng)中確實(shí)有一些聯(lián)系:
此處可以明顯看到:某個(gè)實(shí)例在執(zhí)行Cluster指令的時(shí)候,CPU的使用會明顯上漲。
根據(jù)上述現(xiàn)象,大致可以進(jìn)行一個(gè)簡單的聚焦:
- 業(yè)務(wù)側(cè)執(zhí)行MGET,因?yàn)橐恍┰驁?zhí)行了Cluster指令;
- Cluster指令因?yàn)橐恍┰驅(qū)е翪PU占用較高影響其他操作;
- 懷疑Cluster指令是性能瓶頸。
同時(shí),引申幾個(gè)需要關(guān)注的問題:
- 為什么會有較多的Cluster指令被執(zhí)行?
- 為什么Cluster指令執(zhí)行的時(shí)候CPU資源比較高?
- 為什么節(jié)點(diǎn)規(guī)模大的集群遷移slot操作容易“中招”?
二、問題排查
2.1 Redis熱點(diǎn)排查
我們對一臺現(xiàn)場出現(xiàn)了CPU負(fù)載高的Redis實(shí)例使用perf top進(jìn)行簡單的分析:
從上圖可以看出來,函數(shù)(ClusterReplyMultiBulkSlots)占用的CPU資源高達(dá) 51.84%,存在異常。
2.1.1 ClusterReplyMultiBulkSlots實(shí)現(xiàn)原理
我們對clusterReplyMultiBulkSlots函數(shù)進(jìn)行分析:
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) {
/*注意:此處是對當(dāng)前Redis節(jié)點(diǎn)記錄的集群所有主節(jié)點(diǎn)都進(jìn)行了遍歷*/
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é)點(diǎn)。備節(jié)點(diǎn)的信息會從主節(jié)點(diǎn)側(cè)獲取。*/
if (!nodeIsMaster(node) || node->numslots == 0) continue;
for (j = 0; j < CLUSTER_SLOTS; j ) {
/*注意:此處是對當(dāng)前節(jié)點(diǎn)中記錄的所有slot進(jìn)行了遍歷*/
int bit, i;
/*確認(rèn)當(dāng)前節(jié)點(diǎn)是不是占有循環(huán)終端的slot*/
if ((bit = clusterNodeGetSlotBit(node,j)) != 0) {
if (start == -1) start = j;
}
/*簡單分析,此處的邏輯大概就是找出連續(xù)的區(qū)間,是的話放到返回中;不是的話繼續(xù)往下遞歸slot。
如果是開始的話,開始一個(gè)連續(xù)區(qū)間,直到和當(dāng)前的不連續(xù)。*/
if (start != -1