所以我正在寻求构建一个lua脚本,它使用SCAN根据模式查找键并删除它们(原子地).我首先准备了以下脚本
local keys = {};
local done = false;
local cursor = "0"
repeat
local result = redis.call("SCAN", cursor, "match", ARGV[1], "count", ARGV[2])
cursor = result[1];
keys = result[2];
for i, key in ipairs(keys) do
redis.call("DEL", key);
end
if cursor == "0" then
done = true;
end
until done
return true;
Run Code Online (Sandbox Code Playgroud)
哪个会吐回以下"错误:@user_script:9:非确定性命令后不允许写入命令"所以我想了一下,想出了以下脚本:
local all_keys = {};
local keys = {};
local done = false;
local cursor = "0"
repeat
local result = redis.call("SCAN", cursor, "match", ARGV[1], "count", ARGV[2])
cursor = result[1];
keys = result[2];
for i, key in ipairs(keys) do
all_keys[#all_keys+1] = key;
end
if cursor == "0" then
done = true;
end
until done
for i, key in ipairs(all_keys) do
redis.call("DEL", key);
end
return true;
Run Code Online (Sandbox Code Playgroud)
仍然返回相同的错误(@user_script:17:非确定性命令后不允许写入命令).这让我很难过.有没有办法绕过这个问题?
脚本是使用phpredis和以下运行的
$args_arr = [
0 => 'test*', //pattern
1 => 100, //count for SCAN
];
var_dump($redis->eval($script, $args_arr, 0));
Run Code Online (Sandbox Code Playgroud)
Ita*_*ber 13
更新:以下适用于最高3.2的Redis版本.从那个版本开始,基于效果的复制解除了对非决定论的禁令,所以所有的赌注都是关闭的(或者更确切地说是开启).
您不能(也不应该)将SCAN命令族与脚本中的任何写命令混合在一起,因为前者的回复依赖于内部Redis数据结构,而这些数据结构又是服务器进程唯一的.换句话说,两个Redis进程(例如主服务器和从服务器)不能保证返回相同的回复(因此在Redis复制上下文[这不是操作 - 但基于语句]会破坏它).
Redis的试图通过阻止任何写入命令(例如,以保护自身免受这样的情况下DEL),如果它是一个随机命令后执行(例如SCAN而且TIME,SRANDMEMBER和类似的).我确信有办法解决这个问题,但你想这样做吗?请记住,您将进入未定义系统行为的未知区域.
相反,接受这样一个事实:你不应该混合随机读写,并尝试考虑一种不同的方法来解决你的问题,即以原子方式根据模式删除一串键.
首先问问自己是否可以放松任何要求.它必须是原子的吗?原子性意味着Redis将在删除期间被阻止(无论最终实现),并且操作的长度取决于作业的大小(即删除的键的数量及其内容[删除大的集合是比删除短字符串更昂贵,例如]).
如果原子性不是必须的,定期/懒惰SCAN并且小批量删除.如果它是必须的,要明白你基本上试图模仿邪恶的KEYS命令:)但如果你事先了解这种模式,你可以做得更好.
假设在应用程序的运行时期间已知该模式,您可以收集相关的键(例如,在Set中),然后使用该集合以原子和复制安全的方式实现删除,与遍历整个键空间相比更高效.
但是,最"困难"的问题是,如果您需要在确保原子性的同时运行ad-hoc模式匹配.如果是这样,问题归结为获得密钥空间的按模式过滤的快照,紧接着是一系列删除(重新强调:当数据库被阻止时).在这种情况下,你可以很好地使用KEYS你的Lua脚本,并希望最好的...(但完全知道,你可以SHUTDOWN NOSAVE很快采取:P).
Last Optimization用于索引键空间本身.双方SCAN并KEYS基本上全表扫描,所以如果我们要索引的表?想象一下,在事务期间可以查询的键名称上保留索引 - 您可以使用排序集和词典范围(HT @TwBert)来消除大多数模式匹配需求.但是成本很高......你不仅会进行双重记账(将每个密钥的名称成本存储在RAM和CPU中),还会被迫增加应用程序的复杂性.为何增加复杂性?因为要实现这样的索引,您必须自己在应用程序层(可能还有所有其他Lua脚本)中进行维护,在每次更新索引的事务中将每个写入操作小心地包装到Redis中.
假设你做了所有这些(并考虑到明显的陷阱,例如增加的复杂性可能存在错误,至少加倍Redis,RAM和CPU上的写入负载,对扩展的限制等等),你可以拍拍自己肩并祝贺自己以不适合的方式使用Redis.虽然即将推出的Redis版本可能(或可能不)包含更好的解决方案来应对这一挑战(@TwBert - 想要联合RCP/contrib并再次破解Redis?),在尝试之前我真的恳请您重新考虑原始要求并验证您是否正确使用Redis(即根据您的数据访问需求设计"架构").
| 归档时间: |
|
| 查看次数: |
6517 次 |
| 最近记录: |