天天看点

记一次开发BUG Sharding读写分离事务下使用select for update导致死锁

死锁场景

select for update 为悲观锁,只有当前会话才能过去到锁,其他事务访问加锁数据会被阻塞。

以下代码无实际逻辑,只做演示…

代码如下:

@Transactional
public void test() {
	userService.selectByIdForUpdate(1L);
    userService.increaseAmountById(1L, 10);
}
           

使用Sharding JDBC读写分离时,导致死锁。

死锁原因

使用Sharding JDBC后,当前事务第一个写之前的所有读操作都会优先读取从库,而事务内第一个写以后的读写操作都只会操作主库。

根本原因:查询和更新不是同一个会话,导致死锁。即便是select for update,也会操作从库,导致读取和修改不在同一个会话。但是由于是开发环境,主从为同一个数据库,读方法加锁后,写方法获取锁失败,事务不提交,读不释放锁,写获取不到锁,导致死锁。

解决方法

  1. 在需要指定主库查询前,加一行代码,指定只能从主库读取数据:

    HintManager.getInstance().setMasterRouteOnly();

  2. 在查询之前执行一次主库更新,无业务需求不建议使用。

代码如下

@Transactional
public void test() {
    HintManager.getInstance().setMasterRouteOnly();
    userService.selectByIdForUpdate(1L);
    userService.increaseAmountById(1L, 10);
}