Redis 大 key、热 key 判别和解决方案

Redis 是我们常见的缓存解决方案,但是使用不当的 Redis 同样会造成系统瓶颈。

慢日志分析

要启用慢日志分析,首先先要对慢查询记录进行设置:

# 命令执行耗时超过 5 毫秒,记录慢日志 
CONFIG SET slowlog-log-slower-than 5000 
# 只保留最近 500 条慢日志
CONFIG SET slowlog-max-len 500

设置成功后通过下面的命令就能查到慢日志:

127.0.0.1:6379> SLOWLOG get 5
1) 1) (integer) 32693 # 慢日志ID
   2) (integer) 1593763337 # 执行时间戳
   3) (integer) 5299 # 执行耗时(微秒)
   4) 1) "LRANGE" # 具体执行的命令和参数
      2) "user_list:2000"
      3) "0"
      4) "-1"
2) 1) (integer) 32692
   2) (integer) 1593763337
   3) (integer) 5044
   4) 1) "GET"
      2) "user_info:1000"

主要可能的原因无非也就是:

  1. 数据太大,导致网络 IO 耗时增加
  2. 命令复杂,导致 CPU 耗时增加

而要避免这种慢查询发生,就需要我们尽可能的避免复杂的查询和大 key 的产生。

而今天我们要说的重点就是关于 Redis 中的大 key 要怎么解决。

大 key

大 key 意味着 value 特别大(而不是 key 特别大),大 key 会导致的问题显而易见:

  1. 网络 IO:大 key 的读写都会导致网络 IO 的阻塞,形成上面所说的慢查询
  2. 内存倾斜:在 Redis cluster 中大 key 会存在某个节点,此时该节点会比其他节点消耗更多的内存和网络资源,形成卡点
  3. 阻塞查询:大 key 的读写和删除操作都在主线程中进行,会阻塞其他命令的执行,导致 redis 性能下降
  4. 影响持久化:持久化需要将数据写入磁盘,大 key 意味着单条日志写入量也会变大,持久化过程就会更耗时,甚至会频繁触发 AOF 重写。

因此如果没有特殊情况,我们要尽量避免大 key。

大 key 检测

要发现大 key 也很简单,可以直接通过下面的命令发现大 key:

> redis-cli --bigkeys

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

100.00% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Keys sampled: 18

-------- summary -------

Total key length in bytes is 155 (avg len 8.61)

Biggest string found "rand_16_0" has 4 bytes
Biggest   zset found "job:delayed" has 4 members

0 lists with 0 items (00.00% of keys, avg size 0.00)
0 hashs with 0 fields (00.00% of keys, avg size 0.00)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
17 strings with 67 bytes (94.44% of keys, avg size 3.94)
0 sets with 0 members (00.00% of keys, avg size 0.00)
1 zsets with 4 members (05.56% of keys, avg size 4.00)

当然,这种方法只能显示最大的那个 key,他不一定实际就是大 key,可能本身占用的内存并不多。

此外,如果在主节点执行,同样会阻塞运行,所以建议在从节点执行。

另外,也可以通过 SCAN来扫描 Redis,得到每个 key 的内存占用情况,从而感知到大 key 的存在:

redis-cli --scan | while read key; do 
  echo "$(redis-cli memory usage $key) $key"; 
done | sort -nr | head

使用 SCAN 命令不会阻塞 Redis,比较安全。

但是缺点是效率较低,对大量 key 的 Redis 数据库扫描时间较长。

也可以使用一些第三方软件来完成这一动作,比如 Redis RDB Tools

删除大 key

实际上不仅读写会造成瓶颈,前面我们说过删除大 key 也会有性能问题。因为删除时不仅仅是删除一个操作,还会涉及到资源的回收和再分配,而 DEL是在主线程中执行的,Redis 中的执行命令是单线程的,这意味着直接影响了整个 Redis 的吞吐。

因此我们不能直接 DEL 大 key,更好的实践是使用 UNLINK代替 DEL,这样会分配另一个线程去回收,而不会阻塞主线程。

另一方面,如果 Redis 开启了 Lasy Free:lazyfree-lazy-user-del = yes,此时就不用 UNLINKDEL 也会由其他线程执行了,从而不阻塞主线程。

避免大 key

站在业务的角度,有时候可能不可避免的会产生一些大 key,但原则上我们依旧要尽可能的避免这种情况的出现。

拿我们上一篇缓存文章中微博的例子为例,如果一个微博大 V 有非常多的粉丝(假设有一千万),这些数据量即使我们只存储 user_id,也会有不小的数据。此时我们就可以将大 V 的粉丝拆分成多个子列表来进行查询,一个子列表放 5000 个粉丝 id,避免单 key 过大。

热 key

在上一篇文章中,我们也说过热 key 的问题,热 key 对 Redis 主要造成的影响是:

  1. 接口超时严重,逐步发生雪崩
  2. 网卡被打爆,大量请求失败
  3. 连接数被热 key 占据影响别的请求

说白了核心关键点就是网络实在不够用了。

而对于热 key 来说,除了加机器,还可以配合「发现-解决」的套路来减少热 key 带来的影响。

发现热 key

  1. 业务场景预估热 key:对于一些场景,我们可能能预估出爆点,比如某本季新番预订爆款,那八成就会变成热 key。但是不太能应对微博热搜这种突发场景。
  2. 客户端收集:在 Redis 调用的 SDK 中加入对命令的收集,来分析哪些 key 的访问最多,从而感知热 key。虽然方便,但是需要改造和引入 SDK,与业务耦合。
  3. 代理层收集:本质上和客户端没啥区别,只是从 SDK 变成了网络代理。从架构的角度上虽然与业务解耦了,多加了一层也容易造成单点风险。
  4. Redis 命令监控:

    1. monitor:用于实时打印出 Redis 服务器接收到的命令。但是在高并发的情况下容易拖累 redis 的性能,因此不太常用。
    2. hotkeys:redis-cli --hotkeys -i 0.1来获取,但是相当于全 key 扫描,key 较多时成本会比较高,而且全量扫描的耗时导致它的实时性较差。

解决热 key

对于热 key 来说,其实也没什么特别好的解决方案,主要也就是两个套路:

  1. 读写分离的情况下扩容
  2. 使用多级缓存

在发现热 key 的前提下构造多级缓存是一个比较正常的解决方案,这样有效降低了 Redis 的访问量,多级缓存的问题和解决方案在上一篇文章中也有提到。

总结

大 key 和 热 key 不仅仅是一个技术问题,同时也要站在业务的角度来选择一个合适的解决方案。

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: redis

已有 2 条评论

  1. 对于寻求优化客户服务流程并保证高效及时的客户沟通的企业来说,WhatsApp 앨썴 是一个无价的工具。
    更多信息
    WhatsApp 数据
    https://wsdatab.com

  2. 这可以通过隐藏主聊天面板中的聊天而不将其删除来减少混乱。定期备份您的 WhatsApp 对话和联系人列表。确保在更换设备时您的数据得到安全保存。

添加新评论