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

从较高的 Angular 来看,该操作如下所示:

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只是对实际客户端操作的委托(delegate)。关于执行
该方法的内容如下:
JedisConverters.toBoolean(jedis.setnx(key, value));
所以我遇到了两个独立的问题:
  • 我的executeOperation()由两个单独的进程同时执行。 (仅发生此问题的少数)。
  • 我遇到的情况是密钥保留并且没有过期。
    这意味着未执行代码processKeyExpiration(element,connection)。这意味着作为键执行的setNx并未在该语句之前添加和返回,但实际上已添加了键。

  • 大多数情况下,一切正常。
    密钥序列化器是StringRedisSerializer

    我在用:
    spring-data-redis 1.8.23。发布
    杰迪斯2.9.3

    是否存在一些无法正确处理的环境问题
    是Jedis还是类似的东西?有没有人达到这样的目标?有没有可以尝试的修复程序,库更新?

    最佳答案

    因此,我对此进行了更多分析。而且,由于在多个进程/线程中使用putIfAbsent是如何实现的,因此似乎很容易出现竞争条件。

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

    可以在Redis setNX command中找到有关如何基于Redis setNX操作进行分布式锁定的类似说明。

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

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

    08-28 22:08