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"
主要可能的原因无非也就是:
- 数据太大,导致网络 IO 耗时增加
- 命令复杂,导致 CPU 耗时增加
而要避免这种慢查询发生,就需要我们尽可能的避免复杂的查询和大 key 的产生。
而今天我们要说的重点就是关于 Redis 中的大 key 要怎么解决。
大 key
大 key 意味着 value 特别大(而不是 key 特别大),大 key 会导致的问题显而易见:
- 网络 IO:大 key 的读写都会导致网络 IO 的阻塞,形成上面所说的慢查询
- 内存倾斜:在 Redis cluster 中大 key 会存在某个节点,此时该节点会比其他节点消耗更多的内存和网络资源,形成卡点
- 阻塞查询:大 key 的读写和删除操作都在主线程中进行,会阻塞其他命令的执行,导致 redis 性能下降
- 影响持久化:持久化需要将数据写入磁盘,大 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
,此时就不用 UNLINK
,DEL
也会由其他线程执行了,从而不阻塞主线程。
避免大 key
站在业务的角度,有时候可能不可避免的会产生一些大 key,但原则上我们依旧要尽可能的避免这种情况的出现。
拿我们上一篇缓存文章中微博的例子为例,如果一个微博大 V 有非常多的粉丝(假设有一千万),这些数据量即使我们只存储 user_id,也会有不小的数据。此时我们就可以将大 V 的粉丝拆分成多个子列表来进行查询,一个子列表放 5000 个粉丝 id,避免单 key 过大。
热 key
在上一篇文章中,我们也说过热 key 的问题,热 key 对 Redis 主要造成的影响是:
- 接口超时严重,逐步发生雪崩
- 网卡被打爆,大量请求失败
- 连接数被热 key 占据影响别的请求
说白了核心关键点就是网络实在不够用了。
而对于热 key 来说,除了加机器,还可以配合「发现-解决」的套路来减少热 key 带来的影响。
发现热 key
- 业务场景预估热 key:对于一些场景,我们可能能预估出爆点,比如某本季新番预订爆款,那八成就会变成热 key。但是不太能应对微博热搜这种突发场景。
- 客户端收集:在 Redis 调用的 SDK 中加入对命令的收集,来分析哪些 key 的访问最多,从而感知热 key。虽然方便,但是需要改造和引入 SDK,与业务耦合。
- 代理层收集:本质上和客户端没啥区别,只是从 SDK 变成了网络代理。从架构的角度上虽然与业务解耦了,多加了一层也容易造成单点风险。
Redis 命令监控:
- monitor:用于实时打印出 Redis 服务器接收到的命令。但是在高并发的情况下容易拖累 redis 的性能,因此不太常用。
- hotkeys:
redis-cli --hotkeys -i 0.1
来获取,但是相当于全 key 扫描,key 较多时成本会比较高,而且全量扫描的耗时导致它的实时性较差。
解决热 key
对于热 key 来说,其实也没什么特别好的解决方案,主要也就是两个套路:
- 读写分离的情况下扩容
- 使用多级缓存
在发现热 key 的前提下构造多级缓存是一个比较正常的解决方案,这样有效降低了 Redis 的访问量,多级缓存的问题和解决方案在上一篇文章中也有提到。
总结
大 key 和 热 key 不仅仅是一个技术问题,同时也要站在业务的角度来选择一个合适的解决方案。
植入部分
如果您觉得文章不错,可以通过赞助支持我。
如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。
对于寻求优化客户服务流程并保证高效及时的客户沟通的企业来说,WhatsApp 앨썴 是一个无价的工具。
更多信息
WhatsApp 数据
https://wsdatab.com
这可以通过隐藏主聊天面板中的聊天而不将其删除来减少混乱。定期备份您的 WhatsApp 对话和联系人列表。确保在更换设备时您的数据得到安全保存。