如何使用纯Redis以原子方式删除符合模式的数百万个键?

假设我有数百万个 前缀:<numeric_id> 键。

我想要原子性地清除它们。

如何使用Redis删除匹配模式的键 显示了许多选项。一些使用 redis-cli 或 Bash 脚本,但我需要使用我的客户端以编程方式执行它。

使用 Lua 脚本的方法很有前途,但使用 KEYS 命令的解决方法会失败并提示“要解包的元素太多”的错误。

如何实现这一点?

点赞
用户7017466
用户7017466

以下 Lua 脚本使用 SCAN 命令,在脚本内以块的方式进行删除-避免出现“too many elements to unpack”错误。

local cursor = 0
local calls = 0
local dels = 0
repeat
    local result = redis.call('SCAN', cursor, 'MATCH', ARGV[1])
    calls = calls + 1
    for _,key in ipairs(result[2]) do
        redis.call('DEL', key)
        dels = dels + 1
    end
    cursor = tonumber(result[1])
until cursor == 0
return "Calls " .. calls .. " Dels " .. dels

它返回调用SCAN的次数以及已删除键的数量。

用法如下:

EVAL "local cursor = 0 local calls = 0 local dels = 0 repeat    local result = redis.call('SCAN', cursor, 'MATCH', ARGV[1])     calls = calls + 1   for _,key in ipairs(result[2]) do       redis.call('DEL', key)      dels = dels + 1     end     cursor = tonumber(result[1]) until cursor == 0 return 'Calls ' .. calls .. ' Dels ' .. dels" 0 prefix:1

请注意,在运行时它将阻塞服务器,因此不建议用于生产。

对于生产,考虑将 DEL 更改为 UNLINK。您还可以返回游标(而不是在脚本内重复,直到它变为零),并在 SCAN 中添加 COUNT 参数以进行节流(请参见 Redis 中 SCAN / HSCAN 命令的推荐 COUNT 值?)。这样,您可以将其分块处理,而不是同时处理所有键,类似于 如何在 redis 中获取所有集合?

或者,您可以使用本答案中提到的方法来做一些更复杂的事情:Redis `SCAN`: how to maintain a balance between newcomming keys that might match and ensure eventual result in a reasonable time?

2020-04-22 13:31:49
用户593425
用户593425

Lua 是一个很好的选择,只要你不使用 Redis 集群或者你想要删除的所有键都在同一分片上。

如果你需要从多个分片中删除键,你仍然可以使用 Lua,但你必须手动向所有分片发送 Eval 命令。

另一种为你完成的选择是使用 RedisGears(一个 Redis 模块),它允许你编写基于某些条件的跨集群删除命令。

查看例子:https://oss.redislabs.com/redisgears/examples.html#delete-by-key-prefix

2020-04-22 22:07:43