
什么是大key
没有特别明确的指标说明 xxx key是一个大key,但有一下指标可以作为参考。
从数据结构的角度出发
- String类型:value值超过10KB
- Hash类型:field数量超过500或value值过大
- List类型:元素数量超过1000
- Set类型:成员数量超过5000
- ZSet类型:成员数量超过5000
从内存大小的角度出发
- 一个key占用内存超过10MB
大key有什么影响
- 阻塞redis主线程,redis的读写性能下降,网络带宽占用高
- 主从数据复制延迟加大,数据出现倾斜
为什么会有大key产生
- 数据结构设计有问题,没有考虑数据的增长,过度使用某个key作为数据存储
- 没有及时清理历史数据
如何处理大key
可以使用redis-cli --bigkeys 找到big key
拆分大key
// 原来的大key
HSET user:1 field1 value1 field2 value2 ...
// 拆分后
HSET user:1:basic field1 value1
HSET user:1:detail field2 value2
压缩数据
// 使用压缩算法
func setCompressedData(key string, value []byte) error {
compressed := compress(value) // 使用gzip等压缩算法
return redis.Set(key, compressed, 0)
}
分片存储
// hash取模分片
func shardedSet(key string, value interface{}) error {
shardNum := crc32.ChecksumIEEE([]byte(key)) % 100
shardKey := fmt.Sprintf("%s:%d", key, shardNum)
return redis.Set(shardKey, value, 0)
}
设置过期时间
// 设置合理的过期时间
func setWithExpiry(key string, value interface{}) error {
return redis.Set(key, value, 24*time.Hour)
}
渐进式清理
// 批量删除大key
func progressiveDelete(key string) error {
cursor := 0
for {
// SCAN 渐进式遍历
keys, newCursor := redis.Scan(cursor, key+"*", 100)
for _, k := range keys {
redis.Del(k)
}
if newCursor == 0 {
break
}
cursor = newCursor
}
return nil
}