
做后端的朋友应该都经历过这种场景线上某个模块出了问题往Redis里塞了一堆垃圾缓存比如order:temp:*、cart:tmp_*、verify:code:*业务一恢复这些键就成了没人读没人管的历史遗留。我见过不少同事第一次遇到按模式删键这个问题时第一反应就是在命令行敲DEL order:temp:*结果当然是什么都删不掉——因为DEL命令接受的是具体键名根本不会帮你做模式匹配。真正等到你需要批量清理一批包含特定模式的键时几个关键问题就摆上来了怎么安全地找出这些键怎么在不影响线上服务的前提下删除删错了怎么办这篇文章我会结合自己这些年实际踩坑和实操的经验把Redis键值对批量删除这件事从头到尾讲透。内容主要围绕如何用SCAN扫描、如何用DEL/UNLINK删除、如何在单机和集群环境下安全操作、误删之后怎么补救这几个核心点展开适合后端开发、运维、DBA以及刚接触Redis的初学者参考。读完你会发现批量删键本身不难难的是做得安全、高效、可回滚。我下面的每一个步骤都是自己在生产环境实际验证过的按着做能少走很多弯路。1. 项目概述与方案选型为什么不能直接删删删1.1 需求场景拆解什么情况下需要按模式删键先说说我在实际工作中遇到过的几类典型场景你可以对照一下自己是不是也有同样的需求。第一类是缓存键前缀管理不严格导致的垃圾键堆积。比如系统里某个旧接口用了user:info:前缀做缓存后来接口升级成了account:前缀新旧两套键在Redis里共存。旧键没人读但也不会自动消失除非设置了过期时间。时间一长上千个user:info:*键就占着内存。这种场景下你没法一个个删因为键名中间可能带着用户ID你不可能手写几千条DEL命令。第二类是批量清理测试数据或者临时数据。联调环境、灰度环境里经常会生成大量临时键比如session:tmp_20240101、import:batch:*。这些键往往有共同前缀但过期时间设置得很远不清理就会把测试环境的内存打满。我见过有人在测试环境里直接用FLUSHDB这确实简单粗暴但如果你只想清某个模块的数据FLUSHDB会把其他业务的数据也一并干掉风险极大。第三类是数据迁移或者Key重命名之后的残留。很多团队会写一次性脚本把键从old:key:*迁移到new:key:*迁移完成后旧键就成了废弃数据。这种场景通常涉及数量很大的键而且键名模式非常明确非常适合用模式匹配批量删除。这几个场景的共同特点是什么呢键名很长、数量很多、模式非常清晰。你去数一下可能涉及到几千甚至几万个键手动删不现实用FLUSHDB又太粗暴。这个时候按模式匹配键名再批量删除就是最合理的手段。1.2 核心取舍KEYS 与 SCAN 的差异聊批量删除绕不开两个核心命令KEYS和SCAN。两个命令都能按模式匹配键名但使用场景完全不同。KEYS的用法很简单KEYS user:*就会一次性返回所有匹配的键。看起来很方便但问题非常致命Redis是单线程模型所有命令都在主线程里排队执行。KEYS命令的时间复杂度是O(N)N是Redis里所有的键总数而不是匹配到的键数量。也就是说哪怕你的模式是user:*它也会把整个键空间扫一遍。我在一个键总量在500万左右的生产Redis实例上做过测试执行KEYS *大概需要几十秒甚至更久。这期间Redis无法处理其他任何请求所有读写都会被阻塞。在线上的高流量环境下这就是一场事故。别说生产环境就是测试环境键数量比较大的时候执行KEYS *都能明显感觉到界面卡顿。SCAN则完全不同。它采用的是增量迭代的方式每次执行只返回一小批键配合游标cursor不断推进直到游标回到0才算遍历结束。SCAN每次执行的时间复杂度是O(1)不会阻塞Redis主线程可以在线上环境安全使用。打个比方KEYS就像把整个仓库里的所有东西一次性拖出来清点期间仓库门口全部堵住谁也不能进出SCAN则是拿着一本小小的手账本每次只翻几页记录一页就放行一批仓库还能正常运转。所以在批量删除这个场景下正确思路必然是SCAN遍历键名DEL删除键。先通过SCAN把匹配的键逐步拿出来再交给DEL去删这样既不会阻塞Redis也能精准地按模式清理。2. 核心工具与命令细节解析2.1 用 redis-cli SCAN xargs 组合最经典的做法redis-cli本身就内置了--scan选项它的作用就相当于SCAN命令的客户端封装。最朴素的一条删除命令长这样redis-cli --scan --pattern session:* | xargs -L 100 redis-cli DEL这条命令的思路很清晰第一步redis-cli --scan --pattern session:*把匹配到的键名一行一个输出到标准输出第二步通过管道交给xargs每凑齐100个键就执行一次redis-cli DEL。为什么要用-L 100因为DEL命令支持一次删除多个键比如DEL session:1 session:2 session:3。把键分成一批批删除比一条命令只删一个键要高效得多。一个键一次网络往返和100个键一次网络往返开销差距很明显的。如果你的Redis不在本机或者端口不是默认的6379需要带上连接参数redis-cli -h 192.168.1.10 -p 6379 --scan --pattern session:* | xargs -L 100 redis-cli -h 192.168.1.10 -p 6379 DEL这里有一个我必须提醒的坑命令行传密码很容易被系统进程列表看到。如果你用-a参数传密码执行ps aux的时候其他人都能看到你的Redis密码这在生产环境是非常危险的行为。建议设置环境变量REDISCLI_AUTHexport REDISCLI_AUTHyour_password redis-cli --scan --pattern session:* | xargs -L 100 redis-cli DEL这样密码就不会出现在命令行参数里了。还有一个容易被忽略的问题如果键名里包含空格或者换行符用管道加xargs的方式会被空格把键名拆断导致删除失败或者删错键。虽然Redis键名里带空格的情况很少见但一旦出现就是灾难。如果你要操作的数据比较重要更稳妥的方式是先把键名列表导出到文件再用xargs -a读取文件进行分批删除。# 第一步导出键名 redis-cli --scan --pattern session:* /tmp/session_keys.txt # 第二步统计数量 wc -l /tmp/session_keys.txt # 第三步确认无误后分批删除 xargs -a /tmp/session_keys.txt -L 100 redis-cli DEL这样做的另一个好处是删除前你有机会先看看键的数量如果和你预期差太多就说明模式可能写错了可以及时刹车。2.2 用 unlink 还是 del删除大键时的性能差异很多人在删除Redis键的时候只会用DEL其实Redis 4.0之后提供了一个更好的选择UNLINK。表面上看UNLINK和DEL作用是一样的删除键并释放内存但底层实现有本质区别。DEL是同步删除它会立即回收这个键的内存。如果这个键对应的值是一个很大的列表、哈希或者集合比如里面有几十万个元素那么释放内存本身就是一件耗时的事。前面提过Redis是单线程的DEL一个大键的时间可能长达几百毫秒甚至秒级这段时间Redis同样会被卡住。UNLINK则是异步删除。它执行时只是把键从键空间中摘除然后返回结果真正释放内存的操作交给后台线程慢慢做主线程可以立刻继续处理其他命令。我在测试环境里做过一个实验一个包含100万个元素的Set执行DEL大约花了1.1秒期间对应Redis实例上的其他请求全部超时。换成UNLINK之后命令本身几乎是瞬间返回后台线程慢慢回收内存整个过程中Redis响应完全正常。所以我的建议很直接如果你不确定要删的键对应的值是大是小直接用UNLINK。它和DEL的兼容性很好Redis 4.0以上都支持而且删除小键时的性能和DEL几乎没有差别删除大键时却可以避免阻塞事故。对比项DELUNLINK阻塞性同步删除可能阻塞Redis异步删除后台线程回收内存适用场景小键、明确知道值很小的键大键、不确定键的大小的场景版本要求所有版本Redis 4.0及以上返回效果返回1表示删除成功返回1表示键已从键空间摘除唯一的注意点是UNLINK后内存是逐步释放的如果你想立刻腾出内存给其他业务使用可能还得等一小会儿。但在绝大多数情况下用UNLINK都是更安全的选择。2.3 Lua 脚本批量删除原子性为什么重要另一种常见的批量删除方案是写Lua脚本通过EVAL命令执行。使用Lua的好处是脚本里所有的Redis操作在同一个执行流里完成中间不会插入其他客户端发来的命令。也就是说整个删除过程是原子的。先看一个最简单的Lua脚本local keys redis.call(KEYS, ARGV[1]) if #keys 0 then return 0 end return redis.call(DEL, unpack(keys))调用方式redis-cli EVAL local keys redis.call(KEYS, ARGV[1]); if #keys 0 then return 0 end; return redis.call(DEL, unpack(keys)) 0 session:*这个脚本写起来很简洁但我必须明确告诉你千万不要在大数据量的Redis实例上用这个脚本。原因我们前面说过KEYS命令会阻塞Redis脚本里它依然会阻塞。如果键数量是几百万这个脚本执行的时候Redis就是全站卡死状态。真正需要用到Lua脚本的场景应该是既要按模式删除又要保证删除过程是原子的。比如有一个batch:*前缀的键每次删除的时候可能要同时检查某个状态键的值只有状态合法才执行删除或者需要在删除后更新一个计数键。这种情况下你可以在Lua脚本里组合多条Redis命令-- 按SCAN方式迭代避免KEYS阻塞 local cursor 0 local count 0 repeat local result redis.call(SCAN, cursor, MATCH, ARGV[1], COUNT, 100) cursor result[1] for _, key in ipairs(result[2]) do redis.call(UNLINK, key) count count 1 end until cursor 0 return count这个脚本在Lua里使用SCAN游标迭代每次取100个键执行UNLINK直到遍历完整个键空间。因为SCAN是增量式的脚本不会一次性把所有键都载入内存所以比KEYS方案安全得多。不过我还是要泼一盆冷水如果生产环境键数量特别大哪怕是SCAN在Lua里循环也可能执行很久导致脚本长时间占用Redis执行线程。Lua脚本执行时间过长Redis会对后续命令产生明显的延迟。所以我个人更推荐的方式是用redis-cli --scan在客户端遍历键名键名先落盘或者直接交给xargs分批删除。这样每一步的操作都是短小的即使出了问题影响面也可控。3. 实操过程与核心环节实现3.1 基础命令单机环境安全删除现在进入具体的实操。单机Redis环境的批量删除是最常见的场景我以清理user:temp:*这个前缀的键为例给你梳理一套我认为最稳妥的操作流程。第一步确认模式匹配到的键大致数量。redis-cli --scan --pattern user:temp:* | wc -l先跑一下数量心里有底。如果你预期只有100个键结果返回5万个那模式可能写得太宽了比如user:*把不该删的user:info也带上了。这时候先不要急着删除回到业务代码里确认键名的前缀设计到底是怎么样的。第二步抽样检查键的类型和内容。redis-cli --scan --pattern user:temp:* | head -n 10 | while read k; do echo key $k redis-cli type $k done抽样看几个键确认它们确实是你想清理的临时数据。我见过有同事按照前缀删除结果业务方早就改了键名规范新老前缀混在一起一删就把正在使用的数据删了。先抽样可以避免这种惨剧。第三步导出键名到文件形成删除清单。redis-cli --scan --pattern user:temp:* /tmp/user_temp_keys.txt wc -l /tmp/user_temp_keys.txt删除清单保留下来既方便确认数量也方便后续排查和审计。第四步执行删除。这里我推荐使用UNLINK因为你不知道其中是否隐藏着大键用UNLINK最稳妥xargs -a /tmp/user_temp_keys.txt -L 100 redis-cli UNLINK如果你用的是Redis 4.0以下的老版本才需要用DEL替代xargs -a /tmp/user_temp_keys.txt -L 100 redis-cli DEL第五步删除后验收。redis-cli --scan --pattern user:temp:* | wc -l数量应该是0如果有余留可能是删除过程中业务又生成了新的键。这算正常情况再跑一遍删除流程即可。这套流程看起来简单但每一步都在回答一个关键问题我到底要删什么删了多少删干净了吗养成这个习惯以后你在生产环境做任何删除操作都会从容很多。3.2 进阶实践集群环境的批量删除方案到了Redis Cluster环境批量删除就不能照搬单机命令了这里面的坑还挺多。首先集群环境有16384个哈希槽键会分散在各个节点上。redis-cli直接执行--scan只能扫到当前连接的节点扫不到其他节点的键。你要是只扫一个节点然后拿着部分键去做删除看起来没有报错实际上数据没删干净。要做集群环境的批量删除正确思路是分节点扫描、逐节点删除。先查一下集群有哪些节点redis-cli -h 192.168.1.10 -p 7000 --cluster info然后把每个主节点的地址端口记下来逐个扫描。假设你有三个主节点端口分别是7000、7001、7002redis-cli -h 192.168.1.10 -p 7000 --scan --pattern cart:tmp:* /tmp/cart_keys_7000.txt redis-cli -h 192.168.1.10 -p 7001 --scan --pattern cart:tmp:* /tmp/cart_keys_7001.txt redis-cli -h 192.168.1.10 -p 7002 --scan --pattern cart:tmp:* /tmp/cart_keys_7002.txt删除的时候有一个特别容易踩的坑在集群模式下如果一条DEL命令里带的键不满足同一个哈希槽服务端会直接报CROSSSLOT错误。比如DEL cart:tmp:a cart:tmp:b如果两个键被分到了不同的槽这条命令就没法执行。有人说用redis-cli -c连集群就能自动转发但-c模式面对单键读写时确实会自动转发面对一条包含多个键的DEL命令时多键事务或跨槽命令的限制依然存在。所以最安全的做法是把删除命令拆成单键删除或者每个键单独走一次DEL。我习惯用一个简单的循环来做while read key; do redis-cli -c -h 192.168.1.10 -p 7000 DEL $key done /tmp/cart_keys_7000.txt这样每个键单独一条DEL由客户端自动跟踪键对应的槽并转发到正确的节点。缺点是多几次网络往返但在可接受的范围内。如果你对性能要求比较高可以按槽位分组后把属于同一个槽的键拼到一条DEL里这样能减少命令数量。只是代码复杂度会明显上升。如果你的项目里用了Redis官方推荐的客户端、带有MGET/MSET等接口的连接池方案大部分客户端封装的批量删除接口实际上也是循环执行单键DEL并不会真的用多键DEL命令。所以集群环境下循环单键删除并不可怕反而更符合集群的运作逻辑。另外还有一种方案是借助redis-cli --cluster子命令比如redis-cli --cluster call 192.168.1.10:7000 UNLINK cart:tmp:*这个命令会在所有集群节点上执行同一个命令但它执行的是字面量键名解析不会自动替换为模式匹配。也就是说UNLINK cart:tmp:*只会去尝试删除一个名字叫cart:tmp:*的键而不是匹配所有以cart:tmp:开头的键。所以这个方案没法直接满足按模式删除的需求不要被它误导。3.3 删除前的备份与演练我见过很多人在生产环境删Redis键基本都是删完再说。结果真有误删的时候才发现自己连备份都没有只能干瞪眼。这里我分享一套我在团队里推行的删除前备份保障流程。第一步是判断键数据能否重新生成。如果这些键只是缓存数据删除后业务访问时会重新查询数据库并回填缓存那备份的意义主要在能够审计删了哪些键。这时候你只需要保存键名清单就够了。redis-cli --scan --pattern user:temp:* /tmp/user_temp_keys_$(date %Y%m%d%H%M%S).txt第二步是判断键数据是否必须保留。如果键里存的是用户上传的任务状态、异步任务的中间结果等无法从其他数据源恢复的数据那你不能只备份键名还得想办法备份键值。Redis本身不像关系型数据库那样支持细粒度的单键备份但你可以用DUMP命令把键序列化导出再RESTORE回去。我用过一个土办法批量循环DUMPredis-cli --scan --pattern task:state:* | while read key; do redis-cli --raw DUMP $key /tmp/task_state_dump.bin echo ### $key /tmp/task_state_keys.txt done这里DUMP会输出键的序列化二进制内容RESTORE可以把内容恢复到Redis里。注意DUMP输出的内容可能包含特殊字符直接追加到文件里是可行的但恢复的时候需要对应解析。更规范的做法是配合Python等脚本逐个DUMP并以二进制文件保存。我这里只是给个思路实际操作中光一个DUMP循环的次数也值得注意——键很多时最好分批执行别一口气扫完。第三步就是演练。我在每次计划大范围删除之前一定会先在测试环境跑一遍完整流程扫描、抽样、导出、删除、验证。测试环境的数据结构和生产环境保持一致只是数据量级小一些。演练能发现很多细节问题比如模式写错、忘了加密码环境变量、命令版本不兼容等等。还有个更“保险”的小窍门真正的删除操作安排在工作负载比较低的时间段比如凌晨。这样即使出现异常影响面也小留给团队的应急时间也更多。4. 常见问题与排查技巧实录4.1 SCAN 游标解析与大数据量下的性能表现很多人第一次看SCAN的返回结果会懵第一次返回的是游标和一批键第二次你拿新游标再调用返回的游标又变了。这个游标到底是怎么设计的简单理解SCAN把Redis键空间看成一张固定大小的哈希表游标记录的是遍历到的哈希桶位置。每次调用从当前游标位置开始向后扫描若干个桶返回这些桶里的键同时返回下一个要继续扫描的位置。当游标回到0时整个遍历结束。COUNT参数很多人以为是每次返回多少键实际上它更像是每次扫描多少个桶的提示值。桶里键多的话一次可能返回上千个键桶里键少的话可能只返回几个。所以COUNT是一个性能调优参数并非一个严格的数量限制。我在一个2000万键的Redis实例上测过用COUNT 1000跑完整轮SCAN大概需要几十秒。但是注意这几十秒是分散在很多次命令里的每次命令都不会阻塞Redis其他请求始终能正常处理。这就是SCAN和KEYS最大的区别。另一个现象你可能也会遇到SCAN过程中如果有键被创建、删除或者过期SCAN可能会返回一些已经失效的键也有可能会漏掉少量遍历过程中新添加的键。这是增量遍历的固有特性Redis官方文档也明确说明了。所以当你统计到的数量和实际删除数量对不上时不一定是代码有bug可能是遍历过程中键的状态发生了变动。实操上我会用两次扫描对比数量的方式来规避这种不确定性。第一次扫描只统计数量第二次扫描确认数量如果两次数量一致说明运行环境比较稳定如果不一致就以第二次为准多跑一轮删除。4.2 误删数据找回方案误删数据是每个工程师都不想面对但又确实可能发生的事情。这里我分享几个实际验证过的找回思路覆盖不同场景。如果你的Redis开启了AOF持久化并且删除操作发生之后还没有触发过重写那么AOF文件里会留有删除之前的写操作记录。你可以把AOF文件拉到其他环境重放恢复到误删前的状态再用SCAN把需要的键找出来迁移回生产实例。这个方案理论上可行但需要AOF文件中确实记录了这些键的写入操作而且你得在删除后尽快停下来避免后续操作把AOF记录覆盖掉。如果你的Redis开启了RDB持久化RDB快照里保存的是某个时间点之前的数据。把RDB快照恢复到一台临时Redis实例上然后SCAN找到被误删的键用DUMP和RESTORE把键搬回生产环境。这种方法在恢复单批键的时候非常管用。前提是你得有删除操作之前的RDB快照而且RDB本身没有过期。如果没有备份也没有持久化文件那就只剩下一条路从数据源重新生成。比如缓存键可以从数据库读取后重建临时键可能对应某个异步任务状态重新触发任务就能恢复。这一步在日常运维中最有用因为绝大多数Redis键都是可重建的缓存数据。这也是为什么我一直强调删除前一定要搞清楚键的数据来源。就我个人的团队实践而言我们会在误删发生后立刻做三件事第一所有相关服务暂停写入Redis第二检查备份时间点并评估损失第三如果无法快速恢复立即通知依赖方降级。很多时候误删的键数量不多业务方只需要重试一遍就能自动重建最怕的是默默删了一批没有备份的不可再生数据导致报表错乱、任务丢失排查起来非常费劲。所以预防比找回更关键。我的建议是在生产环境执行危险删除前至少做到三点一是用--scan和wc -l核对数量二是把键名清单保存到本地三是尽量在低峰期操作。4.3 模式匹配特殊字符的坑Redis的键模式匹配用的是glob风格的通配符规则和正则表达式不完全一样。这里很容易踩坑。*匹配任意长度的任意字符?匹配单个任意字符[...]匹配方括号内的任意一个字符。比如user:[0-9]*可以匹配user:123abc但不能匹配user:abc。这套规则看着简单一旦你的键名本身就是带*、?、[、]这些符号麻烦就来了。比如键名叫file[1].cache你执行SCAN MATCH file[*].cache的时候[和]会被解析成字符集结果可能匹配file1.cache、fileA.cache等等而你的真实键名反而匹配不上。这种情况下需要用反斜杠转义redis-cli --scan --pattern file\\[*\\].cache这里\\[和\\]转义了方括号Redis会把它当字面值处理。还有一个容易忽略的问题Redis命令里的模式和Shell命令行的通配符是两个完全不同的体系。你在命令行执行redis-cli --scan --pattern *.txt如果Shell做了路径名扩展它可能在你把参数传给redis-cli之前就已经把模式替换成了当前目录下的文件名列表。我建议你始终用单引号把模式包起来redis-cli --scan --pattern *.txt单引号能阻止Shell对*、?、[这些字符做任何解释确保原样送到Redis。另外MATCH模式的作用范围也值得说一下。SCAN的MATCH过滤是在遍历完当前桶之后才做的并不会因为你的模式很精确就减少哈希桶的扫描范围。比如你执行SCAN MATCH abc*Redis依然会扫描整个键空间的桶然后把不满足匹配条件的键过滤掉再返回。所以模式写得再短也改变不了扫描全量键的开销。这也是我为什么一直建议在键名设计阶段就尽量使用固定前缀来隔离业务扫描匹配时才能更高效。5. 更工程化的封装用 Python 脚本给批量删除加上保险命令行操作适合临时清理但如果你的团队经常要做类似的批量删除我建议把流程固化成脚本。用Python封装一个批量删除工具可以加入dry-run、分批删除、日志记录等能力安全性会高很多。下面是一个我自己经常用的脚本模板功能包括连接参数配置、模式扫描、预览模式、实际删除模式、支持DEL和UNLINK切换、每删一批打印一条日志。import redis import sys import time def scan_keys(r, pattern, count500): 使用scan_iter遍历匹配的键底层是SCAN安全不阻塞 keys [] for key in r.scan_iter(matchpattern, countcount): keys.append(key) return keys def delete_keys(r, keys, use_unlinkFalse, dry_runTrue, batch_size100): 按批次删除支持dry-run total 0 for i in range(0, len(keys), batch_size): batch keys[i:ibatch_size] if dry_run: for k in batch: print([dry-run], k) total len(batch) else: if use_unlink: r.unlink(*batch) else: r.delete(*batch) total len(batch) print(fdeleted {total}/{len(keys)} keys) time.sleep(0.01) # 轻微限速降低对Redis的压力 return total def main(): host 127.0.0.1 port 6379 password None pattern sys.argv[1] if len(sys.argv) 1 else temp:* dry_run True use_unlink True r redis.Redis(hosthost, portport, passwordpassword, decode_responsesTrue) print(fstart scanning pattern: {pattern}) keys scan_keys(r, pattern) print(fmatched keys: {len(keys)}) if not keys: print(no keys matched, exit) return if dry_run: print(dry-run mode, no actual delete) delete_keys(r, keys, use_unlinkuse_unlink, dry_runTrue) else: confirm input(fabout to delete {len(keys)} keys, type yes to continue: ) if confirm yes: delete_keys(r, keys, use_unlinkuse_unlink, dry_runFalse) else: print(cancelled) if __name__ __main__: main()这个脚本有几个设计点我想说明一下。第一个是scan_iter。它封装了SCAN的游标迭代逻辑每次取一批不会一次性把全部键拉进内存但这里会逐步把所有匹配的键收集到Python列表里。键特别多的时候比如十万以上可以考虑改成流式处理边遍历边删除而不是先收集再删除。只是流式处理时如果遍历期间业务新增了匹配的键可能会漏删需要额外处理。第二个是dry_run。我一直坚持默认预览确认后删除的原则。脚本默认不会真删只有你手动改成dry_runFalse并输入yes才会执行真正的删除。这条防线能挡住很多手误。第三个是批次删除和限速。r.delete(*batch)一次删除100个键配合time.sleep(0.01)给Redis一点喘息时间避免在业务高峰期产生过大的瞬时压力。这个脚本在单机环境和集群环境都能用。如果你连的是Redis Cluster只要把redis.Redis换成redis.cluster.RedisCluster客户端会自动处理槽位转发和跨节点访问。Python Redis客户端对集群的scan_iter支持得比较好会帮你轮询所有节点。我自己在实际使用中还会把脚本的输出同时重定向到日志文件这样每次删除操作都有记录哪一天被业务方质问是不是你删了我的数据我能直接甩出日志来自证清白。这个习惯推荐给大家。最后再分享一点个人经验。批量删除Redis键本身是一项非常机械的工作真正的风险都在删错这件事上。所以我强烈建议你在设计Redis键名的时候就想好前缀规范让每个业务模块的键都有清晰可辨的前缀和过期时间。如果发现键没有设置TTL大概率意味着以后你需要人工清理。最理想的状态是键到了时间自动过期后台清理只是兜底手段。有了这个意识你再回来看按模式批量删除它就不是一个救火技能而是一项常规维护操作安全系数和心理压力完全不是一个级别。