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



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

这里在SO上也提出了类似的问题,我发现的是关于Lua脚本的建议,我不喜欢。

我在Reddit上找到了这篇文章,建议如下:

  1. GET X

  2. 如果值AWATCH X

  3. MULTI

  4. SET X 到 B

  5. EXEC

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

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

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

有没有办法仅在 100% 确定在事务时它保持一定的值时才更新密钥,并且仅通过使用 Redis 命令来实现这一点,就像在 SQL 中使用

UPDATE ...
    CASE ...
        WHEN ...
        THEN ... 

陈述?

虽然不建议交易,但您仍然可以使用它来实现您的目标,即在任何其他操作之前观察密钥

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

没有办法在服务器上仅使用命令完成检查。它已经被讨论和丢弃,正是因为Lua脚本适合这个目的。在此处查看讨论

这可以通过单行EVAL命令中的 Lua 脚本来完成:

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 保证脚本以原子方式执行:执行脚本时不会执行其他脚本或 Redis 命令" EVAL 命令文档

Lua 脚本的性能优于使用检查和设置进行乐观锁定。如果有竞争条件,最好进行检查并设置原子操作,以减少输掉比赛的可能性。

最好使用

Lua 脚本,但如果您坚持不使用它,则可以考虑在更新资源时使用 Redlock 来锁定资源。 在锁定期间,任何更新值的尝试都将失败,因为更新过程也使用 Redlock

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

红锁示例

// 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);
    });
});

最新更新