在使用Redis的过程中,我们经常需要按模式批量清理key,比如缓存按业务前缀命名(user:、order:、cache:),当某类缓存需要整体失效时,一条条执行DEL显然不现实。Redis提供了KEYS命令可以匹配模式,但官方并不建议在生产环境使用它,原因在于KEYS会一次性遍历整个键空间,期间阻塞Redis主线程,key数量达到百万级别时,服务可能卡顿数秒甚至更久,这对线上业务是不可接受的。本文将介绍几种安全、高效的批量删除方案。

一、为什么不能直接用KEYS加DEL
很多初学者会写出类似下面的命令:
redis-cli KEYS "user:*" | xargs redis-cli DEL
这种写法在测试环境小数据量下可以工作,但存在两个严重问题。第一,KEYS命令是O(N)复杂度的全量遍历,N是整个库的key总数而不是匹配到的数量,执行期间Redis无法处理其他请求,属于典型的阻塞操作。第二,如果匹配结果非常庞大,一次性把所有key传给DEL同样会造成长时间阻塞,甚至可能撑爆客户端内存。
Redis从2.8版本开始提供了SCAN系列命令作为替代,它的设计思路是游标分批遍历,每次只取一小部分key就返回,将一次性的大阻塞拆分成多次微小阻塞,中间穿插其他正常请求,整体上对服务的影响可控。SCAN的语法是SCAN cursor [MATCH pattern] [COUNT count],cursor初始为0,每次返回新游标,直到再次返回0表示遍历结束。
二、使用SCAN加DEL渐进式批量删除
最推荐的通用做法是通过SCAN逐批获取匹配的key,再逐批DEL删除。以命令行为例:
redis-cli --scan --pattern "user:*" | xargs -L 1000 redis-cli DEL
--scan参数让redis-cli内部使用SCAN遍历而不是KEYS,-L 1000表示每1000个key调用一次DEL,避免单次DEL的key列表过长。这个命令组合简单直接,适合在服务器上快速执行一次性清理任务。
如果需要更精细的控制,可以用脚本手动驱动游标。下面是一个Bash脚本示例:
#!/bin/bash
redis-cli --scan --pattern "user:*" --count 1000 | while read -r line; do
redis-cli DEL "$line"
done逐条DEL虽然网络往返次数多,但每次操作开销极小,对Redis的压力最均匀。也可以把多条DEL合并成一条,通过减少网络往返提升速度,具体取决于清理规模与时间窗口的权衡。需要注意COUNT参数只是一个提示值,不代表每次返回的精确数量,实际返回可能多于或少于该值,代码逻辑不能依赖精确分页。
三、用客户端代码实现批量删除
在应用层实现时,以Python的redis-py库为例,可以这样编写:
import redis
r = redis.Redis(host='127.0.0.1', port=6379, db=0)
def batch_delete(pattern, batch_size=500):
cursor = 0
total = 0
while True:
cursor, keys = r.scan(cursor, match=pattern, count=batch_size)
if keys:
total += r.delete(*keys)
if cursor == 0:
break
return total
deleted = batch_delete("user:*")
print("deleted keys:", deleted)这段代码的逻辑清晰:循环调用scan获取游标和key列表,用delete批量删除,直到游标归零。scan方法底层封装了SCAN命令,match参数对应MATCH子句。Java开发者使用Jedis或Lettuce时思路完全一致,都是先scan再批量del,只是API名称略有差异。
SCAN有一个容易被误解的特性:它保证在遍历开始到结束期间一直存在的key一定被返回,但如果遍历过程中有key被写入或删除,可能会重复返回或漏掉。对于批量删除场景这通常不是问题,重复DEL一个不存在的key只是返回0,不会报错。
四、大key删除与UNLINK方案
如果匹配到的key中有大key,比如包含数百万元素的集合或哈希,DEL本身也是阻塞操作,删除一个大集合可能阻塞数百毫秒。Redis 4.0引入了UNLINK命令,它与DEL语义相同,但删除动作由后台线程异步执行,主线程只做标记和入队,几乎不产生阻塞。上面的脚本中把DEL替换成UNLINK即可:
redis-cli --scan --pattern "bigdata:*" | xargs -L 100 redis-cli UNLINK
如果Redis版本低于4.0,还可以借助SCAN系列的SSCAN、HSCAN等命令,分批删除集合或哈希内部的元素,把大key逐步掏空后再删除key本身。这种方式虽然繁琐,但在老版本环境下是唯一安全的做法。
五、生产环境注意事项
首先,务必确认模式匹配的准确性。建议先用--scan --pattern不带删除命令跑一遍,人工抽查匹配结果,避免误删其他业务的key。其次,尽量在业务低峰期执行,虽然SCAN不阻塞,但大量DEL操作仍会占用CPU和网络带宽。第三,如果使用的是Redis集群,SCAN只能遍历单个节点的键空间,需要针对每个master节点分别执行,或者使用支持的集群遍历工具。最后,对于有主从复制的架构,批量删除会产生大量复制流量,要评估对从库和延迟的影响。
总结一下,批量删除匹配模式的key的标准做法是SCAN加DEL或UNLINK,命令行下用redis-cli --scan配合xargs最便捷,应用层则通过客户端循环驱动游标实现。核心原则只有一条:避免全量阻塞操作,把删除动作拆小拆碎,让清理过程对线上服务无感知。