返回归档
🥎Redis

Redis-大key问题

文章目录

image

什么是大key

没有特别明确的指标说明 xxx key是一个大key,但有一下指标可以作为参考。

从数据结构的角度出发

  • String类型:value值超过10KB
  • Hash类型:field数量超过500或value值过大
  • List类型:元素数量超过1000
  • Set类型:成员数量超过5000
  • ZSet类型:成员数量超过5000

从内存大小的角度出发

  • 一个key占用内存超过10MB

大key有什么影响

  1. 阻塞redis主线程,redis的读写性能下降,网络带宽占用高
  2. 主从数据复制延迟加大,数据出现倾斜

为什么会有大key产生

  1. 数据结构设计有问题,没有考虑数据的增长,过度使用某个key作为数据存储
  2. 没有及时清理历史数据

如何处理大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
}