Spring-Data-Redis 和用于分布式锁的 Jedis putIfAbsent - 不正确的行为

Spring-Data-Redis with Jedis putIfAbsent for distributed lock - incorrect behavior

我在使用 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

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

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

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

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

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

所以总而言之,将 putIfAbsent 与 Jedis 驱动程序一起用于分布式锁定可能不是最好的主意,至少在那个版本的 Spring 上,因为从 2.x.x 我知道实施有所改变。