使用纯Redis如何原子性地删除匹配模式的数百万个键?

6

假设有数百万个键,格式为prefix:<numeric_id>

我想要原子地删除它们所有。

如何使用Redis原子方式删除匹配模式的键展示了很多选项。一些使用redis-cli或Bash脚本,但我需要在我的客户端程序中进行操作。

使用Lua脚本的方法很有前途,但是使用KEYS命令的解决方案会失败并显示“元素过多无法打包”的错误信息。

该如何解决这个问题?

2个回答

8
以下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`:如何在新的可能匹配的键和保证合理时间内的最终结果之间保持平衡?


我使用了你的脚本,为什么结果是:"Calls 281447 Dels 0"? - mnrafg
1
没有符合模式的键吗?尝试使用您的匹配模式进行SCAN,看看是否有任何键与其匹配。 - LeoMurillo
谢谢,问题已经解决了,是我的模式有问题。 - mnrafg
我收到了 ERR Error running script: @user_script:1: ERR invalid cursor stack traceback: [G]: in function 'call' @user_script:1: in main chunk [G]: ? 的错误信息。 - ehrencrona

1
Lua是一个不错的选择,只要您没有使用Redis Cluster或者您想要删除的所有键都在同一个分片上。
如果您需要从多个分片中删除键,则仍然可以使用Lua,但您必须手动向所有分片发送“Eval”命令。
另一种为您完成此操作的替代方法是使用Redis模块RedisGears,它允许您基于某些条件编写跨集群del命令。
请参阅示例:https://oss.redislabs.com/redisgears/examples.html#delete-by-key-prefix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接