使用lua脚本批量删除redis匹配的key

1,318 阅读2分钟

lua脚本. 参数顺序: matchKey, cursor

-- 打印开始删除日志
if (KEYS[2] == "0") then redis.log(redis.LOG_NOTICE, "start scan del key:", KEYS[1]) end

-- 扫描key. 经过测试, count 取值 10000 不会导致长时间阻塞
local v = redis.call("SCAN", KEYS[2], "MATCH", KEYS[1], "COUNT", 10000)

-- 遍历key, 每100条进行一次删除
local ks, size = {}, 0
for key, value in ipairs(v[2]) do
    ks[size + 1], size = value, size + 1
    if (size == 100) then
        redis.call("DEL", unpack(ks))
        ks, size = {}, 0
    end
end

-- 删除剩下的key
if (size > 0) then
    redis.call("DEL", unpack(ks))
end

-- 打印结束删除日志
if (v[1] == "0") then redis.log(redis.LOG_NOTICE, "end scan del key:", KEYS[1]) end

-- 返回游标, 如果返回0 应该结束循环
return v[1]

具有以下特性

  • 提供了简单的输入参数
  • 遍历key和删除由脚本执行, 没有传输到客户端的流量浪费
  • 分段扫描, 每10000个key进行一次扫描, 不会长时间阻塞redis
  • 分段删除, 每100key调用一次redis.del
  • 开始和结束时提供日志打印

使用说明

你应该写一个循环来调用这个脚本.

  1. 首次调用时cursor应该为0, 调用成功后会返回一个cursor
  2. 将上一步获取到的cursor作为新的游标值传递给脚本. 如果cursor为0则遍历结束, 否则重复步奏2.

示例代码(伪代码)

const LuaScanDelKeyScript = `脚本代码` // 这个常量写入lua脚本
const MatchKey = "*" // 这是匹配的key

var cursor int // 首次调用应该将游标设为0
for {
    // 这里的 redis.Call 表示调用redis命令, 具体根据不同模块不同语言请修改为自己的代码.
    cursor = redis.Call("eval", LuaScanDelKeyScript, 2, MatchKey, cursor) // 模拟eval命令
    if cursor == 0 { // 如果游标为0表示遍历完成
        break
    }
}