RedisTemplate.keys()在生产环境严禁使用,因其调用KEYS命令会阻塞Redis单线程、O(N)全量扫描键空间,易引发超时、卡顿及集群雪崩。

RedisTemplate.keys() 在生产环境基本不能用
直接说结论:keys() 会阻塞 Redis 单线程,数据量稍大(比如几万 key)就导致超时、服务卡顿甚至雪崩。Spring Boot 默认配置下,RedisTemplate.keys("user:*") 看似简单,实则是线上事故高发操作。
根本原因:Redis 的 KEYS 命令需遍历整个键空间,时间复杂度 O(N),且执行期间其他命令排队等待。官方文档明确标注“Avoid in production”。
- 本地开发或极小数据集(
- 集群模式下
keys()直接报错:Cross-slot keys或MOVED错误 - Redis 6+ 启用 ACL 后,普通用户默认无
keys权限,会抛NOAUTH或NOPERM
替代方案:用 scan() 非阻塞分批遍历
scan() 是唯一安全的模糊查询手段——它不锁库、支持游标分页、兼容集群,代价是结果可能有重复或漏判(但业务上通常可接受)。
Spring Data Redis 提供了 RedisTemplate.scan() 和 RedisOperations.scan() 两种调用方式,推荐用后者配合 ScanOptions:
ScanOptions options = ScanOptions.scanOptions()
.match("user:*")
.count(100)
.build();
Cursor<String> cursor = redisTemplate.scan(options);
while (cursor.hasNext()) {
String key = cursor.next();
// 处理单个 key,例如 get(key) 或 opsForValue().get(key)
}
cursor.close(); // 必须手动关闭,否则游标泄漏
-
count不是“返回条数”,而是每次扫描的内存量提示,实际返回数量可能更少 - 必须调用
cursor.close(),否则 Redis 会保留游标状态,内存持续增长 - 若需获取对应 value,不要在循环里反复调
redisTemplate.opsForValue().get(key),应批量用pipeline或mget
真正需要模糊查数据?说明设计可能有问题
用 key 模糊匹配,往往意味着把 Redis 当成数据库在用——这违背了 Redis 作为缓存/高速索引的核心定位。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
常见错误场景和修正方向:
- 想查“所有上海用户的最新订单” → 改用 Redis Hash 存用户维度数据:
HSET user:123 city "shanghai" order_id "ord_abc",再用HSCAN查 Hash 内部字段 - 想按时间范围查日志 → 改用 Sorted Set,把时间戳当 score:
ZADD logs:202405 {timestamp} "{json}",用ZRANGEBYSCORE查 - 需要全文检索 → RedisSearch 模块(需额外部署),或直接走 MySQL + Elasticsearch
如果坚持用 key 匹配,请确保 key 命名有层次:比如 user:shanghai:123 而非 user_123_shanghai,这样能用 scan 配合前缀缩小范围。
scan() 的坑:游标失效、结果不一致、性能毛刺
scan 不是银弹,几个关键细节不注意就会出问题:
- 游标超时:Redis 默认游标有效期 0(永不过期),但某些云 Redis(如阿里云 Tair)会强制回收空闲游标,需捕获
InvalidCursorException - 结果不幂等:scan 过程中如有 key 新增/删除,可能重复返回或遗漏——业务逻辑得容忍这种“最终一致性”
- 网络开销大:每次
cursor.next()都是一次 round-trip,高频小 count(如count=10)反而比count=500更慢 - 没有排序:scan 返回顺序完全随机,如需按时间排序,得在应用层二次处理
最隐蔽的坑是:Spring 的 RedisTemplate.scan() 默认不校验连接状态,若 Redis 重启或主从切换,游标可能指向已销毁的上下文,表现就是 cursor.hasNext() 一直返回 true 却拿不到数据——务必加超时和重试逻辑。

















