本文介绍如何在mybatis中设置事务的隔离级别,来达到合理控制数据库中锁,达到并发效果的。
一,事务隔离级别回顾
在数据库操作中,为了有效保证并发读取数据的正确性,提出了事务的隔离级别。实质其实是构建这些隔离级别而使用一些数据库锁。
在SQL标准中,总共有四种隔离级别:
oracle等多数数据库使用的默认隔离级别是已提交读RC级别,而我们经常使用的mysql使用的是可重复读RR隔离级别,
未提交读通常不加锁,所以会有各种并发问题;在RC级别中,数据的读取都是不加锁的,但是数据的写入、修改和删除是需要加锁的,由于读不加锁,不能保证本身事务读到其他事务中修改的数据并提交之后的结果,这就导致了不可重复读和幻读。
很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。
如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。
所以解决以上问题,mysql的innodb引擎引入了mvcc机制。
二,用在解决innoDB并发问题的mvcc机制
mvcc机制是运用乐观锁,即通过版本号记录的方式实现,每一个事务在启动的时候,都有一个唯一的递增的版本号。每开启一个新事务,事务的版本号就会递增。
首先mvcc机制将读划分成两种形式,对于这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库当前版本数据的方式,叫当前读 (current read)。mvcc通过每次操作需要比对版本号实现,这样可以解决快照读的并发问题,和悲观锁的优势是可以提高并发度,劣势是可能读到的数据是之前的旧数据,并发量提高之后可能会达不到锁住的效果。
mysql在RR级别就运用mvcc去解决快照读的问题,而且也通过next-key锁解决了幻读问题
三,next-key锁
Next-Key锁是行锁和GAP(间隙锁)的合并,熟悉B+树的朋友已经知道,B+树只有叶子结点记录行数据,且兄弟节点之间有索引,十分适合做存储引擎的底层数据结构,行锁则是锁住数据行,这种加锁方法比较复杂,但是由于只锁住有限的数据,对于其它数据不加限制,所以并发能力强,MySQL一般都是用行锁来处理并发事务。
next-key锁不仅用行锁,锁住了相应的数据行;同时也在两边的区间,都加入了gap锁。这样事务B就无法在这个两个区间insert进新数据。行锁防止别的事务修改或删除,GAP锁防止别的事务新增,行锁和GAP锁结合形成的的Next-Key锁共同解决了RR级别在写数据时的幻读问题。
四,mybatis操作事务隔离级别
mybatis是一种持久层框架,通过sqlSessionFactory设置连接session的属性,在这里可以修改事务隔离级别
然后通过接口实现设置的隔离级别修改session的隔离级别,一般是jdbc实现set session transaction isolation level read committed; 语句改变隔离级别,作用域只在本session