Redis-键值设计
1.设置key的规范
- 遵循基本格式:【业务名称】:【数据名】:【id】 可读性强,在客户端的情况下使用:如果前缀相同会分目录层级
- 长度不超过44字节 string数据结构的三种类型,在44字节之内是embstring 内存占用小
- 不包含特殊字符
优点
- 可读性强
- 避免key冲突
- 方便估计案例
- 更节省内存
2.BigKey
什么是BigKey
在redis中存储的value空间大,导致效率变低。
BigKey的危害
-
网络阻塞
对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,网络速度变慢 -
数据倾斜
BigKey所在的Redis示例内存使用率远超其他示例 -
Redis阻塞
对元素较多的hash,list,zset,set做运算会耗时
-
CPU压力
对BigKey的数据序列化和反序列化会导致cpu的使用飙升
如何排查BigKey
-
redis-cli-bigkeys
-
scan扫描 Java代码示例
引入pom<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.8.0</version> <!-- 替换为最新的稳定版本 --> </dependency>
/** * 排查大key * */ @SpringBootTest public class RedisScanBigKey { /** * 字符串的大key */ final static int STR_MAX_LEN = 10 * 1024; /** * 集合的大key */ final static int HASH_MAX_LEN = 500; @Test public void scanBigKey() { Jedis jedis = new Jedis("localhost", 6379); int maxLen = 0; long len = 0; // 开始游标,首次调用传入0 String cursor = "0"; do { // 执行SCAN命令,传入游标、匹配模式(可选)和COUNT(可选) ScanResult<String> scanResult = jedis.scan(cursor); cursor = scanResult.getCursor(); // 处理本次扫描返回的元素 List<String> list = scanResult.getResult(); if (CollectionUtil.isEmpty(list)) { break; } // 遍历 for (String key : list) { String type = jedis.type(key); switch (type) { case "string": len = jedis.strlen(key); maxLen = STR_MAX_LEN; break; case "hash": len = jedis.hlen(key); maxLen = HASH_MAX_LEN; break; case "list": len = jedis.llen(key); maxLen = HASH_MAX_LEN; break; case "set": len = jedis.scard(key); maxLen = HASH_MAX_LEN; break; case "zset": len = jedis.zcard(key); maxLen = HASH_MAX_LEN; break; default: break; } if (len >= maxLen) { System.out.printf("大Key的值 : %s, type: %s, length or size: %d %n", key, type, len); } // 检查是否已遍历完所有元素 } } while (!"0".equals(cursor)); } }
-
第三方工具
-
网络监控
如何删除大BigKey
bigkey内存占用较多,即使删除也需要占用非常多的时间,导致redis主线程阻塞
- redis3.0以下版本:如果是集合类型则遍历bigkey的元素,逐个删除.建议使用scan扫描然后以此删除
- redis4.0以后:提供了异步删除命令:unlink
3.使用恰当的数据类型
/**
* 给大key瘦身 拆分成小hash
*/
@Test
void smallHash(){
Jedis jedis = new Jedis("localhost", 6379);
int hashSize = 100;
Map<String, String> map = new HashMap<>(hashSize);
for (int i = 0; i < 100000; i++) {
int k = (i -1) / hashSize;
int v = i % hashSize;
map.put("key_" + v , "value_" + v);
if (v == 0){
jedis.hmset("test:small:hash_" + k, map);
}
}
}