Mysql的事务以及隔离级别
1. 什么是数据库事务?
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态变到另一种一致性状态。事务是逻辑上得到一组操作,要么都执行,要么都不执行。
事务最经典也经常被拿出来说例子就是转账了。
假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
2 事务的四大特性(ACID)是什么
- 原子性:事务是最小的执行单位,不允许分割。事务的原子性确保动作要么都执行,要么都不执行;
- 一致性:执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
- 隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
- 持久性:一个事务被提交之后,它对数据库中的数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
3 什么是脏读?幻读?不可重复读?
- 脏读(Dirty Read): 某个事务已更新一份数据,另一个事务在此时读取了同一份数据,但由于某些原因,前一个进行了rollback操作,则后一个事务所读取的数据就会是不正确的;
- 不可重复读(Non-repeatable read): 在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务之前事务对更新的原有的数据;同一行数据
- 幻读(Phantom Read): 在一个事务的两次查询中数据量不一致,例如有一个事务查询了几列数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。插入了新航
例子:
脏读:
事务查询id为1的数据,num字段为1
B事务将id为1的数据num更新为2,但未commit
事务查询id为1的数据,num字段变为2
B回滚,则A读到的数据为脏数据
不可重复读:
事务查询id为1的数据,num字段为1
B事务将id为1的数据num更新为2,commit
事务查询id为1的数据,num字段变为2
A事务前后两次读到的同一条记录num字段不同,不可重复读
幻读:
A事务查询id >=1 and id <=3的数据,得到id=1和id=3两条数据(注意:没有id=2的数据)
B事务插入id=2的数据
A事务再查询id >=1 and id <=3的数据,发现多了一条id=2的数据,即两次查询数据的行数不同
4 什么是事务的隔离级别?Mysql的默认隔离级别是什么?
为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
SQL标准定义了四个隔离级别:
- READ-UNCOMMITED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读、或不可重复读;
- READ-COMMITED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
- REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被事务本身自己所修改,可以阻止脏读和不可重复读的发生,但幻读仍有可能发生。
- SERIABLIZABLE(可串行化): 最高的隔离级别,完全服务ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以阻止脏读、不可重复读以及幻读。
不可重复读和幻读的区别是什么?
不可重复读主要是说多次读取一条记录,发现该记录中某些列值被修改过;同样的条件,读取过的数据,再次读取出来发现值不一样了。
幻读主要是说多次读取一个范围内的记录(包括直接查询所有记录结果做聚合统计),发现结果不一致。同样的条件,第1次和第2次读出来记录数不一样。
注意:Mysql默认使用的是REPEATABLE-READ隔离级别, Oracle默认使用READ_COMMITED隔离级别。
事务隔离机制的实现是基于锁机制和并发调度。其中并发调度使用是MVCC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读) 并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化) 隔离级别。