【问题标题】:Spring-Data-Redis with Jedis putIfAbsent for distributed lock - incorrect behavior带有 Jedis putIfAbsent 的 Spring-Data-Redis 用于分布式锁 - 不正确的行为
【发布时间】:2019-10-29 14:06:34
【问题描述】:

我在使用 Spring Data Redis 创建分布式锁时遇到了一些问题。为此,它使用了 CacheManager 中的 putIfAbsent 方法。

从高层次的角度来看,操作看起来像这样:

if (manager.putIfAbsent(parameters) == null) {
   executeOperation();
}

从 putIfAbsent 的实现看来,似乎使用了来自底层 Jedis 驱动程序的 setNX 操作。

Spring 实现的代码类似于:

if (!connection.setNX(keyBytes, value)) {
   return connection.get(keyBytes);
}
maintainKnownKeys(element, connection);
processKeyExpiration(element, connection);

连接的 setNX 只是对实际客户端操作的委托。关于实施 该方法有类似的东西:

JedisConverters.toBoolean(jedis.setnx(key, value));

所以我遇到了两个不同的问题:

  1. 我的 executeOperation() 由两个单独的进程同时执行。 (这个问题只发生过几次)。

  2. 我遇到了一种情况,即密钥仍然存在且未过期。 这意味着代码 processKeyExpiration(element, connection) 没有被执行。这意味着在该语句之前没有添加并返回作为键执行的 setNx,但实际上添加了键。

大多数时候一切正常。 密钥序列化程序是StringRedisSerializer

我正在使用: spring-data-redis 1.8.23.RELEASE 绝地武士2.9.3

会不会有一些环境问题没有得到正确处理 由 Jedis 或类似的东西?有没有人达到这样的程度?是否有任何可以尝试的修复程序,库更新?

【问题讨论】:

    标签: java spring-boot redis jedis spring-data-redis


    【解决方案1】:

    所以我对此做了更多分析。而且,由于 putIfAbsent 的实现方式,如果在多个进程/线程中使用,似乎很容易出现竞争条件。

    这个事实是因为 putIfAbsent 实现、setNX、expire、get 的一系列命令不是事务性的,并且由于适当的条件(持久的操作,不适当的驱逐逻辑),它很容易出现不正确的行为。

    类似的解释,关于如何基于 Redis setNX 操作进行分布式锁定,可以在这里找到Redis setNX command

    putIfAbsent 的实现在用于锁定时容易出现错误行为,与上述文档中描述的方式类似。

    总而言之,使用 putIfAbsent 和 Jedis 驱动程序进行分布式锁定可能不是最好的主意,至少在那个版本的 Spring 上是这样,因为我知道从 2.x.x 开始,实现有所改变。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-11
      • 2017-01-05
      • 2014-09-27
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多