天天看点

我想模糊删除 redis key

源自:https://xie.infoq.cn/article/0e34856ec9a88d749e7b1ae7a

前提

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

我想模糊删除 redis key

截屏2020-07-16 下午2.43.17.png



redis 2.6.0 之前

通过 ​

del

 直接删除(不推荐)

redis官方中的 ​

del

 是不支持正则表达式的,但是你可以从网上找到相关的办法让 ​

del

 命令支持正则,因为不推荐,所有不展示

通过 ​

shell

 实现

redis中的 ​KEYS  是支持通过正则表达式获取匹配的key,然后通过管道传递给 ​xargs 进行删除

redis-cli KEYS "unkey*" | xargs redis-cli DEL



我想模糊删除 redis key

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



我想模糊删除 redis key

截屏2020-07-16 下午2.38.59.png



redis 2.8.0 之后

通过 ​SCAN 替代 ​

KEYS

redis的执行是单线程的(至少当前版本是),如果我们通过 ​

KEYS

 去批量的获取key,如果数量很大的话,会有问题:

  1. 没有limit,只能一次获取所有,如果数据量很大的话全部输出的话,交互不友好。
  2. keys 是遍历算法,时间复杂度是 O(n),如果数据量特别大的话,可能会导致服务卡顿,影响到正常业务。

如果我们用 ​

SACN

 替代的话,就会好很多:

  1. 时间复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程
  2. 提供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

 执行完成,下一个命令才会执行。而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];