源自:https://xie.infoq.cn/article/0e34856ec9a88d749e7b1ae7a
前提
redis中存在很多key,可能随着业务的下架永远也用不到了,需要批量删除(当然也可以不处理,等redis内存不足的时候,自动去执行淘汰策略)。假设存在若干个如下模式的key,都是以 unkey 为前缀,现在希望批量删除。

截屏2020-07-16 下午2.43.17.png
redis 2.6.0 之前
通过 del
直接删除(不推荐)
del
redis官方中的
del
是不支持正则表达式的,但是你可以从网上找到相关的办法让
del
命令支持正则,因为不推荐,所有不展示
通过 shell
实现
shell
redis中的 KEYS 是支持通过正则表达式获取匹配的key,然后通过管道传递给 xargs 进行删除
redis-cli KEYS "unkey*" | xargs redis-cli DEL
image.png
redis 2.6.0 之后
通过Lua脚本删除
redis 2.6.0 之后的版本支持通过 EVAL 或 EVALSHA 执行使用Lua。 可以保证操作的原子性,还可以通过
script load
对脚本进行缓存,减少网络开销。使用lua 有更高的自由度,可以添加很多功能模块。
return redis.call('del',unpack(redis.call('keys','unkey*')))
基本流程是这样的,如果逻辑严谨的话,可以动态传入匹配的表达式,返回删除的key之类的逻辑。
-- 参数1: “un*” 正则表达式
local keys=redis.call('keys',ARGV[1]);
if #keys==0 then
return '--No match--'
else
redis.call('del',unpack(keys))
return keys
end
截屏2020-07-16 下午2.38.59.png
redis 2.8.0 之后
通过 SCAN 替代 KEYS
KEYS
redis的执行是单线程的(至少当前版本是),如果我们通过
KEYS
去批量的获取key,如果数量很大的话,会有问题:
- 没有limit,只能一次获取所有,如果数据量很大的话全部输出的话,交互不友好。
- keys 是遍历算法,时间复杂度是 O(n),如果数据量特别大的话,可能会导致服务卡顿,影响到正常业务。
如果我们用
SACN
替代的话,就会好很多:
- 时间复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程
- 提供limit参数,控制返回的条数
-- 参数1: “un*” 正则表达式
-- 每次获取1000条(不一定返回1000条),进行删除
local cursor="0";
repeat
local t = redis.call('scan',cursor,'match',ARGV[1],'count',1000);
local list=t[2];
if #list==0 then
return '--over--'
end
redis.call('del',unpack(list));
cursor=t[1];
until cursor=="0"
return "over";
但是这样写是有问题的,这还是一次获取所有key,虽然每次都获取1000条,但是lua代码原子执行,还是会有一次获取很多阻塞线程的问题。
所有建议控制 每次获取的数量,然后多次执行,每次返回的游标给下次执行使用。
-- 参数1: “0” 游标
-- 参数2: “un*” 正则表达式
local t = redis.call('scan',ARGV[1],'match',ARGV[2],'count',1000);
local list=t[2];
if #list==0 then
return '--over--'
end
redis.call('del',unpack(list));
return t[1];
redis 4.0.0 之后
通过 UNLINK 替代 DEL
DEL
正常的
del
是在主线程内执行,等待当前的
del
执行完成,下一个命令才会执行。而redis在4.0之后提供了更好的选择。
unlink
会在另一个线程中执行内存的回收,不会阻塞正常的get set请求,而且使用的方式也很简单,跟执行
del
一样。
所以说redis 4.0 之后就不再是单线程了,但是基本的操作还是在主线程内执行。
-- 参数1: “0” 游标
-- 参数2: “un*” 正则表达式
local t = redis.call('scan',ARGV[1],'match',ARGV[2],'count',1000);
local list=t[2];
if #list==0 then
return '--over--'
end
redis.call('unlink',unpack(list));
return t[1];