MySQL 事务
事务
InnoDB的事务符合ACID特性:
- 原子性 atomicity
- 一致性 consistency
- 隔离性 i
- 一致性 d
事务通过redo log(重做日志)和 undo log(回滚日志)实现。
redolog保证事务的原子性和持久性,undolog保证事务的一致性。redolog是物理操作,记录页的修改操作,undolog是逻辑日志,根据每行记录进行记录。
实现原理
在事务提交前,会先将事务的所有的日志写入到重做日志文件进行持久化。
undolog
回滚操作用到undolog,undolog记录了写逻辑,但是不会改变数据结构和物理大小,回滚时,只是做了所有与之前相反的工作。
除了回滚操作,undolog另一个作用就是mvcc。当用户读取一条记录时,若该记录已被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。
当事务提交时,innoDB会做以下两件事:
1.将undo放入列表,共purge使用。
2.判断undolog所在页是否可以重用,若可以分配给下个事务使用。
事务隔离级别
SQL标准4个级别
read uncommitted 未提交读
这个级别; 事务的修改即使没有提交,对其他事务也是可见的。可以读取其他事务未提交的数据,称之为脏读。
read committed 提交读(不可重复读)
这个级别满足隔离性的定义。一个事务只能看见已经提交的事务所做的修改。但是同一事务可能对数据修改,所以同一select可能返回不同结果。
read repeatable 可重复读
innorDB通过多版本并发控制 MVCC 解决。每个事务对应一个事务序号,每个序号对应undo日志。
幻读,是指某个事务前后两次查询同一个范围的时候,后一个查询看到了前一次查询没有看到的行。
Serializable 可串行化
锁,所以导致大量的超时和锁争用问题。 场景是确保数据一致性并且没有并发。
MVCC
mvcc(多版本并发控制),可以认为是一个行级锁的变种,但是在某些情况下避免了加锁操作,因此开销更低。
实现
mvcc的实现是通过保存数据在某个时间点的快照来实现的。也就是不管执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对每一张表,每一时刻看到的数据可能不一样。
innoDB的mvcc,是通过在每行记录后面保存两个隐藏的列来实现的。一个保存行的创建时间,一个保存行的过期时间,不是实际时间,而是版本号。每开始一个新的事务,版本号会随之递增。看一下mvcc是如何操作的:
SELECT
根据两个条件检查每条记录:
- 只查早于当前事务版本的数据行(也就是行的版本号小于或等于事务版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或修改过的。
- 行的删除版本要么未定义,要么大于当前事务版本号,这样可以确保事务读取的行,在事务开始前未被删除。
INSERT
为新插入的每一行保存当前系统版本号作为行版本号。
DELETE
为删除的每一行保存当前系统版本号作为行的删除版本号。
UPDATE
innoDB为新插入的行,保存当前系统版本号作为行版本号,同时,保存当前系统版本号到原来行的删除版本号。
幻读与间隙锁
幻读,是指某个事务前后两次查询同一个范围的时候,后一个查询看到了前一次查询没有看到的行。
说明:
1.在rr隔离级别下,普通的查询是快照读,快照读是看不到其他事务插入的数据的,因此,幻读在”当前读“下才会出现。
2.update导致的当前读读到之前为读到的数据,不是幻读,幻读只针对于新插入的行。
通过间隙锁(gap lock)策略防止幻读出现,不仅仅锁定查询涉及的行,还会对索引中的间隙锁定,防止幻影行的插入。
间隙锁和行锁合成next-key lock,每个next-key lock是前开后闭区间。