mysql-事务隔离级别
事务ACID特性
* 原子性(Atomicity)
一个事物内的所有操作视为一个不可分割的操作,事务内所有操要么全执行成功要么全失败
例如:一个事务内A给B转账,先从A账户中扣钱,然后向B账户中加对应的钱数,这两次数据库操作要么全成功要么全失败,不能某个操作成功了,其他的失败了
* 一致性(Consistency)
一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少(摘自百度)
例如:一共有2个账户,这些账户的总余额为100,无论如何进行并发操作最终这2个账户的余额的和必须为100
* 隔离性(Isolation)
隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作
MVCC(Mutil-Version Concurrency Control) 多版本并发控制
如果没有MVCC,当想要读取的数据被其他事务用排它锁锁住时,只能互斥等待;而这时MVCC可以通过提供历史版本从而实现读取被锁的数据的历史版本,从而避免了互斥等待。
锁
Shared Locks(共享锁/S锁)
若事务T对数据对象A加上S锁,则事务T只能读A;其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
Exclusive Locks(排它锁/X锁)
若事务T对数据对象A加上X锁,则只允许T读取和修改A,其它任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。它防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。在更新操作(INSERT、UPDATE 或 DELETE)过程中始终应用排它锁。
注意:排他锁会阻止其它事务再对其锁定的数据加读或写的锁,但是不加锁的就没办法控制了。
Record Locks(行锁)
是加在索引行(对!是索引行!不是数据行!)上的锁。比如select * from user where id=1 and id=10 for update,就会在id=1和id=10的索引行上加Record Lock。
Gap Locks(间隙锁)
间隙锁,它会锁住两个索引之间的区域。比如select * from user where id>1 and id<10 for update,就会在id为(1,10)的索引区间上加Gap Lock。
Next-Key Locks(间隙锁)
也叫间隙锁,它是Record Lock + Gap Lock形成的一个闭区间锁。比如select * from user where id>=1 and id<=10 for update,就会在id为[1,10]的索引闭区间上加Next-Key Lock。
组合起来就有,行级共享锁,表级共享锁,行级排它锁,表级排它锁。
加锁
- insert、delete和update都是会加排它锁(Exclusive Locks)
- select只有显式声明才会加锁
- select: 即最常用的查询,是不加任何锁的
- select … lock in share mode: 会加共享锁(Shared Locks)
- select … for update: 会加排它锁
* 持久性(Durability)
在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚
事务隔离级别
完全隔离是不现实的,因为完全隔离会使事务串行化,严重影响并发性能,所以引入隔离级别来控制并发访问
MySQl支持4中隔离级别
隔离级别 | 描述 |
---|---|
读未提交(read uncommitted) | 一个事务可以读其他事务未提交的数据 |
读已提交(read committed) | 一个事务只能读其他事务已提交的数据 |
可重复读(repeatable read) | 一个事务内多次读同一数据必须一致(MySQL默认隔离级别) |
串行化(serializable) | 事务串行化,同一个表同时只能有一个事务进行操作 |
并发引发问题
脏读
A事务读取了B事务未提交的数据并在此基础上进行操作,结果B事务回滚了,那么A所进行的操作就是有问题的
不可重复读
不可重复读指同一事务中两次读取的数据不一致,A事务读取了一条数据,B事务修改了数据并且提交了,A事务又读了此条数据结果与上一读取的数据不一致
幻读
事务A统计了数据量,事务B添加了N条记录,事务B又查了一次,结果两次结果数量不一致
隔离级别对应问题
- 隔离级别越往下并发效率越低,可重复读行级锁,串行化表级锁(x:可能 √:不可能)
级别\问题 | 脏读 | 不可重复读 | 幻读 | 实现方式 |
---|---|---|---|---|
读未提交(read uncommitted) | x | x | x | 读没有控制,写加排他锁 |
读已提交(read committed) | √ | x | x | 快照读:读MVCC,写加排他锁 当前读:读加行锁,写加排它锁 |
可重复读(repeatable read) | √ | √ | x | 读MVCC,写加排他锁 当前读:读加间隙锁,写加排它锁 |
串行化(serializable) | √ | √ | √ | 读加共享锁,写加排他锁 |
- 在读未提交、读已提交、可重复读的隔离级别中:
- 两个读的事务,可并发;
- 一读、一写的事务,可并发;(因为只有写加锁)
- 两个写的事务,不可以并发。(以内都加了排他锁)
总结
只有InnoDB引擎支持事务并且支持行级锁,MyISAM引擎并不支持事务且仅支持表级锁
InnoDB适合频繁update业务场景(交易系统),MyISAM适用于大量select、insert业务场景(日志类系统)
处理数据库并发问题并非只能依靠数据来处理,可以结合代码进行合理的加锁,例如依靠外部redis/zookeeper做分布式锁