如何在没有 lua 的情况下对 Redis 密钥进行 100% 确定的条件更新?
How to make a 100% sure conditional update of a Redis key without lua?
我想更新一个 Redis 键,但前提是它在更新时的值等于某个值。
有人在 SO here 上提出了类似的问题,我发现的是关于 Lua 脚本的建议,我不喜欢它。
我在 Reddit 上发现 this post,提示如下:
GET
X
如果值为 A
则 WATCH
X
MULTI
SET
X 到 B
EXEC
乍一看,这看起来不错,因为如果密钥在 WATCH
和 EXEC
之间的任何时间被另一个进程更新,Redis 将中止事务。
然而,当我仔细考虑时,我想出了一个问题:
如果密钥在 GET
和 WATCH
之间更新怎么办?
有没有一种方法只有在 100% 确定在交易时它具有特定值时才更新密钥,并且只能通过使用 Redis 命令来实现这一点,就像在 [=66 中所做的那样=] 和
UPDATE ...
CASE ...
WHEN ...
THEN ...
声明?
仅使用命令在服务器上完成检查是无法做到的。它已被讨论和丢弃,正是因为 Lua 脚本适合该目的。见 discussion here
这可以通过 Lua 在一行 EVAL
命令中编写脚本来完成:
EVAL "if redis.call('get', 'myKey') == 'expectedVal' then return redis.call('set', 'myKey', 'newVal') else return redis.error_reply('myKey has changed!') end" 0
"Redis guarantees that a script is executed in an atomic way: no other script or Redis command will be executed while a script is being executed" EVAL command docs
Lua 脚本的性能优于 Optimistic locking using check-and-set。如果存在竞争条件,最好在原子操作上进行检查和设置,以减少输掉比赛的可能性。
最好使用Lua脚本,但如果您坚持不使用它,您可以考虑在更新资源时使用redlock锁定资源。鉴于更新过程也在使用 redlock
,因此在锁定期间任何更新该值的尝试都将失败
您可以更改 retryCount
和 retryDelay
以及其他选项以适应您的用例
示例 redlock
// the string identifier for the resource you want to lock
var resource = 'locks:account:322456';
// the maximum amount of time you want the resource locked in milliseconds,
// keeping in mind that you can extend the lock up until
// the point when it expires
var ttl = 1000;
redlock.lock(resource, ttl).then(function(lock) {
// ...do something here...
// unlock your resource when you are done
return lock.unlock()
.catch(function(err) {
// we weren't able to reach redis; your lock will eventually
// expire, but you probably want to log this error
console.error(err);
});
});
虽然不推荐事务,但您仍然可以使用它来实现您的目标,即在任何其他操作之前观察密钥。
1. WATCH X
2. GET X
3. if value is not equal to A, UNWATCH X and abort
4. MULTI
5. SET X B
6. EXEC
我想更新一个 Redis 键,但前提是它在更新时的值等于某个值。
有人在 SO here 上提出了类似的问题,我发现的是关于 Lua 脚本的建议,我不喜欢它。
我在 Reddit 上发现 this post,提示如下:
GET
X如果值为
A
则WATCH
XMULTI
SET
X 到B
EXEC
乍一看,这看起来不错,因为如果密钥在 WATCH
和 EXEC
之间的任何时间被另一个进程更新,Redis 将中止事务。
然而,当我仔细考虑时,我想出了一个问题:
如果密钥在 GET
和 WATCH
之间更新怎么办?
有没有一种方法只有在 100% 确定在交易时它具有特定值时才更新密钥,并且只能通过使用 Redis 命令来实现这一点,就像在 [=66 中所做的那样=] 和
UPDATE ...
CASE ...
WHEN ...
THEN ...
声明?
仅使用命令在服务器上完成检查是无法做到的。它已被讨论和丢弃,正是因为 Lua 脚本适合该目的。见 discussion here
这可以通过 Lua 在一行 EVAL
命令中编写脚本来完成:
EVAL "if redis.call('get', 'myKey') == 'expectedVal' then return redis.call('set', 'myKey', 'newVal') else return redis.error_reply('myKey has changed!') end" 0
"Redis guarantees that a script is executed in an atomic way: no other script or Redis command will be executed while a script is being executed" EVAL command docs
Lua 脚本的性能优于 Optimistic locking using check-and-set。如果存在竞争条件,最好在原子操作上进行检查和设置,以减少输掉比赛的可能性。
最好使用Lua脚本,但如果您坚持不使用它,您可以考虑在更新资源时使用redlock锁定资源。鉴于更新过程也在使用 redlock
,因此在锁定期间任何更新该值的尝试都将失败您可以更改 retryCount
和 retryDelay
以及其他选项以适应您的用例
示例 redlock
// the string identifier for the resource you want to lock
var resource = 'locks:account:322456';
// the maximum amount of time you want the resource locked in milliseconds,
// keeping in mind that you can extend the lock up until
// the point when it expires
var ttl = 1000;
redlock.lock(resource, ttl).then(function(lock) {
// ...do something here...
// unlock your resource when you are done
return lock.unlock()
.catch(function(err) {
// we weren't able to reach redis; your lock will eventually
// expire, but you probably want to log this error
console.error(err);
});
});
虽然不推荐事务,但您仍然可以使用它来实现您的目标,即在任何其他操作之前观察密钥。
1. WATCH X
2. GET X
3. if value is not equal to A, UNWATCH X and abort
4. MULTI
5. SET X B
6. EXEC