我在使用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));
所以我遇到了两个独立的问题:
这意味着未执行代码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版本开始,我知道实现有所更改。