Mysql事务结合spring管理

spring事务相关问题记录

遇到情况:

在本地单体应用调试代码时,发现在一个加了@transaction注解的方法里进行先更新后查询的操作,查询的结果是可以看到更新的内容的。而在微服务环境中同样的代码却在后查询时查不到前面更新的内容。

伪代码如下:

@Transactional
	public void call() {
		bidPlanMapper.updateByPrimaryKey(plan);
		List<BidPlan> all = systemService.findBidPlan();
		System.out.println(all);//单体应用时可观察到更改的结果
	}

猜测本地可以而微服务上不可以,估计是因为微服务在执行call()方法时,update操作是由biz服务调用的,而findBidPlan()操作是实际是调用system服务进行的(该方法也有@Transactional),故产生的是两个事务。

处理办法:
  • update操作后立刻提交事务,则另外一个事务可以查询到处理的结果。
@Autowired
private DataSourceTransactionManager transactionManager;

public void call() {
    //开启新事务
	DefaultTransactionDefinition transDefinition = new DefaultTransactionDefinition();
	transDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
	TransactionStatus transStatus = transactionManager.getTransaction(transDefinition);
    try {
        bidPlanMapper.updateByPrimaryKey(plan);//先提交更新的操作
        transactionManager.commit(transStatus);
    } catch (Exception e) {
        transactionManager.rollback(transStatus);
    }
}

扩展阅读:

隔离级别:

隔离级别定义一个事务可能受其他并发事务活动活动影响的程度。另一种考虑一个事务的隔离级别的方式,是把它想象为那个事务对于事物处理数据的自私程度。

在一个典型的应用程序中,多个事务同时运行,经常会为了完成他们的工作而操作同一个数据。并发虽然是必需的,但是会导致以下问题

  • 脏读(Dirty read)-- 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。

  • 不可重复读(Nonrepeatable read)-- 不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。(即不可重复读到同值)

  • 幻影读(Phantom reads)-- 幻影读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻影读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。

隔离级别 含义
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别。
ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。
ISOLATION_READ_COMMITTED 允许从已经提交的并发事务读取。可防止脏读,但幻影读和不可重复读仍可能会发生。
ISOLATION_REPEATABLE_READ(MySQL默认隔离级别) 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻影读仍可能发生。
ISOLATION_SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。

1.查看当前会话隔离级别

select @@tx_isolation;

2.查看系统当前隔离级别

select @@gobal.tx_isolation;
模拟read committed隔离级别下出现不可重复读的状况:

开启一个事务A进行三次查询。事务B在事务A第一和第二次查询中进行数据更新,事务B在事务A第二次和第三次查询中进行事务提交。

  • 事务A
mysql> SET SESSION TRANSACTION ISOLATION LEVEL read committed; #设置当前会话事务的隔离级别为read committed

mysql> select @@tx_isolation; #查询当前会话事务的隔离级别
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

mysql> start transaction; #开启事务A
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t_user; #事务A第一次查询 (1)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 张三 | 123      | 000   |
+----+------+----------+-------+

###################### 事务B进行更新操作 ##############################

mysql> select * from t_user; #事务A第二次查询,没有看到事务B没有提交的更新操作。说明已经防止了脏读问题。(3)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 张三 | 123      | 000   |
+----+------+----------+-------+

###################### 事务B进行事务提交 ##############################

mysql> select * from t_user; #观察到事务B提交的结果,说明无法在同个事务中多次查询值都是一样的,即无法避免不可重复读问题。(5)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 张三 | 123      | 123   |
+----+------+----------+-------+

  • 事务B
mysql> start transaction; #开启事务B 

mysql> update t_user set `phone` = '123' where id = 1; #进行更新操作(2)

mysql> commit; #提交事务B (4)

理解REPEATABLE READ 隔离级别下什么是可重复读:

开启两个事务。

  • 事务A
mysql> begin;

mysql> select * from t_user; #(1)
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 张三 | 123      | 188   |
+----+------+----------+-------+

###################### 事务B进行事务提交 ##############################


mysql> select * from t_user; #可观察到两次查询结果都一样,不会因为事务B的更新提交而有影响(3) 
+----+------+----------+-------+
| id | name | password | phone |
+----+------+----------+-------+
|  1 | 张三 | 123      | 188   |
+----+------+----------+-------+

  • 事务B
mysql> begin;

mysql> update `t_user` set `phone` = '8888'; #(2)

mysql> commit; #(2)

虽然该隔离级别下是,无论事务B进行更新、新增、删除,在事务A中仍然可以重复读到相同值的。

但是如果事务A进行更新操作,则可以对事务B新增的数据进行直接更新。

可参考如下例子:

REPEATABLE READ 隔离级别下,MySQL在session1执行UPDATE语句的时候对于session2的INSERT语句是可以看到的,也就是说发生了幻读。
相关阅读:MySQL在REPEATABLE READ 隔离级别下的工作方式

posted @ 2018-09-11 23:13  21Java  阅读(1101)  评论(0编辑  收藏  举报