资讯动态

Redis 缓存清理实战:从底层原理到安全操作指南

发布时间:2026/10/13 12:31:55 来源:尧图企业网站定制
你有没有遇到过这种情况Redis 用着用着内存快满了业务也开始卡了打开客户端一看一堆乱七八糟的 key不知道该删哪些或者干脆执行了FLUSHALL结果把不该清的数据全部干掉线上直接告警。我自己在维护 Redis 的这几年里清理缓存这个看似简单的操作其实踩过不少坑。很多人以为清缓存就是删 key但实际涉及内存淘汰策略、过期键处理、数据类型差异、序列化方式甚至分布式锁的安全性。这篇内容我会结合自己真实操作过的场景把 redis 清理缓存这件事从原理到实战彻底讲透包括命令怎么选、批量怎么删、哪些缓存不能乱清、用什么工具更高效以及我在生产上遇到的问题和排查思路。不管你是刚接触 Redis 的运维还是写业务代码的开发者看完应该都能少走弯路。1. 清理 Redis 缓存前必须搞懂的底层逻辑先说个扎心的现实如果你不知道缓存是怎么存进去的那你大概率也不知道怎么安全地把它清掉。Redis 的缓存清理本质上就是操作内存中的 key-value 数据但 Redis 里 key 的组织方式、过期机制、内存淘汰策略都直接影响你应该用哪种清理方式。1.1 缓存到底存在哪从 key 的空间结构说起Redis 的每个数据库实例里数据都是存在一个全局字典里的你可以把它想象成一个大号的 HashMapkey 是对应的键value 是具体的值。Redis 默认有 16 个逻辑数据库从 0 到 15我们平时用的默认库是 0。清理缓存前你得先确认自己连的是哪个库很多人用客户端工具登录后默认连的是 db0但业务可能把缓存写在了 db1、db2 里面结果你在 db0 里执行FLUSHALL看着好像清干净了实际上业务缓存纹丝不动。另外key 名是有层级结构的我强烈建议团队从一开始就按业务模块来命名比如user:info:1001、order:detail:20240101。这样你做清理的时候只需要按前缀匹配就能精准定位而不是靠肉眼在一堆无规则的 key 里面翻找。还有一点很多人忽略Redis 里存储的 key 数量很大时KEYS命令会直接阻塞主线程。比如线上有上千万个 key你执行KEYS user:*Redis 会疯狂扫描整个字典持续几秒钟甚至更久期间所有读写请求全部卡住。所以清理缓存不是简单敲个命令你得搞清楚数据规模、阻塞风险、还有业务对缓存的依赖程度。1.2 为什么不能一把梭过期键、内存淘汰与惰性删除很多新手清理缓存的思路是“删掉所有 key”但 Redis 的过期键并不是一到时间就立刻消失的。它有两种删除机制惰性删除和周期删除。惰性删除是指当某个 key 被访问时如果发现已经过期顺手删掉周期删除是后台每 100ms 抽查一部分过期 key 来清理。这意味着一个已经过期的 key只要没人访问它它可能还会继续占着内存一段时间。这有什么影响呢就是你清理缓存的时候用DBSIZE看到的内存占用和 key 数量可能不准确。你以为清了实际旧数据还赖在内存里。这时候应该主动用清理命令来处理。内存淘汰策略也要搞明白。Redis 默认是noeviction也就是内存满了不淘汰任何数据直接报错。但如果配置成allkeys-lru或volatile-lruRedis 会按照近期的访问情况自动淘汰 key。这里有个很容易踩的坑如果你用的是allkeys-lru那么当你执行FLUSHALL清掉所有 key 后内存并不会马上完全释放到操作系统因为 Redis 的内存分配器比如 jemalloc有内存碎片你看到 RSS 可能还是很高。这不是没清干净而是内存碎片问题需要重启实例或者用MEMORY PURGE手动整理注意这只对部分分配器有效。从工程角度看清理缓存应该是一种有计划的治理行为而不是等到内存爆了才去救火。你需要知道自己每个业务的 key 大概多大、命中率多少、允许丢失的容忍度是多少。比如用户会话缓存丢了顶多让人重新登录但库存缓存如果被一把梭清掉瞬间把数据库打挂那就是事故了。所以清理之前先评估业务影响这是底线。2. 最常用的清理命令实战与踩坑Redis 提供了多个删除命令每个命令都有它的脾气。我这里直接按照生产环境的使用频率来拆解并附上我实际用过的场景。2.1 DEL、UNLINK、FLUSHDB、FLUSHALL 怎么选先看一张对比表看完你就知道为什么不能无脑用 DEL 了。命令作用阻塞风险推荐场景DEL key1 key2同步删除 key如果 value 很大比如大 List 或大 Hash删除时释放内存可能导致阻塞小 keykey 数量少UNLINK key异步删除先取消链接后台回收内存不阻塞大 key批量删除FLUSHDB清空当前库所有 key全量删除可能阻塞测试环境、可接受全清的业务FLUSHALL清空所有库所有 key同上极度危险生产禁用我见过有同事在线上执行DEL删一个包含几百万元素的 Set结果 Redis 直接卡了差不多 3 秒监控曲线瞬间掉到底。原因就是DEL是同步操作它需要遍历所有元素并释放内存而大对象释放内存是耗时操作。后来我们用UNLINK替代了DEL因为UNLINK先把 key 从全局字典中摘掉然后后台线程慢慢回收内存主线程不会被卡住。那FLUSHDB和FLUSHALL呢我个人的原则是测试环境随便用生产环境只有一种情况能用这个 Redis 实例是专门给某个非核心功能做缓存用的而且业务上允许短暂全量丢失比如某些展示用的配置项丢了可以从数据库重新加载。但凡涉及订单、支付、库存绝对禁止在生产上执行FLUSHALL哪怕是从库也要谨慎因为从库同步数据也会受影响。2.2 按前缀批量清理的正确姿势生产环境下我们经常需要按业务前缀清理缓存比如清理所有promotion:*开头的营销活动缓存。这里有两种主流方案。第一种是先用SCAN找出匹配的 key再用UNLINK删除。SCAN命令是游标式遍历每次返回一部分结果不会像KEYS那样阻塞但仍然会占用一点 CPU。我写过一个常用的 Lua 脚本循环调用SCAN并删除匹配的 key但 Lua 脚本在 Redis Cluster 模式下对 key 有严格的限制脚本里必须传递同一个 key否则会报错。所以更简单的办法是在客户端层面用 Python 或 Shell 脚本循环执行SCAN和UNLINK批量处理。第二种是直接维护一张“可清理的 key 前缀清单”在写缓存的时候就把 key 按前缀隔离清理的时候直接点名。比如定义一个batch_delete接口传入前缀列表服务端用SCAN配合匹配模式执行删除。我还用过redis-cli --scan --pattern user:logout:* | xargs redis-cli UNLINK这种命令行组合但要注意一次 xargs 传入的 key 数量不要太多否则命令行过长。另外在 Redis 6.0 以上还有CLIENT PAUSE命令可以和清理配合使用但它不适合在线业务只适合维护窗口。我的经验是批量清理最好放在流量低峰期比如凌晨两点并且分批次执行每次清理一部分观察一下 Redis CPU 和延迟没有异常再继续下一批。2.3 定时清理与过期时间设置的工程实践清理缓存不应该全靠手动更多时候应该靠合理的过期时间来自动完成。我在不少团队里看到业务方设置过期时间时特别喜欢用 24 小时大家觉得这样可以减轻数据库压力。但问题是如果大量 key 都在同一时间点过期就会产生“缓存雪崩”效应同一瞬间大量请求全部穿透到数据库数据库压力瞬间飙升。我的做法是让过期时间带上随机偏差。比如基础过期时间是 86400 秒那我在写缓存时会额外加一个随机数范围在 0 到 3600 秒之间。这样分散了过期时间降低了雪崩概率。对于一些重要数据用两级的缓存策略比如本地缓存配合 Redis 缓存Redis 过期之后还有本地缓存兜底不至于立刻击穿。如果确实需要在某个时间点清理一批 key比如零点活动结束后清理活动缓存可以用EXPIREAT结合任务调度器比如 Linux 的 crontab 或者云上的定时任务来设置统一过期时间点。但要注意EXPIREAT设置的是时间戳如果业务服务器时钟不同步可能导致过期时间不准。我一般建议所有服务器统一启用 NTP 时间同步这点在缓存清理场景里也算是冷门但实用的细节。3. 不同类型缓存的清理方案Redis 不只是简单的 String还有 Hash、List、Set、ZSet每种数据类型的清理细节不一样。另外我们经常用 Redis 实现分布式锁锁缓存清理更是一个安全红线稍不注意就会出现并发事故。3.1 字符串、Hash、Set、ZSet、List 的清理差异先说最常用的 String 类型直接DEL或者UNLINK就行没有额外逻辑。但 Hash 类型要注意如果你只想清掉某个字段比如HDEL user:1001 name那是局部删除。但如果你要删掉整个 HashDEL和UNLINK都行。Hash 还有一个特殊场景如果 Hash 里的 field 非常多比如有几十万个字段删除整个 key 时DEL也会阻塞同样建议用UNLINK。Set 和 ZSet 的内部编码会根据元素数量变化。当元素较少时Redis 使用压缩列表删除很快但元素多了会转为哈希表和跳表删除大 Key 的耗时就会上涨。我遇到过一个线上问题有个 Set 存了上百万个用户 ID因为某个运营脚本 bug 一直往里面加数据导致内存爆炸。当时我先用了SREM尝试移除部分元素发现太慢了最后直接UNLINK整个 key然后重建数据。List 类型稍微特殊一点。如果 List 很长几百万个元素DEL也会阻塞。但 List 通常用于消息队列或最新动态列表删除整个 key 前要确认下游消费者是否已经消费完否则会丢数据。另外还可以用LTRIM来裁剪列表保留指定的范围这在清理历史数据时非常有用。比如LTRIM mylist 0 99表示只保留前 100 个元素后面的全部删除。这种方式不会阻塞按需裁掉旧数据很丝滑。3.2 分布式锁缓存清理的特殊性Redis 实现分布式锁最常用的是SET key value NX PX 30000这本质上也是一个缓存 key。清理分布式锁的坑点在于你很可能分不清这个 key 是不是锁以及持有锁的任务是不是已经结束了。如果你傻乎乎地用FLUSHALL把所有 key 都清掉等于把锁全释放了多个业务就会同时进入临界区造成数据错乱。所以清理分布式锁的安全姿势是只删除自己持有的锁并且要校验 value。我每次释放锁都会用 Lua 脚本先判断 key 的 value 是否等于当前线程的唯一标识相等才删除这个操作是原子性的。如果你用 Redis 的哨兵或 Cluster 架构还要考虑主从切换导致的锁失效问题不过这属于另一个话题。我想提醒的是清理 Redis 缓存前一定要留意 key 名字里有没有lock、mutex这类关键字这些是红线绝不能批量清。另外如果你用 Redisson 这类客户端实现的锁它的 key 可能带一些内部结构比如 Redisson 的锁底层是用 Hash 类型存的里面包含线程信息和重入次数。这种锁如果被误删重入机制就会失效其他线程也无法正常获取到锁。我在排查一个死锁问题时发现就是清理脚本把锁的 hash key 删掉了导致锁的 watchdog 续期逻辑找不到 key然后其他线程也获取不到锁因为 Redisson 的看门狗看到 key 不存在会自动认为锁被释放但实际上业务持有锁的逻辑已经乱了。这个坑让我意识到任何清理工具都需要配置“白名单”和“黑名单”锁 key 必须进入黑名单绝对不参与批量清理。3.3 序列化方式对清理的影响很多人没意识到不同的序列化方式会让 Redis key 在客户端工具中显示成完全不一样的内容。比如你用 JDK 原生序列化key 前面会带一串类似\xAC\xED\x00\x05t\x00...的二进制前缀用字符串匹配时根本匹配不到如果用 Hessian、JSON、Protobufkey 的形态又各不相同。这意味着你准备按前缀批量清理的时候必须知道业务写入时用的序列化器。我见过一个 case一个 Java 项目用StringRedisTemplate存 valuekey 是普通的cache:user:1001另一个项目用RedisTemplate的默认 JdkSerializationRedisSerializerkey 也经过了序列化结果通过客户端看到的就是一堆乱码二进制。清理的时候只匹配到了前者后者一直没清掉内存不断上涨。所以清理前建议先在客户端工具里选中一个 key右键查看它的原始 Redis key 和 type确认 key 的真实形态。如果你看到的是\xAC\xED开头那说明它是 Java 序列化后的产物你需要用SCAN配合十六进制模式或者直接在代码里删除。另外客户端工具比如 Redis Desktop Manager也支持显示二进制 key但你需要临时修改 key 编码或者用命令行调试。最稳妥的方案是在业务代码里预留一个清理接口不直接在运营工具里乱删。4. 可视化工具与脚本化清理实战命令行再强大很多人平时还是喜欢用可视化客户端。这里我把我常用的工具和一些脚本方案整理一下。4.1 Redis Desktop Manager / Another Redis Desktop Manager 使用要点如果你下载过 Redis Desktop Manager会发现新版已经改名为 Another Redis Desktop Manager简称 ARDM它是跨平台的支持 Windows、Mac、Linux。用它清理缓存时我建议先连接 Redis 实例然后在 key 树里按前缀输入过滤条件比如输入order:*它就会列出所有匹配的 key。这时候不要全选直接按 Delete 键因为 ARDM 的批量删除本质上是逐个发送 DEL 命令如果匹配到的 key 数量很大速度会比较慢而且可能造成阻塞。你可以勾选多个 key 后选择“删除”但此时工具不一定自动帮你改成 UNLINK所以如果有大 key建议还是去命令行操作。另外ARDM 还有一个很好的功能是查看内存分析和 key 的信息比如可以看到每个 key 的大小、过期时间、编码格式。我经常用它来定位哪些 key 占用内存最高然后有针对性的清理。这个功能在旧版 RDM 里叫“Memory Usage”通过MEMORY USAGE key实现注意这个命令本身也会消耗一定性能。如果你用的是 Mac我推荐直接用 brew 安装 Redis CLI然后在终端里操作因为 Mac 下图形客户端有时候会卡特别是 key 数量到百万级别时树形列表加载就很慢。而用 Redis CLI 加管道批量清理效率高得多。Windows 用户可以去 Redis 官网下载 Windows 版或者使用 WSL注意 Windows 版本并非官方原生支持功能上有一些兼容差异我在实际使用中发现SCAN的游标类型在 Windows 版上表现一致但部分命令会有细微差别。4.2 Linux/Windows 下清理缓存的脚本我在 Linux 环境最常用的清理脚本是一个 bash 函数大致逻辑就是循环 SCAN然后用 UNLINK 删除。为了避免一次删太多导致主从同步延迟我会控制每次删除的 key 数量比如每批删除 1000 个 key然后 sleep 0.1 秒。具体写法很简单核心代码如下#!/bin/bash # 按前缀清理 Redis 缓存避免阻塞 REDIS_CLIredis-cli -h 127.0.0.1 -p 6379 -a 你的密码 PATTERN$1 if [ -z $PATTERN ]; then echo Usage: $0 user:* exit 1 fi cursor0 while true do result$($REDIS_CLI -c --scan --pattern $PATTERN --count 1000) if [ -z $result ]; then break fi echo $result | xargs -n 100 $REDIS_CLI UNLINK cursor$(echo $result | tail -1) # 这个写法不严谨仅示意 sleep 0.1 done这里要注意redis-cli --scan命令本身就支持游标但它内部已经帮你循环了所以如果你的 Redis 没有密码并且不需要处理集群可以直接用redis-cli --scan --pattern user:* | xargs -n 100 redis-cli UNLINK这样的一行命令。但 xargs 这种方式有个小坑如果 key 里有空格会出问题所以最好在代码中使用真正的 SCAN 命令。如果是 Windows我一般写一个批处理脚本用 Python 来做删除逻辑因为 bat 里处理编码和特殊字符太痛了。比如在电脑上装个 Python然后写一个简单的爬虫式脚本用 redis-py 连接 Redis循环 scan_iter 然后 unlink。这不麻烦而且能复用很多通用的清理逻辑。很多人问“清理电脑缓存的 bat”和这个没关系那是操作系统层面的临时文件和 Redis 不是一个东西别混了。4.3 结合监控的缓存治理方案清理缓存不是一锤子买卖而是需要结合监控的持续治理。比如用 Prometheus 采集 Redis 的used_memory、keyspace_hits、keyspace_misses、evicted_keys等指标。当命中率突然下降或者内存飙升时你能第一时间发现异常然后通过监控定位大 key。我之前负责的一个系统上线了一个新功能结果它把一个订单对象序列化后整个塞到 Redis 里而且 key 没过期时间短短两天就把 Redis 内存吃光了。当时监控面板上的效率曲线直接爆表后来通过 ARDM 的内存分析定位到了几个特别大的 key然后用脚本把对应的 key 删掉同时让业务方增加了过期时间。这就是治理的价值。我还建议每个 Redis 实例都设置一个慢日志阈值比如slowlog-log-slower-than 1000010ms。如果清理缓存导致命令变慢慢日志里会留下证据方便复盘。再配合 Keyspace 通知notify-keyspace-events你还可以监听过期事件做后置清理逻辑比如当某个缓存的 key 过期通知发生时异步重算缓存。5. 常见问题排查与避坑清单这里我把清理缓存时最容易遇到的问题整理成一张速查表这是我在生产环境里真实遇到过并逐一解决过的。问题现象可能原因解决方案清理后内存没降下来内存碎片或大量过期内key未回收用MEMORY PURGE或重启实例主动UNLINK大keyKEYS命令卡死 Redis生产环境错误使用KEYS全表扫描改用SCAN或客户端工具清理后业务报错误删了锁或正在使用的缓存引入黑名单/白名单机制禁止清理锁 key 和热点 key批量删除时延迟突刺使用DEL删除大 key改为UNLINK并分批操作删除 key 时提示READONLY连接到了从库从库不可写连接主库执行删除或使用只读从库的REPLICAOF解除客户端工具里看到乱码 key序列化方式不同用代码删除或配置客户端显示模式清理后缓存雪崩大量 key 同时过期设置随机过期时间错开过期点FLUSHALL误执行操作失误在生产环境禁用该命令通过 ACL 限制权限使用 pipeline 批量删除时命令丢失管道连接异常增加确认机制逐批检查返回结果5.1 如何安全配置权限避免手滑清空上面表格里提到 ACLRedis 6.0 之后可以针对用户配置命令白名单。比如创建一个只能执行部分命令的用户禁止执行FLUSHALL、FLUSHDB、KEYS、CONFIG等危险命令。这样即使有人拿到连接凭据也无法一把梭清空。我在团队里专门建了一个cache-cleaner用户只给它分配SCAN、UNLINK、DEL、EXPIRE、TTL等必要的命令权限这样清理工作既高效又不越界。5.2 从清理缓存升级到缓存治理最后想说一下清理缓存这件事最怕临时抱佛脚。如果你经常需要清理 Redis说明缓存设计本身可能有问题。比如没设过期时间、key 不合理堆积、大 key 过多。我现在更愿意做的是在应用层面建立统一的缓存工具类所有缓存的读写都通过这个类里面强制设置过期时间并且自动统计 key 的规模。清理的时候只需要调用工具类里的deleteByPrefix方法它会自动判断 key 类型、大小选择用 UNLINK 还是 DEL同时能绕过锁 key。根据我个人的经验清理 Redis 缓存最大的价值不是释放多少内存而是通过一次次清理倒逼你把缓存治理的规范建立起来。比如每次清理前都先看一眼慢日志、内存碎片率、大 key 列表清理后对比一下内存曲线和命中率这样你就能逐渐摸清每个业务的缓存特征。我自己后来已经很少手动去清理缓存了因为前置规范做好了缓存过期时间合理了自动淘汰机制也能兜底大部分问题都不会到需要人工清库的地步。但如果某天真的需要手动清理至少你会知道该用 UNLINK 而不用 DEL该用 SCAN 而不用 KEYS该避开锁 key 而不是一把梭。这些经验就是我一直想分享给你的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑