Redis的keys命令到底有多慢?

keys命令的用法:

keys pattern

查找符合正则匹配的key的列表。扫描对象是Redis服务中所有的key,想想都很慢对不对?

同时执行keys命令的同时,Redis进程将被阻塞,无法执行其他命令,假如超过了哨兵的down-after-milliseconds配置,还会进行主从切换,切换过程中,如果主节点恢复正常,还可能出现脑裂等一系列问题。

所以,生产环境中,建议直接禁用keys命令。

Keys命令的替代方案

  • scan扫描,避免阻塞

  • 将需要统计的数据放入一个set中 (但是这样可能出现Big Key问题,一般数据量大就不推荐)

Keys命令在Redis Cluster中是怎样执行的?

一般来说,keys命令对于集群节点来说,是不知道路由到哪个节点的,不像 get命令。在Java的Jedis客户端的JedisClusterKeyCommands类中,我们看到:

public Set<byte[]> keys(byte[] pattern) {
 // 在每个节点执行keys命令
 Collection<Set<byte[]>> keysPerNode = connection.getClusterCommandExecutor()
   .executeCommandOnAllNodes((JedisClusterCommandCallback<Set<byte[]>>) client -> client.keys(pattern))
   .resultsAsList();
 // 合并成一个整体后返回
 Set<byte[]> keys = new HashSet<>();
 for (Set<byte[]> keySet : keysPerNode) {
  keys.addAll(keySet);
 }
 return keys;
}

我们看到,Jedis是通过在每个节点上执行keys命令,并将结果合并返回的。

本文既然将keys命令的慢,那么他到底有多慢呢?

Keys命令到底有多慢?

这里主要是给大家一个基本的概念,并不是深入剖析。

图片

图片

这是腾讯云上Redis集群服务中,慢查询的日志。我们看到,Keys命令大概执行了250ms ~ 300ms。

图片

图片

根据节点信息,我们看到,每个节点存储了大约153w的key,占用内存300M+,平均每个键值对占用内存0.208KB,合213个字节。

根据我的理解,既然keys命令返回的是key值,而集群中其实有一个结构slots_to_keys 记录着所有key 的, 这只与key的数量有关,与Big key的关系不大。

按照这种猜想,假如此时Redis节点占用内存为3G,且Key数量成比例,那么Keys命令执行时间因为3s左右,这段时间Redis节点是阻塞的。

(感谢阅读,希望对你所有帮助)

<think>我们正在讨论如何优化Redis的MGET命令性能。MGET用于同时获取个键的值,但在某些情况下可能会变。以下是一些优化策略: 1. **管道化(Pipelining)**:虽然MGET本身是批量操作,但在高并发下,使用管道(pipeline)将命令组合发送可以减少网络往返次数。但注意,MGET本身已经是一次获取个键,所以如果每个请求都是获取个键,则MGET已经足够。然而,如果客户端需要执行个独立的MGET,那么可以将这些MGET命令通过管道一起发送,从而减少网络延迟[^1]。 2. **分片(Sharding)**:如果数据量很大,可以考虑将数据分布到Redis实例(分片)。这样,一个大的MGET操作可以拆分成个较小的MGET操作,并行发送到不同的分片,然后合并结果。这需要客户端实现分片逻辑,或者使用支持分片的Redis集群。在Redis集群中,MGET要求所有键必须在同一个槽(slot)中,否则会报错。因此,如果键分布在不同的槽,需要分别获取[^2]。 3. **减少单次请求的键数量**:一次MGET过的键可能会导致Redis阻塞,因为它是单线程处理命令。将大的MGET拆分成个较小的MGET,虽然可能会增加网络请求次数,但可以避免长时间阻塞,提高响应速度。需要根据实际情况平衡。 4. **使用异步操作**:在客户端使用异步I/O,避免同步等待MGET结果,从而提高应用程序的吞吐量。 5. **优化键的设计**:如果可能,考虑将个相关值存储在一个哈希(hash)中,这样只需要一个HGETALL就可以获取个字段,但这取决于数据访问模式。另外,确保键名尽量短以减少网络传输量。 6. **升级Redis版本**:新版本的Redis通常有性能改进,确保使用最新的稳定版。 7. **监控和诊断**:使用Redis查询日志(slowlog)来识别查询。通过设置`slowlog-log-slower-than`参数,记录执行时间超过指定微秒的命令。然后分析这些查询,看是否与MGET有关,以及键的数量是否过[^3]。 8. **使用Lua脚本**:对于复杂的操作,可以考虑使用Lua脚本在服务器端执行,减少网络开销。但注意,Lua脚本执行会阻塞其他命令,所以脚本必须高效。 9. **客户端缓存**:对于不经常变化的数据,可以在客户端缓存结果,减少对Redis的请求。 10. **网络优化**:确保Redis服务器和客户端之间的网络延迟尽可能低。例如,将客户端和Redis部署在同一机房或使用高速网络。 由于MGET的性能瓶颈通常与键的数量和网络延迟有关,因此优化重点在减少网络开销和避免大键集合。 §§相关问题§§ 1. Redis管道(pipelining)和MGET有什么区别? 2. 在Redis集群中如何使用MGET命令? 3. 如何监控Redis查询?</think>### Redis MGET 命令性能优化指南 Redis 的 `MGET` 命令用于批量获取个键的值,但在高并发或大数据量场景下可能出现性能问题。以下是系统化的优化策略: --- #### 1. **减少单次请求的键数量** - **问题**:单次 `MGET` 请求过键(如上千个)会导致 Redis 单线程阻塞,增加延迟。 - **优化**: - 将大请求拆分为个小批量请求(如每次 50-100 个键),通过并行化降低延迟: ```python # Python 示例:分批执行 MGET keys = ["key1", "key2", ..., "key1000"] batch_size = 50 results = {} for i in range(0, len(keys), batch_size): batch = keys[i:i+batch_size] values = redis.mget(batch) # 执行小批量 MGET results.update(zip(batch, values)) ``` - **效果**:避免单次请求阻塞 Redis,提升吞吐量[^1]。 --- #### 2. **使用管道(Pipeline)替代次请求** - **问题**:次独立 `GET` 请求(非 `MGET`)会因网络往返(RTT)降低性能。 - **优化**: - 用 `Pipeline` 将个 `GET` 合并为一次网络传输: ```python # Python 示例:使用 Pipeline pipe = redis.pipeline() for key in keys: pipe.get(key) # 批量放入命令队列 results = pipe.execute() # 一次性发送并获取结果 ``` - **对比**: - 原生 `MGET`:单次批量操作,但键过会阻塞。 - `Pipeline`:模拟批量操作,减少 RTT,适合键分散的场景[^2]。 --- #### 3. **优化键的分布与存储** - **数据分片(Sharding)**: 若数据跨Redis 实例(如集群模式),需确保 `MGET` 的键在同一分片。跨节点操作需次网络请求,效率低下。 - **方案**:通过哈希标签(Hash Tag)强制键分布到同一槽: ```bash # 使用 {tag} 保证 key1 和 key2 在同一分片 MSET {user:1000}:name "Alice" {user:1000}:email "[email protected]" MGET {user:1000}:name {user:1000}:email # 高效 ``` - **压缩值大小**:减少值体积(如使用 `zstd` 压缩),降低网络传输开销。 --- #### 4. **升级 Redis 版本与配置调优** - **升级 Redis 6.0+**: - 线程 I/O 特性(需启用 `io-threads-do-reads yes`)可加速网络请求处理[^3]。 - **调整 TCP 参数**: 优化内核参数,提升网络吞吐量: ```bash # 增加 TCP 缓冲区大小 sysctl -w net.core.rmem_max=16777216 sysctl -w net.core.wmem_max=16777216 ``` --- #### 5. **监控与诊断工具** - **查询日志**: 启用日志定位问题: ```bash CONFIG SET slowlog-log-slower-than 5000 # 记录 >5ms 的操作 SLOWLOG GET 50 # 查看最近50条查询 ``` - **内存分析**: 使用 `redis-cli --bigkeys` 分析大键,优化存储结构。 --- ### 性能对比场景 | 场景 | 延迟 (示例) | 适用情况 | |---------------------|-------------|----------------------------| | 原生 `MGET`(1000键) | 15ms | 键数量少(<100) | | 分批 `MGET`(50×20) | 8ms | 键数量且允许分批 | | `Pipeline`(1000键) | 5ms | 键分散在不同分片时替代 `MGET` | --- ### 相关问题 1. Redis 集群模式下如何保证 `MGET` 的键位于同一分片? 2. 如何通过 Redis 模块(如 RedisGears)进一步优化批量操作? 3. `MGET` 和 `Pipeline` 在事务支持上有何区别? [^1]: 分批次请求减少阻塞,提升吞吐量。 [^2]: Pipeline 通过合并网络请求降低延迟。 [^3]: Redis 6.0+ 线程 I/O 加速网络处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值