这篇文章涉及到数据库锁的概念,我的一篇关于数据库锁的博客:https://www.cnblogs.com/ouhaitao/p/8039537.html.
这里的讨论是以innodb引擎作为基础,一个事务在执行前会去获取自己需要的所有锁,事务提交或者回滚后释放锁.
事务隔离级别有以下五种:
1. TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
这个是最低的一种隔离级别,没有任何防范措施.
2. TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
脏读产生的原因是一个事务A对一行数据进行了修改,随后回滚了.另一个事务B在事务A回滚之前读到了被修改的那一行数据,这样在事务A回滚之后,事务B的数据就是脏数据.该隔离级别下修改数据行时会加上写锁(排它锁),当事务读取被加锁的数据行时,会从undo log中获取最近的数据,这样事务B就不能读取到事务A未提交的数据,从而防止了脏读,但是会产生不可重复读和幻读.
3. TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
不可重复读产生的原因是事务A在第一次读取一行数据之后,另一个事务B对该行数据进行了修改并且提交,然后事务A再一次读取该行数据,会发现两次读取数据不一致.在该隔离级别下,事务读取数据时会加上读锁(共享锁),共享锁与排它锁不能共存,所以这样就能够保证自己在读取事务时,数据不会被其他事务修改,从而防止了不可重复读,但是会产生幻读.
4. TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
幻读产生的原因是事务A在对多行数据进行操作,例如将所有满足条件A的数据全部修改为X这样的批量操作时,事务B插入了一条满足条件A的但是数据值不为X的值,这样事务A再次查询时,就会发现有一行没有被修,产生了执行失败的幻觉.以上说的隔离级别都是对要操作的数据行进行加锁实现的,而幻读是进行insert操作产生的,insert是插入了一条新数据行,锁是无法影响到insert操作的,所以该隔离级别通过让事务串行执行,从而达到防止幻读的功能.
以上都是基于悲观锁,有他的实现原理就能够看出来,他们的并发性不是很好,所以大多成熟的数据库,例如mysql,oracle等都会采用一种基于乐观锁的并发控制-----MVCC(多版本并发控制).
MVCC具体原理以后理解深刻了在写,这里先说一下我现在对MVCC的理解:我们不考虑隔离级别,单纯的考虑MVCC的话,他不能防止脏读,不可重复读,幻读,他的作用是为了提高事务的并发度,由上面的各个隔离级别可以看出,隔离级别越高,并发度越低,MVCC就是在降低隔离级别的同时提高安全性.举一个例子:我们使用REPEATABLE_READ隔离级别时会出现幻读,但是我们的系统要杜绝幻读,可是我们不想使用SERIALIZABLE隔离级别,因为并发度太低了,这个时候MVCC的作用就体现出来了,虽然他不能杜绝幻读,但是如果使用了MVCC的话,那么在REPEATABLE_READ隔离级别加上MVCC机制下,可能出现幻读的情况我只能想到一种:
当前系统版本号0,数据版本号0->事务A开启版本号1,系统版本号1->事务B开启版本号2,系统版本号2->事务B执行update->事务A执行insert,数据版本号2->事务B执行select,查询版本号<=2,幻读发生
如果想要完全杜绝幻读,那么我们可以给程序增加保险机制,例如:里面使用一个循环操作,每次update之后再查询数据库是否还有为更改的数据,这样的操作肯定会很费时间.但是我们使用了MVCC之后,发生幻读的概率下降,那么进入保险机制的概率就被减小,所以提高了并发度,同时解决了幻读问题.