如何在没有 lua 的情况下对 Redis 密钥进行 100% 确定的条件更新?

How to make a 100% sure conditional update of a Redis key without lua?

我想更新一个 Redis 键,但前提是它在更新时的值等于某个值。

有人在 SO here 上提出了类似的问题,我发现的是关于 Lua 脚本的建议,我不喜欢它。

我在 Reddit 上发现 this post,提示如下:

  1. GET X

  2. 如果值为 AWATCH X

  3. MULTI

  4. SET X 到 B

  5. EXEC

乍一看,这看起来不错,因为如果密钥在 WATCHEXEC 之间的任何时间被另一个进程更新,Redis 将中止事务。

然而,当我仔细考虑时,我想出了一个问题:

如果密钥在 GETWATCH 之间更新怎么办?

有没有一种方法只有在 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

,因此在锁定期间任何更新该值的尝试都将失败

您可以更改 retryCountretryDelay 以及其他选项以适应您的用例

示例 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