简析Mysql事务隔离(一)
本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。
什么是事务
事务的定义
事务,由一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
事务的特点
事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)四个特性,简称 ACID,缺一不可。
原子性是指事务是不可分割的最小单元;
一致性是指数据从一个状态转到另外一个一致性的状态。
隔离性是指一个事务做的修改在未提交之前是对其他事务不可见
持久性是指事务做的修改会永久保存到数据库中
事务隔离级别
未提交读
在未提交读级别,事务的修改,即使没有提交,对其他事务也是可见的。事务可以读取未提交的数据,称为脏读;这种级别使用的很少,优点很小,缺点却很多,不常使用。
提交读/不可重复读
大多数默认的隔离级别都是提交读,但是mysql不是,提交读表示在一个事务开始的时候,只能看见已经提交的事务做出的修改。也就是未提交的数据,其他事务是不可见的。但是该级别不保证同一个事务中多次读取同样的结果是一致的。
可重复读
可重复读解决脏读的问题,该级别保证了同一个事务中多次读取同样的结果是一致的。但是无法解决幻读的问题。
可串行化
可串行化是最高的隔离级别,强制事务的串行执行,避免了幻读的问题,简单来说,可串行读会在读取的每一行都加锁,这样可能导致大量的超时和锁争用的问题,使用这个级别使用的很少。
与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束。
事务隔离级别要实际解决的问题
脏读
脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。
不可重复读
对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。
可重复读
可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。
幻读
幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。
事务隔离级别能解决实际问题的情况
隔离级别的实现原理
读写锁
未提交读和可串行化跟锁有关,未提交读,采取的是读不加锁原理。事务读不加锁,不阻塞其他事务的读和写。事务写阻塞其他事务写,但不阻塞其他事务读;
可串行化的所有SELECT语句会隐式转化为SELECT ... FOR SHARE,即加共享锁。读加共享锁,写加排他锁,读写互斥。如果有未提交的事务正在修改某些行,所有select这些行的语句都会阻塞。
MVCC
提交读和可重复读与MVCC有关,MVCC中文叫多版本并发控制。
MVCC的实现原理
MVCC它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并发性能的一种机制。它的实现依赖于隐式字段、undo日志、快照读&当前读、Read View
隐式字段
对于InnoDB存储引擎,每一行记录都有两个隐藏列,一个保存行的创建时间,一保存行的过期时间(具体的时间值是事务的版本号)。
比如插入操作,为新插入的行保存当前的创建事务编号。
比如SELECT操作,需要找到版本早于当前事务版本的数据行,这样可以保证获取的数据在事务开始之前就已经存在或者由当前事务创建,而对于行的删除版本要么未定义,要么大于当前的版本号,保证事务读取的行在事务开始之前未被删除。
undo日志
事务未提交的时候,修改数据的镜像(修改前的旧版本),存到undo日志里。以便事务回滚时,恢复旧版本数据,撤销未提交事务数据对数据库的影响。
undo日志是逻辑日志。可以这样认为,当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录。存储undo日志的地方,就是回滚段。
总而言之,undo log 中记录某行数据的多个版本的数据
快照读&当前读
快照读,读取的是记录数据的可见版本(有旧的版本),不加锁,普通的select语句都是快照读;
当前读,读取的是记录数据的最新版本,显示加锁的都是当前读。
Read View
Read View就是事务执行快照读时,产生的读视图,用来判断当前版本数据的可见性。
事务执行快照读时,会生成数据库系统当前的一个快照,记录当前系统中还有哪些活跃的读写事务,把它们放到一个列表里。
提交读跟可重复读隔离级别,最大的区别就是:提交读每次读取数据前都生成一个ReadView,而可重复读只在第一次读取数据时生成一个ReadView。
锁相关概念
InnoDB 实现了标准的行级锁,包括两种:共享锁(简称 S 锁)、排它锁(简称 x 锁)。
共享锁:允许持锁事务读取一行。
排他锁:允许持锁事务更新或者删除一行。
记录锁:记录锁是最简单的行锁,仅仅锁住一行。如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;记录锁永远都是加在索引上的,即使一个表没有索引,InnoDB也会隐式的创建一个索引,并使用这个索引实施记录锁。记录锁会阻塞其他事务对其插入、更新、删除
间隙锁:间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。
Next-key锁:是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。