MySQL事务隔离级别
事务以及事务的隔离级别
1. 概念
事务就是用户定义的一系列操作,这些操作可以视为一个完整的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。
数据库事务: 通常指对数据库进行读或写的一个操作序列(如,一条sql语句就是一个事务),这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;是数据库操作的最小工作单元。
2. 事务的特性
我们都知道事务四大基本特性,那就是ACID
-
原子性(Atomicity):事务开始后所有操作,要么都做,要么都不做
-
一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。
如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态;
比如A向B转账,不可能A扣了钱,B却没收到。
-
隔离性(Isolation):一个事务的执行不能其它事务干扰;
同一时间,只允许一个事务请求同一数据**,不同的事务之间彼此没有任何干扰**。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
-
持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的
3. 事务的隔离级别
3.1 Q:那么为什么事务需要有隔离级别呢?
A:事物隔离级别,是为了解决事务并发产生的问题;事物隔离级别越高,并发下产生的问题就越少,但付出的资源消耗也越大,所以我们要根据实际使用在并发性和性能之间做一个权衡,因此有了事物隔离级别。
3.2 Q:那么什么事务并发会产生的问题?
事务的并发执行可能会产生三种常见的问题:脏读、不可重复读、幻读
-
脏读: 事务A读取了事务B更新的数据(但此时B还未回滚【提交数据】),那么A读取到的数据是脏数据;
eg:事务A先执行,查到age=10;接着事务B执行,将age改成11,事务B未提交;接着继续事务A进行查询,得到age=11;但此时事务B还未提交,所以这就是读未提交,也就是我们所说的脏读;
-
不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
-
幻读: 系统管理员A学生的成绩按照等级分类,但是系统管理员B在这个时候插入了一条成绩记录,当系统管理员A修改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
总结:不可重复读的和幻读很容易混淆
不可重复读侧重于
修改
;
幻读侧重于新增或删除
;解决不可重复读的问题只需锁住满足条件的行( 行级锁);
解决幻读需要锁表(间隙锁防止幻读)
3.3 Q:事务的隔离级别分为哪几种?
MySql种事务的隔离级别分为四种:读未提交;读已提交;可重复读;串行化
;
-
读未提交 read uncommitted :事务A会读到事务B还未提交的数据 (会出现:脏读,不可重复读,幻读)
设置当前会话的隔离级别:set session transaction isolation level READ UNCOMMITTED;
-
读已提交 read committed :事务A不会读到事务B还未提交的数据 (避免了
脏读
)
在读提交的隔离级别下,当事务B修改完数据尚未提交时,事务A是无法读取到修改的数据的;当事务B提交事务后,事务A才可以看到修改的结果 -
可重复读 repeatable read:(MySQLd的默认隔离级别)(避免了
脏读
和不可重复读
)
可以发现,当事务B提交后,事务A查询的结果依然是事务A开启时读到的数据,这就是所谓的可重复读:也就是说事务开启时读到的数据,在事务提交前,是一致的,不会因为外面事务的修改提交而改变开启事务前读到的值。
-
串行化 serializable :(避免了
所有并发问题
)读写数据都会锁住整张表;
就是严格按照串行序列排队执行事务,一个事务A执行提交结束以后,事务B才会开启。
总结
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | √ | √ | √ |
读已提交 | × | √ | √ |
可重复读 | × | × | √ |
串行化 | × | × | × |
4. 举例说明隔离级别
4.1 读未提交
设置A的隔离级别为read uncommitted
A:启动事务,A进行查询,得到初始数据;
B:启动事务,更新数据,但不提交;id=1,num=1改为num=10;
A:再次读取数据,发现读取到了B未提交的数据,这就是脏读;
B:回滚事务;
A:再次读取数据,发现数据变回初始状态;
总结: 先事务A,查到num=1;接着再事务B,将num改成10,接着继续事务A进行查询,得到num=10;但此时事务B还未提交,所以这既是读未提交,也就是我们所说的脏读
;
4.2 读已提交
设置A的隔离级别为read committed
A:启动事务,A进行查询,得到初始数据;
B:启动事务,更新数据,但不提交;id=1,num=1改为num=10;
A:再次读取数据,发现数据并未被修改;
B:提交事务;
A:再次读取数据,发现数据已经变化,说明提交的修改被事务中的A读到了,这就是不可重复读
总结:读已提交读隔解决了脏读的问题,但仍有不可重复读的问题;即事务A在两次查询的数据不一致。
4.3 可重复读
设置A的隔离级别为repeatable read
A:启动事务,此时数据为初始状态;
B:启动事务,更新数据,但不提交;
A:再次读取数据,发现数据未被修改;
B:提交事务;
A:再次读取数据,发现数据仍未发生变化,则说明可以重复读;
B:插入/更新一条新的数据记录,并提交
A:再次读取数据,发现数据未发生变化,虽然可以重复读了,但是却发现读的不是最新数据记录,这就是所谓的“幻读”
A:提交本次事务,再次读取数据,发现读取到了更新的数据
总结: 可重复读只允许读取已提交记录,也就是说事务开启时读到的数据,在事务提交前,是一致的,不会因为外面事务的修改提交而改变开启事务前读到的值。但是在事务A两次读取一个记录期间,事务B插入新纪录,事务A是可能在未提交之前读取不到更新的记录,这就是幻读
;
4.4 可串行化(可序列化)
设置A的隔离级别Serializable
A:启动事务,此时数据为初始状态
B:发现B此时进入了等待状态,原因是因为事务A尚未提交结束,只能等待;(此时,B可能会发生等待超时)
A:提交事务
B:发现插入成功
总结:serializable
完全锁定整张表,当事务B来查询同一份数据就必须等待,直到事务A完成并解除锁定为止。就是严格按照串行序列排队执行事务,事务B只有等待到事务A执行提交结束,事务B才会开启。因此性能上消耗比较大。