代码之家  ›  专栏  ›  技术社区  ›  talex

compareAndSet在redis中的内部工作原理

  •  2
  • talex  · 技术社区  · 6 年前

    spring-data-redis RedisAtomicLong

    在这节课上你可以看到

    public boolean compareAndSet(long expect, long update) {
    
        return generalOps.execute(new SessionCallback<Boolean>() {
    
            @Override
            @SuppressWarnings("unchecked")
            public Boolean execute(RedisOperations operations) {
                for (;;) {
                    operations.watch(Collections.singleton(key));
                    if (expect == get()) {
                        generalOps.multi();
                        set(update);
                        if (operations.exec() != null) {
                            return true;
                        }
                    }
                    {
                        return false;
                    }
                }
            }
        });
    }
    

    我的问题是它为什么有效?

    generalOps.multi() get() 被调用。这意味着两个不同的线程(甚至客户端)有可能改变值,并且它们都会成功。

    operations.watch

    for (;;) ? 总有一次迭代。

    2 回复  |  直到 6 年前
        1
  •  3
  •   sazzad    6 年前

    问:运营观察是否以某种方式阻止了它?

    引用 Redis documentation about transaction

    WATCH用于为Redis事务提供检查和设置(CAS)行为。

    监视监视的键,以便检测针对它们的更改。如果在EXEC命令之前修改了至少一个关注的密钥,则整个事务将中止,EXEC将返回一个Null回复,通知事务失败。

    问:为什么是(;)?总有一次迭代。

    你发布的代码似乎很旧。从谷歌的 this url ,我看到了您提供的代码,它可以追溯到 Oct 15th, 2012 !

    最新的代码看起来大不相同:

        2
  •  2
  •   for_stack    6 年前

    对查看密钥后,如果该密钥在事务完成之前已被修改, EXEC 将失败。所以如果

    为什么是(;)?总有一次迭代。

    在您的例子中,无限循环似乎是多余的。

    但是,如果要实现检查和设置操作以使用旧值修改值,则需要无限循环。请从中检查此示例 redis doc

    WATCH mykey
    val = GET mykey
    val = val + 1
    MULTI
    SET mykey $val
    EXEC
    

    执行官 可能会失败,您需要在循环中重试整个过程,直到成功为止。

        3
  •  1
  •   Nikita Koksharov    5 年前

    RedisAtomicLong.compareAndSet 5对Redis的请求

    Redisson -RedisJava客户端提供了更高效的实现。

    org.redisson.RedissonAtomicLong#compareAndSetAsync 使用原子评估脚本实现的方法:

      "local currValue = redis.call('get', KEYS[1]); "
    + "if currValue == ARGV[1] "
           + "or (tonumber(ARGV[1]) == 0 and currValue == false) then "
          + "redis.call('set', KEYS[1], ARGV[2]); "
          + "return 1 "
    + "else "
          + "return 0 "
    + "end",
    

    对Redis的单一请求 .

    RAtomicLong atomicLong = redisson.getAtomicLong("myAtomicLong");
    atomicLong.compareAndSet(1L, 2L);