前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL-MVCC多版本控制及事务的隔离性

MySQL-MVCC多版本控制及事务的隔离性

作者头像
关忆北.
发布2023-10-11 09:40:03
2210
发布2023-10-11 09:40:03
举报
文章被收录于专栏:关忆北.关忆北.
MySQL事务的启动方式

  1. 隐式:执行SQL语句自动提交(前提MySQL使用SET AUTOCOMMIT=1开启自动提交)
  2. 显式:begin/start transaction; update user set username = 'timi' where id =1; commit;

begin/start transaction命令并不是一个事务的起点,在执行到它们之后的第一个操作InnoDB表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用start transaction with consistent snapshot

MySQL的InnoDB引擎具有不同的事务隔离级别,不同事务隔离级别通过视图创建时机的不同来实现。

MySQL的两种视图
  • View:它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是create view …,而它的查询方法与表一样。
  • 另一个是InnoDB在实现MVCC时用到的一致性读视图,即consistent read view,用于支持RC(Read Committed,读提交)和RR(Repeatable Read,可重复读)隔离级别的实现。
MySQL的MVCC快照

MVCC:Multiversion concurrency control,即多版本控制,在并发访问数据库时,通过对数据做多版本管理,也就是为每条记录保存多份历史数据供读事务访问,新的写入只需要添加新的版本即可,无需等待。避免因为写数据时要加写锁而阻塞读取数据的请求,实现读取数据不用加锁,读取数据同时修改。修改数据同时可读取。

多版本指的是数据表中同一个行数据可能会有多个版本(row),每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id,InnoDB复用了Undo log中已经记录的历史版本数据来满足MVCC的需求。

控制指的是,InnoDB使用Undo log控制不同的事务找到对应的数据版本。

InnoDB中每一个事务都有一个唯一的事务ID,叫做transaction id,每个事务在开始的时候向InnoDB事务系统申请的,其值按申请的顺序严格递增。

官方解释: A unique transaction ID number, internal to InnoDB. These IDs are not created for transactions that are read only and nonlocking

Undo log

为形象说明undo log,举个例子。

现有数据表如下:

代码语言:javascript
复制
CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `k` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

一次执行执行insertupdate及再次update语句,对应的事务编号分别为:Transaction I、Transaction J、Transaction K,那么新插入的id=1的数据行就有三个版本,undo log记录的数据数据分别为:

undo_logicial
undo_logicial

图片引用自taobao数据库月报。

undo log即为上图中的Rollptr,历史版本数据通过Rollptr穿成一个链表供MVCC使用。因此,undo log并不是在数据库中真实存在的,当需要查询某行数据历史版本时,可以通过Rollptr计算出。

Undo log与隔离级别的关系
数据可见性

一个事务在启动时声明:以我启动的时刻为准,如果一个数据版本是在我启动之前生成的,就认,如果事务在我启动后生成的,就不认,必须找到它的上一个可见的版本。如果数据是这个事务自己更新的数据,它还是要认的。

InnoDB为每一个事务构造了一个数组,用来保存这个事务启动的瞬间,当前正在“活跃”指的是,启动了还没提交。

数据里事务ID的最小值记为低水位,当前系统里边已经创建过的事务ID的最大值加1记为高水位。视图数组和高水位,组成了当前事务的一致性视图(read-view)。而数据版本的可见性规则,就是基于数据的row trx_id和这个一致性视图的对比结果得到的。

视图数组把所有的row trx_id分成了几种不同的情况。

对于事务启动瞬间来说,一个数据版本的row trx_id有以下几种可能:

对于当前事务的启动瞬间来说,一个数据版本的row trx_id,有一下几种可能:

  1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的。
  2. 如果落在红色部分,标识这个版本是由将来启动的事务生成的,是肯定不可见的。
    • 如果row trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见。
    • 如果row trx_id不在数组中,表示这个版本是已经提交的事务生成的,可见。

假设未提交事务数组中含有的row trx_id包含:90,91,92,96,93,94,95并未在未提交事务数组中,有一种可能性是:93,94,95事务执行较快,已提交。

一致性读

读操作基于某时间点得到一份那时的数据快照,无论其他数据对该行数据的修改,在查询过程中,若其他事务修改了数据,那么就要从undo log中获取旧版本的数据。

更新逻辑

规则:

更新数据都是先读后写的,读取的数据,读到的是当前最新版本值,称为“当前读”(current read)。

当某行数据被其他事务修改,拿到写锁还未释放时,“当前读”会等待写锁释放后再去执行。

可重复读与读提交

可重复读与读提交最大的区别是:

  • 可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后的事务里的其他查询都共用这个一致性视图;对于可重复读,查询只承认在事务启动前就已经提交完成的数据。
  • 在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。对于读提交,查询只承认在语句启动前就已经提交完成的数据。
举例
代码语言:javascript
复制
CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `k` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1);
img
img

在可重复读隔离级别下,事务A查询到的k值为1,因为事务A首先启动,创建事务id,接着是事务B,事务B的row trx_id会大于事务A,落在高水位未开始事务中,数据修改对A不可见,事务C隐式开启事务,执行完成后隐式提交,由于同样C的row trx_id大于A,修改对于事务A版本依旧不可见,(即一致性读),所以事务A查询到的k值为历史版本1。

事务B查询到的k值为3,事务B首先开启事务,事务C随后开启,事务C将k=1修改为k=2,由于在修改时会使用“当前读”来查询数据的最新版本来保证数据的修改不会丢失,所以事务B在执行update语句前会查询到当前版本k=2,更新后k=3。

举例引用自极客时间《MySQL实现45讲》

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MySQL事务的启动方式
  • MySQL的两种视图
  • MySQL的MVCC快照
  • Undo log
  • Undo log与隔离级别的关系
    • 数据可见性
      • 一致性读
        • 更新逻辑
          • 可重复读与读提交
          • 举例
          相关产品与服务
          云数据库 MySQL
          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档