mysql事物的隔离级别包括四种
READ_UNCOMMITTED spring实现读未提交 (脏读) 脏读
READ_COMMITTED spring实现读已提交 (待解决--不可重复读+幻读) 不可重复读
REPEATABLE_READ spring实现可重复读 (待解决--幻读) 可重复读
SERIALIZABLE spring实现串行化(已解决) 串行化
mysql默认为不可重复读,oracle默认为可重复读
脏读和串行化使我们最容易理解的.假设现在有A、B两个事务在运行。
脏读:在A事务中sql读取到了B中还没有进行事务提交的数据。(如果B回滚,那么A就读到了错误的数据)
不可重复读(你在一个事务中不能重复的读取数据,否则结果出现问题):在A事务中不会读取到B事务中还没有提交的数据,避免了脏读。但是在B事务完成提交后,A事务还没有完成之前,A又去读了数据,那么A读取的在B提交前后的数据是不一致的。
可重复读(你在事务中可以重复的读取数据):A事务在B事务提交前后读取的数据一致。也就是说如果A事务在B事务提交前读取了一次数据,那么即使B已经更改了数据库,但是A读取到的,还是B事务提交之前的数据,这种会导致幻读。
串行化:事务串行化,数据与数据库一致。
思考:既然我们说 可重复读 解决了不可重复读的问题,也就是说我们在一个事务中读取到的数据都是一致的,不会因为别的事务提交的数据导致变化,那是不是就说明我们的事务将该行锁住了,从而避免了其他行对该行的操作呢?
其实不是的,经测试可以看到,其实数据已经更改了,但是我们读取的数据依然是以前的数据,这是为什么呢,这时因为mysql采用了MVCC(多版本并发控制)方式。而这种方式读取的数据会读取快照(历史版本),而不会读取最新版本,但是insert update delete会对当前版本进行操作。
举个简单的例子,如果开启可重复读,你在A事务里两次读取一个数据假设为400,那么在两次读取之间事务B更改了数据减去了50此时数据库为350,对A的读取结果为400没有影响,但是如果你在事务A中再让数据减去50那么你再在事务A中查询数据,数据就变成了300.因为update语句会更新数据。
最后来说说 可重复读 时mysql采用MVCC的实现机制。
其实挺简单的。这里值分析一下select的实现,具体的可以看下面的博文
其实在每行数据有面还有两个列隐藏的行,一行是我们事务的版本号,一行是删除的标志
那么我们上面说了,每次做增删改的时候,这一行的事务版本号就会更新。
而MVCC告诉我们我们查询出的数据,都必须满足两个条件,
a.InnoDB只会查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的.
b.行的删除版本要么未定义,要么大于当前事务版本号,这可以确保事务读取到的行,在事务开始之前未被删除.
只有a,b同时满足的记录,才能返回作为查询结果.
那么每次连接数据库的时候开启事务,他都会给你一个最新的版本号,假设A事务是读取,那么A事务在打开读取了一次所有数据,那么B事务开动增加一条数据,并提交,这时这条数据的事务版本号是高于A事务的事务版本好的,所以A再查询也是查询不到B事务提交的版本号的。
参考:
https://blog.csdn.net/whoamiyang/article/details/51901888
https://www.cnblogs.com/huanongying/p/7021555.html