mysql事务 -- 事务id介绍,数据库表的隐藏列,undo log的简单介绍,当前读(select的当前读方式),快照读(与隔离性的关系),读视图(三个字段介绍)

目录

事务id

引入

介绍

如何管理事务

数据库表的隐藏列

介绍

DX_TRX_ID

DB_ROLL_PTR

DB_ROW_ID

delete flag

undo log

引入

介绍 

两种读取操作方式

当前读

介绍

使用select的当前读方式

快照读

介绍

与隔离性的关系

Read View(读视图)

介绍

结构

m_ids

up_limit_id 

low_limit_id


以下介绍的,都是MySQL中innodb存储引擎独有的特性

事务id

引入

  • 事务虽然有执行中的过程,但mysql为了解决执行中会出现的并发问题,需要让事务变成原子的
  • 并且还要判断当前事务应该看到哪些数据,为了保证隔离性

以上,都要让事务有先有后

  • 事务再怎么同时到来,都一定有先后顺序
  • 就像多线程去抢占锁资源一样,一定会有一个线程首先占到

我们如何区分事务的先后呢 -- 事务id

介绍

每当一个事务开始时,InnoDB都会通过全局递增计数器为每个事务分配一个唯一的事务id

  • 所以,这个id是严格递增且不重复的
  • 当事务完成时,事务id会随着事务的结束而不再使用,但该id仍然保存在与该事务相关的表记录中

可以根据事务id的大小,判断事务到来的顺序

  • 数字越小,来的越早 

如何管理事务

mysqld会同时处理多个事务,并且我们心里清楚,每个事务都有自己的生命周期(被创建,被执行,被销毁)

  • 所以,mysqld必然要对这么多的事务进行管理 -- 先描述,再组织
  • 所以,事务在内存中一定是一个/一套结构体/类对象(mysql是用c/c++实现的)

那么前面说到的事务id,就是这个对象中的一个字段

  • 先原子性地申请一个id,然后再用该值初始化对象中对应字段

数据库表的隐藏列

介绍

在 MySQL 的 InnoDB 存储引擎中,每个数据行都会有三个隐藏列(hidden columns)

  • 这些隐藏列并不是用户直接定义的,而是由 InnoDB 自动添加并用于管理内部事务和多版本控制(MVCC)等功能
  • 这三个隐藏列为 MySQL 实现事务的隔离性、多版本并发控制等功能提供了基础

DX_TRX_ID

6字节字段,最近修改 ( 修改 / 插入 ) 事务id
  • 该列记录创建这条记录/最后一次修改该记录的事务id

无论是手动开启的事务,还是系统默认封装单sql为事务

  • 所有对数据库表做操作的sql,都会以事务的形式交给mysql
  • 所以,每个操作都可以和事务关联起来,都有一个事务id

所以,记录下最新对该记录做修改的事务id,让mysql可以对信息做溯源

  • 并且可以用来判断其他事务是否可以看到该行的修改

DB_ROLL_PTR

7字节字段,回滚指针

  • 指向该条记录的上一个版本(也就是历史版本,在undo log中保存)

在修改表中数据之前,可能会将要改的这条记录先保存一份,然后再对数据做修改

  •  类似于写时拷贝

既然保存了历史数据,就一定要让最新记录可以找到对应的历史记录

  • 所以就有了这一列回滚指针,指向历史记录
  • 用于支持快照读,回滚操作

DB_ROW_ID

6字节字段,隐藏自增id

  • 也就是隐藏主键

如果表中没有主键,会自动以DB_ROW_ID产生一个聚簇索引(b+树)

由于DB_ROW_ID是自增的

  • 所以这个聚簇索引的顺序将根据插入的顺序排列

如果表中已经定义了主键,那这一列就没用了

  • 会使用定义出来的主键构建索引结构

delete flag

实际上还有一个删除flag隐藏字段

  • 记录该条记录是否被删除

删除记录时:

  • 并没有真的删除,只是变化flag的值
  • 因为记录是内存级的,所以我们删除时并不需要清空这块内存,并进行结构上的移动,那样太麻烦了
  • 我们只需要维护page是否"脏"(表示这些页已被修改但尚未写入磁盘),刷盘时再将有效数据紧凑排列在磁盘中

所以,flag也是一个典型的用空间换时间的策略

  • 不仅如此,也可以用于在后续恢复数据,只需要再次变化flag

undo log

引入

mysql中索引,事务,隔离性,日志等机制,都是在内存中完成

除了buffer pool,mysql还有自己的日志缓冲区

  • 保存mysql中对数据做并发提交时产生的临时数据,来支持回滚等操作

介绍 

是 MySQL(特别是 InnoDB 存储引擎)中一个重要的机制,用于实现事务的回滚、维护数据的一致性,并支持多版本并发控制(MVCC)

  • 会存储数据在事务执行期间的旧版本信息,以及与修改操作的相反sql语句

两种读取操作方式

当前读

介绍

读取的是数据的最新记录

  • 执行增删改,和部分select时,会采用当前读的方式

当前读会在读取数据时加锁

  • 确保读取到的数据在整个读取过程中不被其他事务修改

使用select的当前读方式

select * from 表名 for update;
select * from 表名 lock in share mode;

快照读

介绍

读取的是数据的历史版本

  • 不需要加锁,因为历史版本只会被读取,不能被修改,所以不需要保护
  • 允许事务在执行期间获取特定时间点的数据快照

既提高了效率,又为隔离性提供了底层支持

与隔离性的关系

因为隔离级别的设置,可能两个事务看到的数据不同

  • 就代表他们读的一定不是同一份数据

所以,为什么读写可以并发呢?

  • 因为两个操作访问的不是同一份数据,也就不需要加锁,也就可以并发执行了
  • 写的是当前的最新版本,读的是历史版本(指普通的select)

所以,事务的隔离性:

  • 本质是在数据层面上隔离 -- 在undo log中保存了数据的历史版本
  • 做法是在版本上隔离 -- 快照读通过提供独立的视图和历史数据版本,支持了事务隔离性的实现

那么我们应该看到哪个版本呢? -- 由隔离级别决定

Read View(读视图)

介绍

事务进行快照读操作时,会对该记录创建一个读视图

  • 是做可见性判断的一个重要数据结构,记录并维护系统当前活跃事务的id
  • 因为每条记录都有[最新修改它的事务id],所以可以通过当前id和该id的对比,来决定自己应不应该看见它

每个事务都要做可见性判断

  • 可见性 -- 某一条记录是否对当前事务可见
  • 所以mysql需要对所有可见性进行管理,这个管理结构就是读视图

事务和可见性的关系

  • 类似于进程和进程地址空间的关系
  • 都是用于判断某个结构对于某个资源的可见性

结构

class ReadView {
// 省略...
private:
    trx_id_t m_low_limit_id; /** 高水位,大于等于这个ID的事务均不可见*/

    trx_id_t m_up_limit_id; /** 低水位:小于这个ID的事务均可见 */
 
    trx_id_t m_creator_trx_id;   /** 创建该 Read View 的事务ID*/
   
    ids_t m_ids;  /** 创建视图时的活跃事务id列表*/
   
    trx_id_t m_low_limit_no;  /** 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOG,
    * 如果其他视图也不需要,则可以删除小于m_low_limit_no的UNDO LOG*/

    bool m_closed;    /** 标记视图是否被关闭*/
// 省略...
};
  • purge -- mysql中的一个线程,进行数据刷新,undo log的清空等

总之,重要字段就是下面4个:

m_ids

ids_t 集合类型

记录当前读视图结构创建时,系统中正在活跃的事务id列表

  • 正在活跃 = 处于执行中的事务

up_limit_id 

记录m_ids列表中的最小事务id

low_limit_id

当前读视图生成时,系统尚未分配的下一个事务id

  • 也就是目前系统中已经出现过的最大事务id+1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值