三歪连MVCC和事务隔离级别的关系都不知道...
2020-12-26 14:32
在可重复读隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。
在正式介绍之前,我还需要介绍一下版本链。
版本连
在可重复读隔离级别下,事务在启动的时候就“拍了个快照”,注意,这个快照是基于整库的。
你肯定会说,这怎么可能?如果一个库有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响应速度特别慢,他们的场景都是很严密的,各种事务,锁,都是结合的,就是为了保证结果的准确性。
大家可以用这个命令去看看自己公司或者自己现在使用的数据库的隔离级别:
上一篇:一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系
下一篇:HTML5 GAME TUTORIAL(六): Collision detection and physics(译)