深度評測丨 GaussDB(for Redis) 大 Key 操作的影響

華為雲開發者社群發表於2022-01-27

本文分享自華為雲社群《墨天輪評測:GaussDB(for Redis)大Key操作的影響》,作者: 高斯 Redis 官方部落格。

在前一篇文章《墨天輪評測:GaussDB(for Redis)穩定性與擴容表現》 中,我們使用多執行緒壓測工具 memtier_benchmark 對華為 GaussDB(for Redis)和原生 Redis 進行了對比壓測,發現原生 Redis 容易出現 OOM 故障,且擴容操作會很慢,給運維帶來很大壓力。反觀華為 GaussDB(for Redis)不僅效能穩定,還具備在壓測過程中秒級擴容的能力,擴容操作對業務讀寫無影響。華為 GaussDB(for Redis)支援全量資料落盤,GaussDB 基礎元件服務提供底層資料三副本冗餘儲存,能夠保證資料零丟失。如果使用場景既要滿足 KV 查詢的高效能,又希望資料得到重視能夠不丟,則華為 GaussDB(for Redis)是合適的選型。

我們大多在實際生產環境中都遇到過 big key 對 Redis 效能的嚴重影響。接下來我們通過幾個簡單的實驗,測試下對於大 key 這一“效能殺手”,GaussDB(for Redis)的表現怎樣,和原生 Redis 相比在效能上有哪些改進?

1、訪問大 key

首先分別在 GaussDB(for Redis)和原生 redis 中建立一個大的 hash 型別的 key。

編輯一個簡單的 lua 指令碼,向一個 hash key 中插入一千萬條資料。

# vim redis-bigkey.lua
local result
for var=1,10000000,1 do
redis.call('hset',KEYS[1],var,var)
redis.call('lpush',KEYS[2],var)
redis.call('sadd',KEYS[3],var)
end
return result

向 GaussDB(for Redis)【192.168.0.226:8635】中插入大 key

# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635   --eval /root/redis-bigkey.lua 3 hset_test list_test set_test
(nil)

# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 hlen hset_test
(integer) 10000000
# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 scard sadd_test
(integer) 10000000
# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 llen lpush_test 
(integer) 10000000

向原生 Redis【192.168.0.135】中插入大 key

# redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379   --eval /root/redis-bigkey.lua 3 hset_test list_test set_test                
(nil)

# redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 hlen hset_test
(integer) 10000000
# redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 scard sadd_test
(integer) 10000000
# redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 llen lpush_test
(integer) 10000000

 

使用 memtier_benchmark 模擬業務壓力的同時 對大 key 進行訪問,觀察對業務 qps 的影響。

編輯一個簡單 shell 指令碼,對大 key 進行頻繁的訪問

#!/bin/bash
while true
do
redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 hget hset_test $RANDOM
redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 hget hset_test $RANDOM
redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 LREM lpush_test 0 $RANDOM
redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 LREM lpush_test 0 $RANDOM
redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 SISMEMBER sadd_test $RANDOM
redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 SISMEMBER sadd_test $RANDOM
done

使用 memtier_benchmark 進行壓測,讀寫混合場景。通過反饋的效能資料可以看到 GaussDB(for Redis) 和 Redis 每秒的運算元 ops/sec 分別為 14 萬和 13 萬,差別不大。

#GaussDB(for Redis)
memtier_benchmark -s 192.168.0.226 -a XXXXXXX -p 8635 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=1:1 --out-file=./result_gauss_setget.log

[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 3%,   4 secs] 12 threads:      582045 ops,  144053 (avg:  145472) ops/sec, 21.16MB/sec (avg: 21.51MB/sec),  1.33 (avg:  1.32) msec latency
#原生Redis
memtier_benchmark -s 192.168.0.135 -a XXXXXXX -p 6379 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=1:1 --out-file=./result_redis_setget.log

[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 7%,  11 secs] 12 threads:     1430798 ops,  132637 (avg:  130051) ops/sec, 70.51MB/sec (avg: 68.79MB/sec),  1.44 (avg:  1.47) msec latency

啟動 shell 指令碼後再次觀察,發現 GaussDB(for Redis) 的每秒運算元幾乎無變化,而原生 Redis 的每秒運算元波動巨大,甚至降低到了 3k 左右。說明大 key 操作對原生 Redis 效能有較大影響,對 GaussDB(for Redis) 的影響可控。

# bash hget_bigkey.sh

#GaussDB(for Redis)
# memtier_benchmark -s 192.168.0.226 -a XXXXXXX -p 8635 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=1:1 --out-file=./result_gauss_setget.log

[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 47%,  64 secs] 12 threads:     9099444 ops,  139186 (avg:  142163) ops/sec, 20.60MB/sec (avg: 20.96MB/sec),  1.38 (avg:  1.35) msec latency
#原生Redis
# memtier_benchmark -s 192.168.0.135 -a XXXXXXX -p 6379 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=1:1 --out-file=./result_redis_setget.log

[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 29%,  75 secs] 12 threads:     5607700 ops,    3329 (avg:   74759) ops/sec, 1.80MB/sec (avg: 40.08MB/sec), 52.35 (avg:  2.55) msec latencyy

2、刪除大 key

繼續使用 memtier_benchmark 對 GaussDB(for Redis) 和原生 Redis 進行測試,只讀場景。在 GaussDB(for Redis)中刪除大 key 很快就能完成,且對效能幾乎無影響。

#GaussDB(for Redis)
# memtier_benchmark -s 192.168.0.226 -a XXXXXXX -p 8635 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=0:1 --out-file=./result_gauss_setget.log
Writing results to ./result_gauss_setget.log...
[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 4%,   5 secs] 12 threads:      719216 ops,  151326 (avg:  143795) ops/sec, 22.16MB/sec (avg: 21.13MB/sec),  1.27 (avg:  1.33) msec latency
# time redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 del sadd_test   
(integer) 1

real    0m0.003s
user    0m0.001s
sys     0m0.002s
# memtier_benchmark -s 192.168.0.226 -a XXXXXXX -p 8635 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=0:1 --out-file=./result_gauss_setget.log
Writing results to ./result_gauss_setget.log...
[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 42%,  57 secs] 12 threads:     8031731 ops,  144874 (avg:  140890) ops/sec, 21.46MB/sec (avg: 20.77MB/sec),  1.32 (avg:  1.36) msec latency

反觀原生 Redis,刪除大 key 耗時 3 秒,且在刪除期間對效能影響較大。可以觀察到在刪除期間 ops/sec 變成 0,也就是說大 key 刪除期間操作是沒有辦法正常響應的。

#原生Redis
# memtier_benchmark -s 192.168.0.135 -a XXXXXXX -p 6379 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=0:1 --out-file=./result_redis_setget.log
Writing results to ./result_redis_setget.log...
[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 6%,   7 secs] 12 threads:     1107132 ops,  157621 (avg:  158125) ops/sec, 16.07MB/sec (avg: 16.13MB/sec),  1.22 (avg:  1.21) msec latency

# time redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 del sadd_test
(integer) 1

real    0m3.001s
user    0m0.000s
sys     0m0.003s

# memtier_benchmark -s 192.168.0.135 -a XXXXXXX -p 6379 -c 16 -t 12  -n 100000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000000 --key-minimum=1 --key-prefix= --ratio=0:1 --out-file=./result_redis_setget.log
Writing results to ./result_redis_setget.log...
[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 57%,  68 secs] 12 threads:     1015893 ops,       0 (avg:  126961) ops/sec, 0.00KB/sec (avg: 12.98MB/sec),  -nan (avg:  1.13) msec latencyy

手動刪除大 key 對效能的影響差別明顯,如果設定大 key 的過期時間交由 Redis 刪除過期資料 是否會有效能影響呢?下面簡單測試下

手動設定大 key 的過期時間,並啟動 memtier_benchmark 讀寫混合測試,檢視對效能的影響。通過測試發現大 key 的過期對於 GaussDB(for Redis)的效能幾乎沒有影響。

#GaussDB(for Redis)
[root@ecs-ef13-0001 ~]#  redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 EXPIRE sadd_test 8 && redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 EXPIRE sadd_test1 12 
(integer) 1
(integer) 1

[root@ecs-ef13-0001 ~]# memtier_benchmark -s 192.168.0.226 -a XXXXXXX -p 8635 -c 16 -t 12  -n 10000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000 --key-minimum=1 --key-prefix= --ratio=1:1 --out-file=./result_gauss_setget.log
Writing results to ./result_gauss_setget.log...
[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 100%,  17 secs]  0 threads:     1920000 ops,  106367 (avg:  109940) ops/sec, 105.02MB/sec (avg: 108.55MB/sec),  1.74 (avg:  1.74) msec latency

在對原生 Redis 測試時,我們發現大 key 過期操作幾乎阻塞了正常的讀寫,在 memtier_benchmark 測試時 ops/sec 指標為 0,只有當大 key 過期操作結束後才恢復正常。

 
#原生Redis

[root@ecs-ef13-0001 ~]# redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 EXPIRE sadd_test 8 && redis-cli  -h  192.168.0.135 -a XXXXXXX -p 6379 EXPIRE sadd_test1 12 
(integer) 1
(integer) 1

 

深度評測丨 GaussDB(for Redis) 大 Key 操作的影響

 

深度評測丨 GaussDB(for Redis) 大 Key 操作的影響

 

深度評測丨 GaussDB(for Redis) 大 Key 操作的影響

 

[root@ecs-ef13-0001 ~]# memtier_benchmark -s 192.168.0.135 -a XXXXXXX -p 6379 -c 16 -t 12  -n 10000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000 --key-minimum=1 --key-prefix= --ratio=1:1 --out-file=./result_redis_setget.log
Writing results to ./result_redis_setget.log...
[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 100%,  42 secs]  0 threads:     1920000 ops,  134502 (avg:   45551) ops/sec, 132.80MB/sec (avg: 44.98MB/sec),  1.43 (avg:  4.21) msec latency

 

開源 redis 的過期雖然也支援非同步,但需要使用者手動配置策略;刪除操作則需用 UNLINK 替換常規的 DEL,具體對效能的影響可能會有所降低,本次不做深入驗證。華為 GaussDB(for Redis)的刪除和過期對效能 0 影響。

3、大 key 對 GaussDB(for Redis)擴容操作的影響

在上一篇文章中我們測試了 GaussDB(for Redis)的線上擴容功能,經測試 GaussDB(for Redis)可以在不影響業務讀寫的前提下實現秒級的擴容。這次我們增加一些“難度”,看看存在大 key 的情況下 GaussDB(for Redis)擴容操作是否還能做到秒級和業務零感知。

預先在 GaussDB(for Redis)中插入大 key

[root@ecs-ef13-0001 ~]# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 scard sadd_test4
(integer) 10000000
[root@ecs-ef13-0001 ~]# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 scard sadd_test5
(integer) 10000000
[root@ecs-ef13-0001 ~]# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 scard sadd_test6
(integer) 10000000
[root@ecs-ef13-0001 ~]# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 scard sadd_test7
(integer) 10000000
[root@ecs-ef13-0001 ~]# redis-cli  -h  192.168.0.226 -a XXXXXXX -p 8635 scard sadd_test8
(integer) 10000000

使用 memtier_benchmark 模擬讀寫請求,同時在控制檯上進行擴容操作。同之前的測試效果一樣,GaussDB(for Redis)同樣實現了對業務零感知的秒級擴容

 
[root@ecs-ef13-0001 ~]# memtier_benchmark -s 192.168.0.226 -a XXXXXXX -p 8635 -c 16 -t 12  -n 50000 --random-data --randomize --distinct-client-seed -d 1000 --key-maximum=65000 --key-minimum=1 --key-prefix= --ratio=0:1 --out-file=./result_gauss_setget.log  
Writing results to ./result_gauss_setget.log...
[RUN #1] Preparing benchmark client...
[RUN #1] Launching threads now...
[RUN #1 9%,  20 secs] 12 threads:      902361 ops,   42634 (avg:   45112) ops/sec, 41.99MB/sec (avg: 44.44MB/sec),  4.53 (avg:  4.23) msec latencycy

 

深度評測丨 GaussDB(for Redis) 大 Key 操作的影響

 

深度評測丨 GaussDB(for Redis) 大 Key 操作的影響

4、總結

原生 Redis 對大 key 的訪問,刪除等操作會嚴重阻塞業務的正常訪問,這是由 Redis 自身單執行緒處理請求的架構決定的。使用原生 Redis 時需要嚴格限制大 key 的使用,一旦出現大 key 對系統的效能影響通常是“致命”的。

反觀 GaussDB(for Redis)由於採用多執行緒架構,對大 key 的訪問、刪除,以及存在大 key 情況下的擴容操作,對效能的影響都是可控的。1)大 key 訪問場景中,由於 GaussDB(for Redis)採用的多執行緒的架構,不易阻塞其他業務操作。2)大 key 刪除的場景中,由於 GaussDB(for Redis)實現的邏輯不同,刪除操作能夠快速完成,對業務無影響。3)擴容場景中,GaussDB(for Redis)不涉及 key 遷移,大 key 對擴容更是 0 影響。

綜上,雖然一般推薦業務設計避免大 key,但在一些需要操作少量大 key 的業務場景,華為雲 GaussDB(for Redis)表現更佳。

此外,從業務開發角度看,當多業務共用一個例項時,使用 GaussDB(for Redis)的話,即使其他業務引入大 key,自己的業務也不至於受太大影響。

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章