一.先了解ACID
原子性(A):一个事务执行过程中,所有步骤要么全部成功,或者全部回滚失败,不能执行其中一部分;
一致性(C):从一种状态,执行该事务后,转变为另外一种正确状态 ,经典例子:A向B转账,俩者总和不变;
隔离性( I):为了有效保证并发读取数据的正确性,提出的事务隔离级别;
持久性(D):事务执行成功之后,数据不能更改。
二.事务隔离级别
在数据库操作中,为了有效保证,并发读取数据的正确性,提出事务隔离级别。数据库锁,也是为了构建这些隔离级别存在的。
隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
未提交读(Read uncommitted) | 可能 | 可能 | 可能 |
已提交读(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
1、Read Uncommitted(未提交读):一个事务已对数据修改,但没有提交之前,其他并行事务也可以读到,会导致“脏读”、“幻读”和“不可重复读取”。
2、READ COMMITTED (提交读):保证一个事务,不会读到其他并行事务,已修改,但未提交的数的数据。换句话说:只能读取到已经提交的数据。提交读,可能会出现“幻读”和“不可重复读取”。(Oracle、SqlServer采用默认隔离级别)
3、REPEATABLE READ(重复读):保证一个事务,不会修改已经由另一个事务读取但未提交(回滚)的数据。换句话说:
在同一个事务内的查询都是事务开始时刻一致的。重复读,可能会出致“幻读”。(Mysql采用默认隔离级别)
4、Serializable (串行化):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞;
三.脏读、不可重复读、幻读
(1)概念
脏读:所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。
不可重复读:事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
幻读:事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条数据,或增加了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读。
也就是说,事务A第一次读取到的数据,比后一次读取到数据条量少。
两者有些相似,但是前者针对的是update或delete,后者针对的insert。
(3)重复读和幻读原理分析
在可重复读中,该sql第一次读取到数据后,就将这些数据加锁(悲观锁),其它事务无法修改这些数据,就可以实现可重复读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。
但是MySQL、ORACLE、PostgreSQL等成熟的数据库,出于性能考虑,都是使用了以乐观锁为理论基础的MVCC(多版本并发控制)来实现。