什麼是大key
查詢bigkey
叢集模式檢視bigkey
redis-cli排查
cluster模式排查大key
因為clister叢集模式下查詢bigkey時,因為鍵會分散在不同的槽(slot)和不同的節點上,因此需要分別連到各個主節點進行檢查,或者在命令新增-c引數
首先需要檢視cluster各個節點,連線其中任意一個節點執行以下命令
CLISTER NODES
檢視叢集中每個節點的資訊,返回結果如下,是以空格分割的CSV格式
07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004,hostname4 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected
67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002@31002,hostname2 master - 0 1426238316232 2 connected 5461-10922
292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 127.0.0.1:30003@31003,hostname3 master - 0 1426238318243 3 connected 10923-16383
6ec23923021cf3ffec47632106199cb7f496ce01 127.0.0.1:30005@31005,hostname5 slave 67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 0 1426238316232 5 connected
824fe116063bc5fcf9f4ffd895bc17aee7731ac3 127.0.0.1:30006@31006,hostname6 slave 292f8b365bb7edb5e285caf0b7e6ddc7265d2f4f 0 1426238317741 6 connected
e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 127.0.0.1:30001@31001,hostname1 myself,master - 0 0 1 connected 0-5460
每行格式如下:
<id> <ip:port@cport[,hostname]> <flags> <master> <ping-sent> <pong-recv> <config-epoch> <link-state> <slot> <slot> ... <slot>
其中
master 主節點
slave 從節點
myself 當前節點
fail 當前節點處於失戀狀態
以三主三從為例可以看到當前叢集的節點資訊
透過以上獲取到各個節點資訊,可以檢視每個節點的bigkey情況
執行命令
redis-cli -h 節點ip -p 埠 --bigkeys
返回的並非就是bigkey, 而是每個型別的top1,同時給出每種資料型別的鍵值個數和平均大小, 大致衡量大小為:string型別大於10kb,hash,zset,list,set元素個數超過5000個,
以三主三從為例,如果卡槽為0-5460、5461-10922可能會出現以下提示
意味著訪問的key被移到另一卡槽了可以手動重定向到提示的MOVE節點進行嘗試
除了上述方法,也可以使用下面命令檢視某個key的大小
MEMORY USAGE key [SAMPLES count]
返回結果為所佔位元組大小,如果key不存在resps2返回nil,resp3返回null
解決方案
如何是key的名稱較大,可以使用md5進行摘要演算法生成固定的長度
如果是value較大,可以拆分為多個子key,比如一個大的list,拆分成多個小的list
再利用管道操作可以一定程度上保證事務
public void saveBigKey() {
ArrayList<Object> list = new ArrayList<>();
List<List<Object>> partList = Lists.partition(list, 100);
String masterKey = "master_key";
redisTemplate.executePipelined((RedisCallback<Object>) con -> {
con.del(rawKey(masterKey));
for (int i = 0; i < partList.size(); i++) {
String partKey = String.format("CHILD_KEY_%s", i);
con.set(rawKey(partKey), rawValue(JsonUtil.dumpObject(partList.get(i))));
con.expire(rawKey(partKey), TimeUnit.DAYS.toSeconds(2));
con.zAdd(rawKey(masterKey), i, rawValue(partKey));
}
con.expire(rawKey(masterKey), TimeUnit.DAYS.toSeconds(1));
return null;
});
}
RedisSerializer keySerializer() {
return redisTemplate.getKeySerializer();
}
RedisSerializer valueSerializer() {
return redisTemplate.getValueSerializer();
}
@SuppressWarnings("unchecked")
byte[] rawKey(Object key) {
Assert.notNull(key, "non null key required");
if (keySerializer() == null && key instanceof byte[]) {
return (byte[]) key;
}
return keySerializer().serialize(key);
}
@SuppressWarnings("unchecked")
byte[] rawValue(Object value) {
if (valueSerializer() == null && value instanceof byte[]) {
return (byte[]) value;
}
return valueSerializer().serialize(value);
}