MySQL实战45讲学习笔记-事务隔离级别-MVCC-当前读

事务隔离级别

  • 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

查询隔离级别:

show variables like 'transaction_isolation'

在这里插入图片描述

在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

MVCC

举例:

建表:

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),(2,2);

执行:

在这里插入图片描述

三个事务的执行流程:

在这里插入图片描述

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

  • 第一种启动方式,一致性视图是在执行第一个快照读语句时创建的;
  • 第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的。

数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。

在这里插入图片描述

图中的三个虚线箭头,就是 undo log;而 V1、V2、V3 并不是物理上真实存在的,而是每次需要的时候根据当前版本和 undo log 计算出来的。比如,需要 V2 的时候,就是通过 V4 依次执行 U3、U2 算出来。

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

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

  • 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
  • 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
  • 如果落在黄色部分,那就包括两种情况
    • 若 row trx_id 在数组中(这个事务比当前事务开始的早),表示这个版本是由还没提交的事务生成的,不可见;
    • 若 row trx_id 不在数组中(这个事务比当前事务开始的晚),表示这个版本是已经提交了的事务生成的,可见。

当前读

实测在上面实例中语句7之前查询select * from test1 where id = 1会得到 1,而执行了语句7之后的查询得到 3,为什么?

更新数据的时候,就不能再在历史版本上更新了,否则事务 C 的更新就丢失了。因此,事务 B 此时的 set k=k+1 是在(1,2)的基础上进行的操作。

所以,这里就用到了这样一条规则:更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。自己的版本号和最新的版本号一致,这个更新才是"自己的",才能更新。

update触发了当前读,获取到最新版本的(1, 2),再update得到(1, 3),而事务A没有触发当前读,所以还是(1, 1)。

select语句触发当前读

如果把事务 A 的查询语句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update,也都可以读到最新版本的数据,返回的 k 的值是 3。下面这两个 select 语句,就是分别加了读锁(S 锁,共享锁)和写锁(X 锁,排他锁)。

select k from t where id=1 lock in share mode;
select k from t where id=1 for update;

实测结果:

在这里插入图片描述

执行第9句时卡住了,因为事务B没有提交,事务B提交(第10句)后,事务A的第9句出现了结果。因为事务A的当前读需要id = 1的行锁,而事务B没提交,没有释放这个行锁,事务A就需要等待。

可重复读的核心就是一致性读(consistent read);而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。

可重复读和读提交最主要的区别是:

  • 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;
    • 个人理解:事务B证明了当前读会更新这个一致性视图
  • 在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值