【发布时间】:2016-07-26 07:14:11
【问题描述】:
我只是想了解 Redis/Lua 脚本,我想知道是否有人发现以下代码有问题。
这是我尝试实现非常简单的“CAS”语义:用一个键和两个参数调用它。它将检查服务器上与该键关联的值是否以第一个参数开头,如果是,则将设置键的新值设置为第二个参数并返回 1 否则它将返回0;如果键与字符串以外的某种类型的数据相关联,则 Redis 将返回并出错,就像您尝试对此类键/值组合执行 SET 命令一样。如果在调用之前该键不存在,则该函数将返回 0(失败)。
这是脚本:
local x=string.len(ARGV[1]);
if redis.call('GETRANGE', KEYS[1], 0, x-1) == ARGV[1] then
redis.call('SET', KEYS[1], ARGV[2]);
return 1;
end;
return 0
这是一个在前缀值为“bar”的键“foo”上调用脚本的示例(在 redis-cli 中):
eval "local x=string.len(ARGV[1]); if redis.call('GETRANGE', KEYS[1], 0, x-1) == ARGV[1] then redis.call('SET', KEYS[1], ARGV[2]); return 1; end; return 0" 1 foo bar barbazzle
我认为这种情况的使用模式可能是您希望同时存储“防护令牌”和带有键的值......如果并发客户端持有正确的防护,则允许并发客户端尝试更新该值令牌。
这似乎是一种安全的使用模式来代替 WATCH/MULTI/EXEC 语义? (似乎您可以获取当前值,在本地代码中拆分围栏令牌,构建一个新值,然后尝试使用看起来比 WATCH/MULTI/EXEC 调用更容易混淆的语义随时更新密钥)。
(我知道我的脚本的语义与 memcached CAS 命令略有不同;这是故意的)。
这确实通过了我有限的测试......所以我真的在问任何潜在的并发/原子性问题以及 Lua 中是否有任何愚蠢的东西——因为我过去几乎没有接触过 Lua)。
【问题讨论】:
-
好问题,完美呈现,只要键是字符串,就原子性/并发性/其他方面而言,您将完全没问题
-
有一个有趣的 discussion 与 Salvatore Sanfilippo 讨论了为什么 CAS 方法没有在 Redis 设计中实现。