redis 无占用 两种方式 清除大批量数据 lua脚本

发布于:2024-04-29 ⋅ 阅读:(37) ⋅ 点赞:(0)

redis存储了很多无用的key,占用了大量内存,需要清除

第一种 (颗粒度较大)

lua脚本,删除某些规则的key,输入删除的key,返回删除的符合规则的key的数量
弊端:颗粒度比较大,发送一个lua脚本去执行,会占用较多时间,堵塞其他redis命令

local function scan(key)
	local cursor = 0
	local keynum = 0
	repeat
		local res = redis.call("scan", cursor, "match", key, "COUNT", ARGV[1])
		if(res ~= nil and #res >= 0) then
			redis.replicate_commands()
			cursor = tonumber(res[1])
			local ks = res[2]
			local keynum1 = #ks
			keynum = keynum + keynum1
			for i=1,keynum1,1 do
				local k = tostring(ks[i])
				redis.call("del", k)
			end
		end
	until(cursor <= 0)
	return keynum
end

local a = #KEYS
local b = 1 
local total  = 0
while(b <= a)
do 
	total = total + scan(KEYS[b])
	b=b+1
end

return total

java代码

String delLua  = "local function scan(key)\n" +
				"\tlocal cursor = 0\n" +
				"\tlocal keynum = 0\n" +
				"\trepeat\n" +
				"\t\tlocal res = redis.call(\"scan\", cursor, \"match\", key, \"COUNT\", ARGV[1])\n" +
				"\t\tif(res ~= nil and #res >= 0) then\n" +
				"\t\t\tredis.replicate_commands()\n" +
				"\t\t\tcursor = tonumber(res[1])\n" +
				"\t\t\tlocal ks = res[2]\n" +
				"\t\t\tlocal keynum1 = #ks\n" +
				"\t\t\tkeynum = keynum + keynum1\n" +
				"\t\t\tfor i=1,keynum1,1 do\n" +
				"\t\t\t\tlocal k = tostring(ks[i])\n" +
				"\t\t\t\tredis.call(\"del\", k)\n" +
				"\t\t\tend\n" +
				"\t\tend\n" +
				"\tuntil(cursor <= 0)\n" +
				"\treturn keynum\n" +
				"end\n" +
				"\n" +
				"local a = #KEYS\n" +
				"local b = 1 \n" +
				"local total  = 0\n" +
				"while(b <= a)\n" +
				"do \n" +
				"\ttotal = total + scan(KEYS[b])\n" +
				"\tb=b+1\n" +
				"end\n" +
				"\n" +
				"return total";
// 指定 lua 脚本,并且指定返回值类型
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(delLua, Long.class);
// 参数一:redisScript,参数二:key列表,参数三:arg(可多个)
long start = System.currentTimeMillis();
Long result = (Long) redisTemplate.execute(redisScript, Collections.singletonList(scenicId + ":*"), 20000);
long end = System.currentTimeMillis();

这样直接删除,因为规则有很多个,需要循环删除,每一次调用脚本都需要占用redis一分多时间,

第二种(切割所有key,每次返回指定数量,然后过滤删除)

lua脚本

local cursor = ARGV[1]
local result = redis.call('SCAN', cursor,  'COUNT', 10000)
return {result[1], result[2]}
// Lua脚本用于SCAN操作,获取指定数量的key
String luaScript = "local cursor = ARGV[1]\n" +
						"local result = redis.call('SCAN', cursor,  'COUNT', 10000)\n" +
						"return {result[1], result[2]}";

// 创建Redis脚本对象
DefaultRedisScript<List> defaultRedisScript = new DefaultRedisScript<>(luaScript, List.class);
defaultRedisScript.setResultType(List.class);

Integer cursorInt = 0;
String newCursor = cursorInt.toString(); // 初始游标

// 参数:cursor初始为0,match为""表示匹配所有key,count为1000
List<Object> keys = stringRedisTemplate.execute(defaultRedisScript, Collections.emptyList(), newCursor);
// 解析结果
newCursor = keys.get(0).toString(); // 新的cursor
List<String> keyList = (List<String>)keys.get(1);
//对获取出来的key进行规则筛选,查看那些可以直接删除
//过滤逻辑 和 删除