MySql事务-版本链
以下所有的知识都是基于 InnoDB 的,因为MyISAM不⽀持事务。
事务的隔离级别四种
读未提交(READ UNCOMMITTED) :⼀个事务还没提交时,它做的变更就能被别的事务看到。
读提交(READ COMMITTED) :⼀个事务提交之后,它做的变更才会被其他事务看到。
可重复读(REPEATABLE READ) :⼀个事务执⾏过程中看到的数据,总是跟这个事务在启动时看到的
数据是⼀致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可⻅的。
串⾏化(SERIALIZABLE) :对于同⼀⾏记录,“写”会加“写锁”,“读”会加“读锁”,当出现读写锁冲突
的时候,后访问的事务必须等前⼀个事务执⾏完成,才能继续执⾏。
隔离级别解决了哪些问题⼤家也应该都是知道的分别有:
脏读(dirty read) :如果⼀个事务读到了另⼀个未提交事务修改过的数据
不可重复读(non-repeatable read) :如果⼀个事务只能读到另⼀个已经提交的事务修改过的数
据,并且其他事务每对该数据进⾏⼀次修改并提交后,该事务都能查询得到最新值。
幻读(phantom read) :如果⼀个事务先根据某些条件查询出⼀些记录,之后另⼀个事务⼜向表中
插⼊了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另⼀个事务插⼊的记录也读
出来
我们可以通过下边的语句修改事务的隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;//等级就是上⾯的⼏种
在 可重复读 隔离级别下,事务在启动的时候就“拍了个快照”,注意,这个快照是基于整库的。
你肯定会说,这怎么可能?如果⼀个库有100G,那么我启动⼀个事务,MySQL就要拷⻉100G的数据出
来,这个过程得多慢啊,这谁顶得住啊?可是你回头⼀想,平时的事务执⾏起来不是很快么
实际上,数据库并不需要拷⻉出这100G的数据,那快照怎么实现的?
InnoDB⾥⾯每个事务有⼀个唯⼀的事务ID,叫作 transaction id ,它是在事务开始的时候向InnoDB
的事务系统申请的,是按申请顺序严格递增的
每⾏数据也都是有多个版本的,每次事务更新数据的时候,都会⽣成⼀个新的数据版本,并且把
transaction id 赋值给这个数据版本的事务ID,记为** row trx_id** 。同时,旧的数据版本要保留,并
且在新的数据版本中,能够有信息可以直接拿到它。
也就是说,数据表中的⼀⾏记录,其实可能有多个版本(row),每个版本有⾃⼰的 row trx_id 。
这是⼀个隐藏列,还有另外⼀个 roll_pointer :每次对某条聚簇索引记录进⾏改动时,都会把旧的版
本写⼊到 undo⽇志 中,然后这个隐藏列就相当于⼀个指针,可以通过它来找到该记录修改前的信息
两者都在InnoDB的聚簇索引中,⼤概就⻓这样:
undo log 的回滚机制也是依靠这个版本链,每次对记录进⾏改动,都会记录⼀条undo⽇志,每条
undo⽇志也都有⼀个 roll_pointer 属性(INSERT操作对应的undo⽇志没有该属性,因为该记录并没
有更早的版本),可以将这些undo⽇志都连起来,串成⼀个链表,所以现在的情况就像下图⼀样
接下来可以说⼀下事务隔离级别和MVCC的关系了,下⾯的例⼦是⼀个版本链,事务id⼤家可以看出,
三个事务分别作了不同的事情。
在 读提交 隔离级别下,这个视图是在每个SQL语句开始执⾏的时候创建的,在这个隔离级别下,事务在
每次查询开始时都会⽣成⼀个独⽴的ReadView。
可重复读,在第⼀次读取数据时⽣成⼀个ReadView,对于使⽤ REPEATABLE READ 隔离级别的事务来
说,只会在第⼀次执⾏查询语句时⽣成⼀个 ReadView ,之后的查询就不会重复⽣成了,所以⼀个事务
的查询结果每次都是⼀样的。
这⾥需要注意的是, 读未提交 隔离级别下直接返回记录上的最新值,没有视图概念,也就是图中丙丙那
⼀栏,脏读,幻读,不可重复读都有可能发⽣。⽽ 串⾏化 隔离级别下直接⽤加锁的⽅式来避免并⾏访问。
mvcc⾥⾯跟事务隔离级别相关的,只有可重复读和读已提交这两种
大部分公司数据库隔离级别默认都是读已提交,不过我们会在⼀些场景开启可重复读,序列化很少⻅
可重复读我们之前都是在跟订单⾦额相关的场景去开启的,还有很多数据修改过程也会⽤可重复度,因
为很多值是需要查询出来,依据那个值做别的操作的,如果多次查询的结果值不⼀样,那后者也会受到
影响。
序列化 被称为数据库隔离级别的 ⻩⾦标准 ,它是绝⼤多数商业数据库系统中提供的最⾼隔离级别,⼀
些⾼度⼴泛部署的系统甚⾄⽆法提供隔离级别与可序列化⼀样⾼,⾦融的场景居多,性能也是最差的,
但是银⾏取钱你会在乎那⼏秒么?
有没有发现银⾏的ATM响应速度特别慢,他们的场景都是很严密的,各种事务,锁,都是结合的,就是
为了保证结果的准确性。
⼤家可以⽤这个命令去看看⾃⼰公司或者⾃⼰现在使⽤的数据库的隔离级别
show variables
image.png
参考:《MySQL 是怎样运⾏的:从根⼉上理解 MySQL》、《⾼性能MySQL》、《 MySQL 实战45 讲》
总结
从上边的描述中我们可以看出来,所谓的 MVCC(Multi-Version Concurrency Control ,多版本并发控
制) 指的就是在使⽤ 读已提交(READ COMMITTD)、可重复读(REPEATABLE READ) 这两种隔离级别的事务
在执⾏普通的SELECT操作时访问记录的版本链的过程,这样⼦可以使不同事务的读-写、写-读操作并
发执⾏,从⽽提升系统性能。
这两个隔离级别的⼀个很⼤不同就是: ⽣成ReadView的时机不同 ,READ COMMITTD在每⼀次进⾏普通
SELECT操作前都会⽣成⼀个ReadView,⽽REPEATABLE READ只在第⼀次进⾏普通SELECT操作前⽣
成⼀个ReadView,数据的可重复读其实就是ReadView的重复使⽤。