Madao No More

你的努力程度之低,根本轮不到拼天赋.


  • 首页

  • 底层基础

  • 人文书籍

  • 日记

  • 面试问题

  • Linux

  • 编程语言

  • 服务器应用

  • 各种工具软件

  • 工作中的问题

  • 归档

  • 关于

  • 搜索
close

7_事务和锁

时间: 2019-08-22   |   分类: 服务器应用     |   阅读: 3319 字 ~7分钟
  • 1. 行锁和事务
    • 1.1. 实例
      • 1.1.1. 事务中视图生成时机
    • 1.2. MySQL的两个视图的概念
  • 2. 快照的工作原理
    • 2.1. 快照的实现
    • 2.2. 快照具体分析
      • 2.2.1. 可见性规则
  • 3. 具体例子
    • 3.1. 前置条件
    • 3.2. 每个事务的事务数组
    • 3.3. 执行顺序
    • 3.4. 原则
    • 3.5. 事务B更新时为什么能看到事务C的提交
      • 3.5.1. 当前读的其他应用
  • 4. 事务C使用显示提交
    • 4.1. 执行顺序
  • 5. 概念和原理
    • 5.1. 可重复读实现
    • 5.2. 读提交级别和可重复读级别的区别
  • 6. 读提交级别下上面的例子
    • 6.1. 执行顺序
  • 7. 总结
    • 7.1. 问题

1. 行锁和事务

  • 事务:可重复读级别下,启动时会创建一个视图,保证操作时数据一致.
  • 锁:事务在执行时,如果指定行有锁,事务会进行等待.

1.1. 实例

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);

事务和锁

过程:

  • 事务C:没有显示的使用begin/commit,一个update语句本身就是一个事务.执行完成自动提交
  • 事务B:立刻创建视图,更新后查询
  • 事务A:只读事务,直接查询

结果:

  • 事务B:3
  • 事务A:1

1.1.1. 事务中视图生成时机

默认autocommit=1

  • begin/start transaction不是事务的起点
    • 他们执行后第一个执行的SQL语句才是
    • 即在第一个SQL执行时,生成视图
  • start transaction with consistent snapshot:立刻启动事务
    • 立刻生成视图

1.2. MySQL的两个视图的概念

  1. view:查询语句定义的虚拟表,在调用时执行查询语句并生成结果:create view
  2. InnoDB实现MVCC时用的一致性视图(consistent read view):
    1. 用于支持读提交和可重复读隔离级别的实现.
    2. 其没有物理结构,定义事务执行期间,事务能看到什么数据.

2. 快照的工作原理

可重复读级别下:事务启动时会对整个库拍快照

2.1. 快照的实现

  1. InnoDB中每个事务有一个唯一的事务ID:transaction id.
    1. 事务id在事务开始时向系统申请.
    2. 具体id按照申请顺序递增
  2. 每行数据也是有多个版本.每次事务更新数据时,会生成一个新的数据版本.并且把transaction id复制给该数据版本的事务ID:row trx_id
    1. 同时,保留就的数据版本
    2. 并且新的数据版本中可以拿到旧的数据.
  3. 数据表中的每一行都可能有多个版本row,每个版本有自己的row trx_id

行

  1. 上面的虚线是undo log
  2. v1,v2,v3不是真实存在的,而是根据当前版本和undo_log计算出来的

2.2. 快照具体分析

可重复读级别下:

  1. 启动事务A
    1. 事务A能够看到所有已经提交的事务的结果
    2. 在事务A执行期间,无法看到其他事务的结果
  2. 事务A启动时声明:
    1. 以其transaction id为准,数据在其id前面的,就可以用
    2. 以其transaction id为准,数据在其id后面的,数据就对事务A不可见,需要当前数据之前的版本
    3. 如果是事务A自己修改的数据,是可以用的.
  3. 事务A启动时,会有一个数组:记录事务A启动瞬间,当前活跃的事务id(启动了,尚未提交的事务)
    1. 数组中最小的ID称为低水位
    2. 数组中最大的ID+1称为高水位
    3. 数组+高水位:形成了当前事务的一致性视图
    4. 数据版本的可见性:根据row trx_id和上面的一致性视图对比得到

2.2.1. 可见性规则

可见性规则

一个数据版本的row trx_id的可能性:

  1. 绿色部分:该版本是以提交的事务或者当前事务自己生成的
    1. 可见
  2. 红色部分:该版本是将来启动的事务生成的
    1. 不可见
  3. 黄色部分:
    1. row trx_id在数组中,该版本是由还未提交的事务生成
      1. 不可见
    2. row trx_id不在数组中,该版本是由已经提交的事务生成的.
      1. 可见

3. 具体例子

例子执行流程

3.1. 前置条件

  1. 事务A开始前,系统里只有一个活动的事务ID是99
  2. 事务A,B,C的ID分别是100,101,102.且当前系统只有这4个事务
  3. 3个事务开始前,(1,1)这行数据的row trx_id是90.

例子

3.2. 每个事务的事务数组

  • 事务A:[99,100]
  • 事务B:[99,100,101]
  • 事务C:[99,100,101,102]

3.3. 执行顺序

  1. 事务C首先执行
    1. 个人猜测:应该是事务C直接就是update (直接生成了view并执行了,另外的两个,首先生成view)
    2. 数据更改:
      1. 90版本—–>102版本===(1,1)变成(1,2)
  2. 事务B执行
    1. 数据更改:
      1. 102版本—–>101版本===(1,2)变成(1,3)
  3. 事务A执行
    1. 数据查找:事务A当前的数组是[99,100]
      1. 读当前版本,发现是(1,3)—–>是101版本,是高水位,红色区,不可见
      2. 计算上一个版本,发现是(1,2)—–>是102版本,是高水位,红色区,不可见
      3. 计算上一个版本,发现是(1,1)—–>是90版本,是低水位,绿色区,可见.
      4. 查询结果:(1,1)

3.4. 原则

对于一个事务视图来说,除了自己的更新总是可见以外

  1. 版本未提交,不可见
  2. 版本已提交,但是是在视图创建后提交的,不可见
  3. 版本已提交,而且是在视图创建前提交的.可见

3.5. 事务B更新时为什么能看到事务C的提交

事务B

原因:

事务B更新前如果查询数据,还是1(因为事务B生成了视图,事务C才提交的.对事务B不可见)

但是更新的时候,不能在历史数据上更新(否则事务C的提交就丢失了),必须在当前数据上更新.

更新数据是先读后写.

  • 读是读当前值,称为当前读.当前值是(1,2)===>更新后是(1,3),row trx_id版本号是101
  • 事务B再次查询时,自己的版本号==该数据的版本号====>数据可以直接使用

3.5.1. 当前读的其他应用

除了update以外,如果select语句要加锁,也是当前读

-- 如果事务A使用如下方法查询,也是直接获得3,即101版本的数据
select k from t where id=1 lock in share mode;
select k from t where id=1 for update;

4. 事务C使用显示提交

事务C显示提交

4.1. 执行顺序

两阶段锁协议:

  1. 事务C更新后没有立刻提交
    1. 数据更新:
      1. 生成了(1,2)这个数据版本===>数据版本102
      2. 该行的写锁还没有释放
  2. 事务B也更新了,也没有提交
    1. 数据未更新:
      1. 事务B要更新,必须是是当前读,且必须加锁.
      2. 因为事务C的锁还没有释放,事务B等待.hi到事务C’释放锁,才能继续当前读

事务B等待

5. 概念和原理

5.1. 可重复读实现

可重复读的核心:一致性读(consistent read)

  • 事务更新数据时,只能使用当前读(读取当前的值)
  • 如果当前记录的行锁被其他事务杭甬,进入锁等待

5.2. 读提交级别和可重复读级别的区别

  • 可重复读隔离级别下:事务开始时(执行第一条语句,或者使用start transaction with consistent snapshot立即创建)创建一致性视图,hi后事务中的其他**查询(select)**公用这个一致性视图
  • 读提交:没一个语句执行前都会重新计算一个新的视图

6. 读提交级别下上面的例子

读提交中start transaction with consistent snapshot语句没用==start transaction

读提交例子

6.1. 执行顺序

  1. 事务C执行
    1. 数据更改
      1. 90版本===>102版本,数据变成(1,2)===>数据提交
  2. 事务B执行
    1. 数据更改
      1. 102版本===>101版本,数据变成(1,3)===>数据未提交
  3. 事务A执行查询.
    1. 数据查询
      1. 101版本还没有提交,不可见
      2. 102版本提交了,可见

结果:

  • 事务A:2
  • 事务B:3

7. 总结

  • Innodb中,每个版本有自己的row trx_id
  • 每个事务有自己的一致性视图
  • 普通查询语句是一致性读
    • 一致性读会根据row trx_id和一致性视图来确定数据版本的可见性.
  • 可重复读:只承认事务启动前提交完成的数据
  • 读提交:只承认语句启动前提交完成的数据
  • 当前读:读取已经提交完成的最新版本
  • 为什么表结构不支持可重复读?
    • 因为表结构没有对应的行数据===>没有对应的row trx_id===>只能当前读
    • MySQL8.0可以把表结构放到InnoDB字典中,以后也有可能支持表结构的可重复读

7.1. 问题

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, c) values(1,1),(2,2),(3,3),(4,4);

-- 一个事务
begin;
select * from t;
-- 数据正确
update t set c=0 where id=c;
select * from t;
-- 数据不变;
-- 该语句不起效果,有哪些可能?

可能1:

可能1

  1. 事务A执行第一个语句创建一致性视图
  2. 事务B执行.更新数据并提交
  3. 事务A的update语句当前读
    1. 读的结果(1,2),(2,3),(3,4),(4,5)
    2. 执行更新,条件不匹配
  4. 事务A最后的select(使用创建的一致性视图)

可能2:

可能2

  1. 事务B’执行,创建一致性视图
  2. 事务A执行,创建一致性视图
  3. 事务B更新,并提交
  4. 事务A的update当前读
    1. 读的结果(1,2),(2,3),(3,4),(4,5)
    2. 执行更新,条件不匹配
  5. 事务A最后的select(使用创建的一致性视图)
#服务器应用 - MySQL书籍 - 极客时间_丁奇的MySQL基础课#
6_行锁
2019-08-22_为什么PHP类名不区分大小写
Madao

Madao

人的一切痛苦,本质上都是对自己无能的愤怒.

505 日志
8 分类
78 标签
GitHub E-mail
© 2009 - 2022 Madao No More
Powered by - Hugo v0.89.4
Theme by - NexT
0%