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

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

我在使用 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只是对实际客户端操作的委托(delegate)。关于实现 该方法有类似:

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

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

  1. 我的executeOperation() 由两个单独的进程同时执行。 (此问题仅出现几次)。

  2. 我遇到的情况是 key 仍然存在且未过期。 这意味着代码 processKeyExpiration(element, connection) 未执行。这意味着 setNx 是在该语句之前执行的,因为 key 尚未添加并返回,但实际上 key 已添加。

大多数时候一切正常。 关键的序列化器是StringRedisSerializer

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

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

最佳答案

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

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

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

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

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

关于java - Spring-Data-Redis 与 Jedis putIfAbsent 用于分布式锁 - 不正确的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58609065/

相关文章:

java - Java中使用TCP连接向多个客户端广播

java - Java 中的 "implements Runnable"与 "extends Thread"

java - SpringBoot 1.2.1 MongoRepository

java - Tomcat 可能的内存泄漏

java - Android 4.4.4,创建大量字节数组时,垃圾收集器如何处理?

java - 包含 Spring Boot/Thymeleaf 应用程序的模板

java - 无法在拦截器中正确读取请求正文 - Spring BOOT 2.0.4

java - Redis中成员的过期时间

replication - 添加 redis slave 是否会向 master 发出阻塞调用?

python - Spark 流作业性能改进