四大特性ACID
事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability)。这四个特性简称为 ACID 特性。
原子性
事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做。
一致性
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
隔离性
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
持续性
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
四种隔离级别
Read Uncommitted(读未提交)
在该隔离级别,一个事务可以看到其它事务未提交的数据,称之为脏读(Dirty Read)。
Read Committed(读提交)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。
在该隔离级别,一个事务只能看见其它事务已经提交的数据。在其它事务的提交前后,当前事务的两次select返回了不同结果,即所谓的不可重复读(Nonrepeatable Read)。
Repeatable Read(可重复读)
这是MySQL的默认事务隔离级别。
在该隔离级别,其它事务对数据的修改提交并不影响当前事务的查询结果。即当前事务在其它事务的提交前后,看到的数据是一致的。但是可能出现另一个新的问题,即幻读,简单的说,就是当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | √ | √ | √ |
读提交(read-committed) | × | √ | √ |
可重复读(repeatable-read) | × | × | √ |
串行化(serializable) | × | × | × |
举例说明
测试数据库为demo,表为test,表结构:
两个命令行客户端分别为A,B;不断改变A的隔离级别,在B端修改或新增数据。
读未提交(read-uncommitted)
将A的事务隔离级别设置为read uncommitted(读未提交)
A:启动事务,此时数据为初始状态
B:启动事务,更新数据,但不提交
A:再次读取数据,发现数据已经被修改了,这就是所谓的“脏读”
B:回滚事务
A:再次读数据,发现数据变回初始状态
总结:事务B更新了一条记录,但是没有提交,此时事务A可以查询出未提交记录,即“脏读”。
读提交(read-committed)
将A的事务隔离级别设置为read committed(读提交)
A:启动事务,此时数据为初始状态
B:启动事务,更新数据,但不提交
A:再次读数据,发现数据未被修改
B:提交事务
A:再次读取数据,发现数据已发生变化,说明B提交的修改被事务中的A读到了
总结:事务A在事务B提交前后两次查询的数据不一致。读提交只允许读取已提交的记录。读已提交解决了脏读的问题,但是出现了不可重复读的问题。
可重复读(repeatable-read)
将A的事务隔离级别设置为repeatable read(可重复读)
A:启动事务,此时数据为初始状态
B:启动事务,更新数据,但不提交
A:再次读取数据,发现数据未被修改
B:提交事务
A:再次读取数据,发现数据依然未发生变化,这说明这次可以重复读了
B:插入一条新的数据,并提交
A:再次读取数据,这里分两种情况
第1种情况,当前数据库存储引擎为InnoDB || Falcon,结果无变化,说明无“幻读”
第2中情况,当前数据库存储引擎为非InnoDB && 非Falcon,结果如下,说明出现了“幻读”
注意:InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion
Concurrency Control)机制解决了“幻读”问题。
总结:幻读出现在数据新增上面,而且要求必须是非InnoDB && 非Falcon这类没有实现MVCC的存储引擎。
串行化(Serializable)
将A的隔离级别设置为可串行化(Serializable)
A:启动事务,此时数据为初始状态
B:发现B此时进入了等待状态,原因是因为A的事务尚未提交,只能等待(此时,B可能会发生等待超时)
A:提交事务
B:发现插入成功
总结:串行化serializable隔离级别是完全锁定,另一个事务必须等待当前事务完成并解除锁定,即 “写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。因而会有效率的问题。
补充
多版本并发控制(MVCC,Multiversion Concurrency Control)机制
step 1:打开一个客户端A,并设置当前事务模式为repeatable read,查询表account的所有记录
step 2:在客户端A的事务提交之前,打开另一个客户端B,更新表account并提交
step 3:在客户端A查询表account的所有记录,与step 1查询结果一致,没有出现不可重复读的问题
step 4:在客户端A,接着执行update balance = balance - 50 where id = 1,balance没有变成400-50=350,lilei的balance值用的是步骤step 2中的350来算的,所以是300,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。
step 5:重新打开客户端B,插入一条新数据后提交
step 6:在客户端A查询表account的所有记录,没有 查出 新增数据,所以没有出现幻读
其它
1、事务隔离级别为读提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
5、MYSQL MVCC实现机制参考链接:https://blog.csdn.net/whoamiyang/article/details/51901888
6、关于next-key 锁可以参考链接:https://blog.csdn.net/bigtree_3721/article/details/73731377