Spring Boot 3.2.4 JDBC分布式锁失效问题排查求助
问题分析与修复方案
1. 锁Key生成错误(最可能导致锁失效的核心原因)
你的getKey方法存在两处致命问题:
- 变量拼写错误:方法参数是
request,但代码中使用了未定义的req,这会导致编译错误或生成完全错误的锁Key值。 - 业务维度错误:你用
request.getNumber()生成锁Key,但从日志逻辑来看,需要防止重复创建的是同一orderId的订单。如果request.getNumber()不是订单的唯一标识(比如是请求流水号或未生成的订单号),不同请求会生成不同的锁Key,锁机制完全起不到作用。
修复代码:
修正变量拼写,并基于订单ID生成锁Key:
private static String getKey(AbstractOrderRequest request) { return "ORDER_ID_%s".formatted(request.getOrderId()); }
2. tryLock参数概念误用
你把锁存活时间(lockTimeToLive)传给了tryLock的等待超时参数,完全混淆了两个概念:
tryLock(long timeout, TimeUnit unit)的作用是:当前线程最多等待timeout时间尝试获取锁,超时则返回false。- 锁的存活时间是
JdbcLockRegistry的配置参数,用于防止持有锁的实例宕机后锁永久占用。
修复配置:
单独配置等待超时时间,同时在JdbcLockRegistry中指定锁存活时间:
@Bean public LockRegistry lockRegistry(DataSource dataSource) { // 锁存活时间设为5秒(5000毫秒),需大于请求实际执行时长 return new JdbcLockRegistry(dataSource, "INT_LOCK", 5000); }
3. 错误的锁释放逻辑
无论是否成功获取锁,你都在finally块中调用unlock(),这会导致:
- 未拿到锁的线程执行无效释放操作,可能干扰锁的状态管理。
- 日志出现误导性的"Unlocking lock"信息,增加排查难度。
修复代码:
仅在成功获取锁后才执行释放操作:
@Override public T lockAndExecute(String key, Callable<T> runnable) throws Exception { log.info("Trying to obtain lock for key: {}", key); Lock lock = lockRegistry.obtain(key); boolean isLocked = false; try { // 这里的参数是等待锁的超时时间,可根据业务需求单独配置 isLocked = lock.tryLock(3, TimeUnit.SECONDS); if (isLocked) { log.info("Obtained lock for key: {}", key); return runnable.call(); } } catch (Exception e) { log.error("Exception occurred while trying to get lock and execute", e); throw e; } finally { if (isLocked) { log.info("Unlocking lock for key: {}", key); lock.unlock(); } } return null; }
4. 数据库锁表配置验证
确保两个服务实例使用同一个Postgres数据库,且INT_LOCK表已正确创建(Postgres SQL语句):
CREATE TABLE INT_LOCK ( LOCK_KEY VARCHAR(255) NOT NULL PRIMARY KEY, REGION VARCHAR(255) NOT NULL, CLIENT_ID VARCHAR(255) NOT NULL, CREATED_DATE TIMESTAMP NOT NULL );
若两个实例配置的锁表名称不同,分布式锁机制会完全失效。
内容的提问来源于stack exchange,提问作者Chitransh Agarwal
相关产品推荐
相关产品推荐

