天天看點

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

在2016杭州雲栖大會的“開源資料庫之redis專場”上,來自唯品會的進階資料工程師申政帶來了題為《redis在唯品會的應用實踐》的精彩分享。分享中,他主要介紹redis叢集架構演進、redis使用經驗以及唯品會對redis二次開發實踐積累三部分,幹貨滿滿,精彩不容錯過。

以下内容根據演講ppt及現場分享整理。

redis叢集架構演進<b></b>

目前在唯品會内對redis的使用屬于重量級别,目前在唯品會内大概有8000個redis執行個體、1000台實體機、500個應用。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

上圖是唯品會對redis使用的演進曆史圖。在2014年7月左右,采用的是用戶端分片(client sharding)的架構;2014年7月到2015年12月期間,主要采用的是twemproxy叢集模式;從2015年6月至今,唯品會内主要采用redis cluster架構。

下面簡單介紹上述三種架構的優缺點。

<b>client sharding架構</b><b></b>

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

client shareding的架構如上圖所示,從圖上可以看出該架構十分簡單穩定,但它需要業務方面自定義key分布,進而增加了業務開發難度和工作量;其次該架構的線上擴容能力非常弱,并且不能在資料庫層面提供failover能力。

<b>twemproxy架構</b><b></b>

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

支援資料自分片和一緻性hash

同時支援redis和mc協定

支援pipeline、mget、mset等多key操作

twemproxy自身擴容簡單,可以做到使用者無感覺,降低了業務開發複雜度

事有正反面,在使用twemproxy過程中也發現其存在很多不足:首先該架構十分複雜,導緻機器成本高;其次,redis和mc擴容比較難;此外,該架構分為三層,每一個環節都可能成為叢集的性能瓶頸;用戶端的請求需要經過三層的請求鍊路,導緻請求相應時間長;最後尤其值得注意的一點是:它的運維成本很高。

<b>redis cluster</b><b></b>

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

redis cluster架構如上圖所示,從圖中可以看出:它是一個無中心架構,叢集中每個節點完全對等,任一節點都與其他節點保持一個長連接配接,通過高速協定維護整個叢集的狀态資訊;并且該架構還提供了自動failover和線上擴縮容能力;同時采用單層架構,相應時間短;此外,由于該架構中用戶端與redis直接相連,相比于twemproxy架構,節省了一半的機器開銷。

但該架構對用戶端依賴非常重,需要智能用戶端支援;對mget、mset、pipeline支援不友好。

一些經驗<b></b>

下面分享一下在使用redis過程中踩過的坑,供大家借鑒。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

是以,建議在使用twemproxy時,一次pipeline包含的指令量适中,最好不要超過1000個。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

按照twemproxy執行hash規則,當後端server出現問題時,理論上隻會影響目前server内的key。但我們遇到的問題是:當其中的一個server出現問題時,其他server傳回請求都出現問題。這是由twemproxy代碼結構所決定的,它對每一個用戶端的連結都有一個隊列,當隊列頭部的請求恰好是出現問題server的請求(不能響應到twemproxy),則該請求會将隊列中後續的其他server請求全部卡死,此時這些請求都緩存在twemproxy,不能相應到用戶端,進而導緻twemproxy的記憶體飙升。

還有一點需要注意:twemproxy中timeout參數,預設是永遠等待,即便server沒用相應,但其仍認為該server是正常的,一直等待下去。是以在使用twemproxy時,務必要注意timeout參數的設定。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

twemproxy中設計了剔除政策。有時twemproxy下面挂載的redis/mc并沒有挂掉,網絡也沒出現問題,但twemproxy會剔除某些server。這是由twemprox自身的設計缺陷導緻,在twemproxy中存在server_connections和server_failure_limit兩個參數,前者表示twemproxy與後端server建立長連接配接的數量;後者是twemproxy與後端server出現問題的連接配接數,如果連接配接連續出現問題,則觸發剔除政策。

在實際使用時,建議設定server_connections數目小于server_failure_limit,以規避剔除現象的發生。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

這是由lvs機制導緻的:lvs内有一個專門維護用戶端向後端發起連接配接數量的表,其内置了exprie機制,當連接配接在15min沒有活躍的請求時,會将該連接配接從表内剔除,而不通知雙方。此時,twemproxy認為該連接配接是正常的,而用戶端發現連接配接不通時,會建立連接配接繼續發送請求,長此以往,兩者的連接配接數差距越來越大;并且twemproxy的檔案描述符會消耗很快,直到不能再正常建立連接配接。

為了解決該問題,在twemproxy中增加了tcpkeepalive功能,完美地規避了該情況的發生。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

如果在使用twemproxy時,發現線上twemproxy記憶體監控曲線如上圖所示,并且在業務中還用到了mset指令,基本上可以确定是由mset引起的記憶體洩露。

在mset指令進行拆分時,由于特殊情景導緻記憶體不被釋放,進而導緻twemproxy記憶體越來越大。應對該問題,可以采用重新開機twemproxy進行解決。

在使用redis cluster的過程中,唯品會積累了一些實踐技巧,先總結如下:

 cluster-require-full-coverage建議為no(官方預設為yes)

 cluster-node-timeout建議适當增大

 jediscluster注意捕獲異常maxredirectionsexception(該異常表示指令執行不成功)

 不建議使用mget、mset等multi-key指令

 盡量使用官方redis-trib腳本管理叢集,不要輕易使用cluster指令

二次開發<b></b>

在使用redis過程中,為了更加滿足業務需求,唯品會對其進行了二次開發。<b></b>

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制
一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

目前唯品會線上最大的twemproxy叢集用到的執行個體超過100個,如果想更改twemproxy配置時,工作量是比較大的。是以,唯品會在twemproxy叢集中內建zookeeper,twemproxy叢集的配置資訊統一由zookeeper管理、分發,使用者無感覺。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

唯品會對mc也進行可二次開發,由于mc中不存在主從概念,但mv叢集中其中一個節點挂掉之後會對後端的資料庫造成很大的壓力。為了解決該問題,在twemproxy層面增加了複制池(replication pool)功能,在master pool和slave pool之間建立主從關系,用戶端同時寫入兩個pool内;讀取時,首先讀取master pool中内容,當master pool中無資料時,并不會回傳到資料庫,而是從slave pool中擷取資料,有效減輕由于mc挂掉帶給後端資料庫的壓力。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

借鑒redis,在twemproxy中增加了slowlog功能,停留時間;此外,還增加簡單統計資訊功能,将一天分為幾個時間段,每個時間段記錄slowlog次數,便于定位slowlog和解決slowlog。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制
一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

很多使用者在使用redis cluster是都會遇到這樣問題:flushall删不掉資料;明明master是存活的,網絡也正常,但cluster卻執行了failover。這是因為redis是單線程的,在執行時間複雜度為o(n)指令時,線程被阻塞,無法響應其他節點的ping指令,造成假死現象,進而導緻主從切換。

針對該問題的處理方式是:增加了額外程序監聽額外端口(extra-port),當超過cluster-node-timeout/2s時間,ping請求沒有收到回複,主動向extra-port發送ping請求。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

最初上線redis cluster時,很多用戶端并不是友善支援,是以增加了redis cluster用戶端功能,在hiredis上進行功能改造使其支援叢集模式,用于:

 解析并更新路由表

 處理moved/ask錯誤

 内含max redirect機制

 支援mget、mset、pipeline、異步api

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制
一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

redis migrate具體遷移步驟如上所示,這其中關鍵點是左側的配置檔案,這裡不不再一一叙述。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

reids多線程是目前唯品會正在改造,借鑒于上文提到的twemproxy多線程改造,将redis改造成master+n個worker多線程模型:用戶端有worker線程管理;多線程中不可能避免有鎖的存在,redis中存在db級别讀寫鎖,通過單獨背景線程處理過期key和dict維護。

目前redis多線程實作支援五大資料結構(string/list/hash/set/zset);支援100個redis指令(包括管理指令)。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

redis多線程中,為了降低db鎖競争,引入了邏輯db。一個邏輯db包含n個真實實體db,使用者使用的是邏輯db。一個邏輯db的所有key分散在各個實體db上嗎,每個實體db擁有一把讀寫鎖——pthread_rwlock_t。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

上圖是redis處理用戶端請求的指令過程,這裡不再一一叙述。每個worker線程都會進行上圖六個步驟,db鎖的競争主要是在第四步和第五步上,其他步驟worker之間完全并行。

<b>redis多線程-性能測試</b><b></b>

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

上圖是用于redis多線程性能測試的測試環境配置。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

測試用例圖上圖所示,從圖上可以看出set的效率為34萬左右;get效率為40萬左右。

一步一個腳印:解密唯品會中Redis叢集架構演進與功能定制

熱key問題是使用redis中不可避免的問題。在redis多線程中,遇到熱key首先将其拆分,将其分散到不同的redis執行個體中。上圖是多線程中針對熱key的性能測試:隻寫一個key時,四個線程的qps可以達到30萬/s;隻讀一個key時,四個線程的qps可以達到43萬/s,一定程度上有效改善了熱key問題。

<b>附錄:</b><b></b>