spring事务管理与查询是否需要事务以及可重复读的问题

原创 2018-04-04 11:12 阅读(2584)次

我们先来看下通常的spring配置:

	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="remove*" propagation="REQUIRED" />
			<tx:method name="newTx*" propagation="REQUIRES_NEW" /> 
			<tx:method name="get*"  read-only="true"/>
			<tx:method name="find*" read-only="true" />
			<tx:method name="*" read-only="true"/>
		</tx:attributes>
	</tx:advice>

上面的配置中增删改中用propagation="REQUIRED"和"REQUIRES_NEW",都是需要开启写事务的,应该每个人都知道,但查询是否需要开启事务呢?反正我在这之前,真的以为不用,也没必须开启。但今天研究了下mycat的读写分离,发现写事务里的读都走写库,这种做法是为了保证查询数据的一致性,也就是保证数据库的可重复读事务隔离性。


我们再看看上面的读配置:

<tx:method name="get*"  read-only="true"/>
其中tx里的默认传播性是propagation="REQUIRED" read-only默认为"false",默认可写的意思,也就是说上面的配置是有事务的,只是告诉数据库此事务没有需要更新的语句,事务只读,用spring这么久了,我才发现虽然配置了read-only="true",但上面的事务配置查询也是有事务的!

所以说查询也是需要开启事务的,以MySQL为例,这是为了这一组事务里的所有查询在Repeatable Read隔离级别下能做到一致性,也就是常说的可重复读。


举个例子,如果上面的读配置成无事务,如下:

<tx:method name="get*"  propagation="NEVER"/>
那么就可能在一组查询中出现数据不致的问题,如:

public void getUserList(){

	select count(*) from user_info
	
	select * from user_info limit 0,10
}


如上我们service里有这么一个方法(事务切面定义的是service里的方法),用来分页查询的,当第一条查询出来count为6条数据时,正好有人删除了其中一条数据,因为getUserList()方法定义为无事务,下面这条语句查询用户数据查询出来就只有5条,造成了前后数据不一致,这违背了Repeatable Read隔离级别。我们也不希望用户看到这种情况。所以说为了保证可重复读,查询也是需要事务的,只不过为了优化数据查询,将read-only设为"true"。

另外,如果你的方法里或者说此次查询操作只有一个查询语句,那就没必要开启只读事务了,因为只有一个语句,没有一致性的问题,数据库默认可重复读的事务级别,也就是说spring里的事务配置,可以给这种方法配置成无事务,这样做会让你的查询性能提升很多,当然如果你的程序的查询密集型,并且不在意查询一致性的问题的话,一样可以设置成查询无事务,因为这么做相对于有只读事务的查询来说,性能可以提升3-4倍。