Apache Ignite持续执行SELECT与UPDATE操作时出现死锁错误
问题背景
使用Apache Ignite 3.0.0版本,创建表结构如下:
CREATE TABLE INSTANT_CDR ( MSISDN VARCHAR PRIMARY KEY, RECHARGE_AMOUNT DOUBLE, VOICE_AMOUNT DOUBLE, LAST_NAME VARCHAR, BALANCE DOUBLE );
通过JDBC Java程序对该表执行以下操作:
- 查询语句:
SELECT * FROM INSTANT_CDR WHERE MSISDN='123456789'
- 更新语句:
UPDATE INSTANT_CDR SET RECHARGE_AMOUNT=RECHARGE_AMOUNT+1, VOICE_AMOUNT=VOICE_AMOUNT+1 WHERE MSISDN='123456789'
持续执行插入和更新操作时,Ignite端抛出如下错误:
Caused by: org.apache.ignite.internal.tx.LockException: IGN-TX-4 TraceId:b5f06ecc-1378-4315-96d9-e25d9ff70e6f Failed to acquire a lock due to a possible deadlock [locker=0195176a-f0ce-000b-e5f1-efe300000001, holder=0195176a-f0ce-000b-e5f1-efe300000001]
at org.apache.ignite.internal.tx.impl.HeapLockManager.lockException(HeapLockManager.java:424)
at org.apache.ignite.internal.tx.impl.HeapLockManager$LockState.isWaiterReadyToNotify(HeapLockManager.java:931)
at org.apache.ignite.internal.tx.impl.HeapLockManager$LockState.tryAcquire(HeapLockManager.java:851)
at org.apache.ignite.internal.tx.impl.HeapLockManager.acquire(HeapLockManager.java:216)
20 more
Ignite并发SELECT/UPDATE的处理机制
Ignite 3.0.0采用乐观锁+悲观锁结合的事务模型,默认规则如下:
- 事务内的SELECT语句会获取共享锁(S锁),允许其他事务读取同一行,但会阻止排他锁(X锁)的获取;
- UPDATE语句会尝试将共享锁升级为排他锁(X锁),或直接获取排他锁,此时会阻止其他事务的读写操作。
日志中出现同一事务ID既是锁请求者又是持有者的情况,并非真死锁,通常是事务上下文管理不当、锁超时配置过短导致的异常告警。
死锁问题解决与并发优化方案
1. 规范事务边界
- 确保SELECT与UPDATE在同一事务内执行,避免跨事务的锁竞争。分开的事务会导致SELECT持有的S锁与UPDATE的X锁相互阻塞;
- 减少事务内重复执行相同SELECT的次数,缩短锁持有时间。
2. 调整锁与事务超时配置
在Ignite配置文件(如ignite-config.yaml)中修改以下参数,避免因超时触发假死锁:
transaction: defaultTimeout: 60000 # 默认事务超时设为60秒 lock: waitTimeout: 10000 # 锁等待超时设为10秒
3. 优化SQL与锁粒度
- 直接使用原子UPDATE语句完成操作(当前更新语句已满足原子性),避免先SELECT再UPDATE的冗余流程,减少锁持有时间;
- 若必须执行前置查询,使用
SELECT ... FOR UPDATE显式获取排他锁,避免后续锁升级冲突:
SELECT * FROM INSTANT_CDR WHERE MSISDN='123456789' FOR UPDATE
4. 启用乐观事务模式
乐观事务通过版本号而非锁控制并发,适合高并发读写场景。在JDBC代码中启用:
Connection conn = DriverManager.getConnection(jdbcUrl); conn.setAutoCommit(false); // 乐观事务依赖读已提交隔离级别 conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 执行查询与更新操作 conn.commit();
5. 调整锁管理器配置
启用公平锁避免线程饥饿,优化高并发场景下的锁分配:
lock: fairLock: true
内容的提问来源于stack exchange,提问作者Arjun

