文章目录
一、事务的隔离级别
1.1 Spring 事务隔离级别介绍
当两个事务对同一个数据库的记录进行操作时,那么,他们之间的影响是怎么样的呢? 这就出现了事务隔离级别的概念。
数据库的隔离性与并发控制有很大关系。数据库的隔离级别是数据库的事务特性ACID
的一部分。ACID,即原子性(atomicity)、一致性(consistency)、隔离性(isolation) 和 持久性(durability).
事务隔离级别分为: READ_UNCOMMITTED 读未提交, READ_COMMITTED 读提交, REPEATABLE_READ 可重复读, SERIALIZABLE序列化.
DEFAULT(不同的数据库不同的默认隔离级别,MySQL默认为repeatable_read).
1.2 隔离级别和事务缺陷
隔离级别/事务缺陷 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ_UNCOMMITTED | √ | √ | √ |
READ_COMMITTED | √ | √ | |
REPEATABLE_READ | √ | ||
SERIALIZABLE |
1、READ_UNCOMMITTED
事务A读取到事务B insert 而未提交的数据,B回滚,造成A事务脏读(dirty read) 等
2、READ-COMMITTIED
事务A读取数据,B事务修改数据,事务A再次读取数据,造成两次读取数据不一致,造成不可重复读(non-repeated read)
3、REPEATABLE_READ
事务A两次或多次读取一系列记录,期间事务B insert 一条记录,事务A多次读取的一系列记录不一致,造成幻读****(phantom read)
4、SERIALIZABLE
所有事务的read 和 write 都上锁,串行执行。但性能会下降。
二 击穿隔离级别
2.1 READ_UNCOMMITTED
顾名思义,READ_UNCOMMITTED
意思是,一个事务可以读取到另一个事务未提交的事务记录。换句话说,a transaction can read the data that is still uncommitted by other transactions。
这是Spring事务最弱的隔离级别。见下面的图,事务A开启,写入一条记录,这时候,事务B读入数据,读到了这条记录,但是,之后事务A回滚。因此,事务B读到的数据不是有效的(the database is in an invalid state)。这种情况称为脏读(dirty read)。除了脏读的问题,READ_UNCOMMITTED
还可能出现non-repeatable read(不可重复读)
和phantom read(幻读)
的问题。
2.2 READ_COMMITTED
READ_COMMITTED
隔离级别表明,一个事务只能读取到已经提交的记录,不能读取到未提交的记录。换句话说,a transaction can only read the committed data, and it can’t read the uncommitted data. 因此,dirty read的情况不再发生,但可能会出现其他问题。见下图。
在事务A两次读取的过程之间,事务B修改了那条记录
并进行提交。因此,事务A前后两次读取的记录不一致。这个问题称为non-repeatable read(不可重复读)。(两次读取的记录不一致,重复读取就会发现问题。) 除了non-repeatable read的问题,READ_COMMITTED
还可能发生phantom read的问题。
2.3 REPEATABLE_READ
REPEATABLE_READ
意思是,一个事务可以多次从数据库读取某条记录,而且多次读取的那条记录都是一致的,相同的。这个隔离级别可以避免dirty read和non-repeatable read的问题,但可能发生phantom read的问题。如下图:
事务A两次从数据库读取**一系列
记录,期间,事务B插入了某条记录
并提交。事务A第二次读取时,会读取到事务B刚刚插入的那条记录。在事务期间,事务A两次读取的一系列
记录不一致,这个问题称为phantom read**。
2.4 SERIALIZABLE
SERIALIZABLE
是Spring最强的隔离级别。事务执行时,会在所有级别上加锁,比如read和write时都会加锁,仿佛事务是以串行的方式进行的,而不是一起发生的。这会防止 dirty read、non-repeatable read 和 phantom read 的出现,但是,会带来性能的下降。
三 不可重复读和幻读的区别
行锁和表锁的区别
如果或者删除一条数据,加上行锁;就可避免其他事务的不可重复读.
但是如果增加一条数据,行锁对于新来的伙计(insert的数据)不起作用,这时表锁可以解决此类问题
这里可以衍生出对这两者的其他区别:
如: 不可重复读针对delete/update
操作, 幻读针对insert
操作
所以说不可重复读和幻读最大的区别,就在于如何通过锁机制来解决他们产生的问题。
这里说的,是使用悲观锁机制来处理这两种问题,但是MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)来避免这两种问题。
四 悲观锁和乐观锁
####悲观锁和乐观锁####
- 悲观锁
正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处 于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机 制,也无法保证外部系统不会修改数据)。
在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。
- 乐观锁
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )
记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如 果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
要说明的是,MVCC(Multi-Version Concurrency Control)
的实现没有固定的规范,每个数据库都会有不同的实现方式,这里讨论的是InnoDB的MVCC。MVCC 下面章节会讲到