Redis熱點key尋找與優化

1.客戶端

客戶端實際上是距離key」最近」的地方,由於Redis命令就是從客戶端發出的,例如在客戶端設置全局字典(key和調用次數),每次調用Redis命令時,使用這個字典進行記錄,以下所示。redis

 

1緩存

2網絡

3數據結構

4架構

5併發

6運維

7異步

8分佈式

9高併發

10

11

12

 

public static final AtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create();

String get(String key) {

counterKey(key);

//ignore

}

String set(String key, String value) {

counterKey(key);

//ignore

}

void counterKey(String key) {

ATOMIC_LONG_MAP.incrementAndGet(key);

}

爲了減小對客戶端代碼的侵入,能夠在Redis客戶端的關鍵部分進行計數,例如Jedis的Connection類中的sendCommand方法是全部命令執行的樞紐。

 

1

2

3

4

5

6

7

 

public Connection sendCommand(final ProtocolCommand cmd, final byte[]... args) {

//從參數中獲取key

String key = analysis(args);

//計數

counterKey(key);

//ignore

}

同時爲了防止ATOMIC_LONG_MAP過大,能夠對其進行按期清理。

 

1

2

3

 

public void scheduleCleanMap() {

ERROR_NAME_VALUE_MAP.clear();

}

使用客戶端進行熱點key的統計很是容易實現,可是同時問題也很是多:

(1). 沒法預知key的個數,存在內存泄露的危險。

(2). 對於客戶端代碼有侵入,各個語言的客戶端都須要維護此邏輯,維護成本較高。

(3). 只能瞭解當前客戶端的熱點key,沒法實現規模化運維統計。

固然除了使用本地字典計數外,還可使用其餘存儲來完成異步計數,從而解決本地內存泄露問題。可是一樣會存在第(2)(3)個問題。

2.代理端

像Twemproxy、Codis這些基於代理的Redis分佈式架構,全部客戶端的請求都是經過代理端完成的,以下圖所示。此架構是最適合作熱點key統計的,由於代理是全部Redis客戶端和服務端的橋樑。但並非全部Redis都是採用此種架構。

3. Redis服務端

使用monitor命令統計熱點key是不少開發和運維人員首先想到,monitor命令能夠監控到Redis執行的全部命令,下面爲一次monitor命令執行後部分結果。

 

1

2

3

4

5

6

7

8

 

1477638175.920489 [0 10.16.xx.183:54465] "GET" "tab:relate:kp:162818"

1477638175.925794 [0 10.10.xx.14:35334] "HGETALL" "rf:v1:84083217_83727736"

1477638175.938106 [0 10.16.xx.180:60413] "GET" "tab:relate:kp:900"

1477638175.939651 [0 10.16.xx.183:54320] "GET" "tab:relate:kp:15907"

...

1477638175.962519 [0 10.10.xx.14:35334] "GET" "tab:relate:kp:3079"

1477638175.963216 [0 10.10.xx.14:35334] "GET" "tab:relate:kp:3079"

1477638175.964395 [0 10.10.xx.204:57395] "HGETALL" "rf:v1:80547158_83076533"

如上圖所示,利用monitor的結果就能夠統計出一段時間內的熱點key排行榜,命令排行榜,客戶端分佈等數據,例以下面的僞代碼統計了最近10萬條命令中的熱點key。

 

1

2

3

4

5

6

7

8

9

10

 

//獲取10萬條命令

List<String> keyList = redis.monitor(100000);

//存入到字典中,分別是key和對應的次數

AtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create();

//統計

for (String command : commandList) {

ATOMIC_LONG_MAP.incrementAndGet(key);

}

//後續統計和分析熱點key

statHotKey(ATOMIC_LONG_MAP);

Facebook開源的redis-faina 正是利用上述原理使用Python語言實現的,例以下面獲取最近10萬條命令的熱點key、熱點命令、耗時分佈等數據。爲了減小網絡開銷以及加快輸出緩衝區的消費速度,monitor儘量在本機執行。

redis-cli -p 6380 monitor | head -n 100000 | ./redis-faina.py

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

 

Overall Stats

========================================

Lines Processed 50000

Commands/Sec 900.48

Top Prefixes

========================================

tab 27565 (55.13%)

rf 15111 (30.22%)

ugc 2051 (4.10%)

...

Top Keys

========================================

tab:relate:kp:9350 2110 (4.22%)

tab:relate:kp:15907 1594 (3.19%)

...

Top Commands

========================================

GET 25700 (51.40%)

HGETALL 15111 (30.22%)

...

Command Time (microsecs)

========================================

Median 622.75

75% 1504.0

90% 2820.0

99% 6798.0

此種方法會有兩個問題:

(1) 本書屢次強調monitor命令在高併發條件下,會存在內存暴增和影響Redis性能的隱患,因此此種方法適合在短期內使用。
(2) 只能統計一個Redis節點的熱點key,對於Redis集羣須要進行彙總統計。

4. 機器層面

第四章咱們介紹過,Redis客戶端使用TCP協議與服務端進行交互,通訊協議採用的是RESP。若是站在機器的角度,能夠經過對機器上全部Redis端口的TCP數據包進行抓取完成熱點key的統計,以下圖所示。

此種方法對於Redis客戶端和服務端來講毫無侵入,是比較完美的方案,可是依然存在兩個問題:

(1) 須要必定的開發成本,可是一些開源方案實現了該功能,例如ELK(ElasticSearch Logstash Kibana)體系下的packetbeat[2] 插件,能夠實現對Redis、MySQL等衆多主流服務的數據包抓取、分析、報表展現。

(2) 因爲是以機器爲單位進行統計,要想了解一個集羣的熱點key,須要進行後期彙總。

最後經過下給出上述四種方案的特色。

方案 優勢 缺點
客戶端 實現簡單 內存泄露隱患
維護成本高
只能統計單個客戶端
代理 代理是客戶端和服務端的橋樑,實現最方便最系統 增長代理端的開發部署成本
服務端 實現簡單 monitor自己的使用成本和危害,只能短期使用
只能統計單個Redis節點
機器TCP流量 對於客戶端和服務端無侵入和影響 須要專業的運維團隊開發,而且增長了機器的部署成本

最後咱們給出三種解決熱點key問題的思路,具體選用那種要根據具體業務場景來決定。

(1) 拆分複雜數據結構: 若是當前key的類型是一個二級數據結構,例如哈希類型。若是該哈希元素個數較多,能夠考慮將當前hash進行拆分,這樣該熱點key能夠拆分爲若干個新的key分佈到不一樣Redis節點上,從而減輕壓力。

(2) 遷移熱點key:以Redis Cluster爲例,能夠將熱點key所在的slot單獨遷移到一個新的Redis節點上,但此操做會增長運維成本。

(3) 本地緩存加通知機制:能夠將熱點key放在業務端的本地緩存中,由於是在業務端的本地內存中,處理能力要高出Redis數十倍,但當數據更新時,此種模式會形成各個業務端和Redis數據不一致,一般會使用發佈訂閱機制來解決相似問題。

相關文章
相關標籤/搜索