悔棋.

undo 日志记录了一些进行 undo 操作的元数据.

事务id

给事务分配id的时机

在只读事务中不可以对普通的表(其他事务也能访问到的表)进行增、删、改操作,但可以对临时表做增、删、改操作。

如果某个事务执行过程中对某个表执行了增、删、改操作,那么InnoDB存储引擎就会给它分配一个独一无二的事务id,分配方式如下:

  • 对于只读事务来说,只有在它第一次对某个用户创建的临时表执行增、删、改操作时才会为这个事务分配一个事务id,否则的话是不分配事务id
  • 对于读写事务来说,只有在它第一次对某个表(包括用户创建的临时表)执行增、删、改操作时才会为这个事务分配一个事务id,否则的话也是不分配事务id

有的时候虽然我们开启了一个读写事务,但是在这个事务中全是查询语句,并没有执行增、删、改的语句,那也就意味着这个事务并不会被分配一个事务id。

事务id是怎么生成的

  • 服务器会在内存中维护一个全局变量,每当需要为某个事务分配一个事务id时,就会把该变量的值当作事务id分配给该事务,并且把该变量自增1。
  • 每当这个变量的值为256的倍数时,就会将该变量的值刷新到系统表空间的页号为5的页面中一个称之为Max Trx ID的属性处,这个属性占用8个字节的存储空间。
  • 当系统下一次重新启动时,会将上边提到的Max Trx ID属性加载到内存中,将该值加上256之后赋值给我们前边提到的全局变量

trx_id隐藏列

一条记录在页面中的真实结构看起来就是这样的:

image.png

undo日志的格式

undo日志是被记录到类型为FIL_PAGE_UNDO_LOG 的页面中。这些页面可以从系统表空间中分配,也可以从一种专门存放undo日志的表空间,也就是所谓的undo tablespace中分配。

示例表

  1. CREATE TABLE undo_demo (
  2. id INT NOT NULL,
  3. key1 VARCHAR(100),
  4. col VARCHAR(100),
  5. PRIMARY KEY (id),
  6. KEY idx_key1 (key1)
  7. )Engine=InnoDB CHARSET=utf8;

可以通过系统数据库information_schema中的innodb_sys_tables表来查看某个表对应的table id是什么:

mysql> SELECT * FROM information_schema.innodb_sys_tables WHERE name = 'xiaohaizi/undo_demo';
+----------+---------------------+------+--------+-------+-------------+------------+---------------+------------+
| TABLE_ID | NAME                | FLAG | N_COLS | SPACE | FILE_FORMAT | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE |
+----------+---------------------+------+--------+-------+-------------+------------+---------------+------------+
|      138 | xiaohaizi/undo_demo |   33 |      6 |   482 | Barracuda   | Dynamic    |             0 | Single     |
+----------+---------------------+------+--------+-------+-------------+------------+---------------+------------+
1 row in set (0.01 sec)

INSERT操作对应的undo日志

undo insert 操作其实就是删除那条记录, 在写对应的undo日志时,主要是把这条记录的主键信息记上

image.png

  • undo no在一个事务中是从0开始递增的,也就是说只要事务没提交,每生成一条undo日志,那么该条日志的undo no就增1。
  • 如果记录中的主键只包含一个列,那么在类型为TRX_UNDO_INSERT_REC的undo日志中只需要把该列占用的存储空间大小和真实值记录下来,如果记录中的主键包含多个列,那么每个列占用的存储空间大小和对应的真实值都需要记录下来

向undo_demo中插入两条记录:

BEGIN;  # 显式开启一个事务,假设该事务的id为100

# 插入两条记录
INSERT INTO undo_demo(id, key1, col) 
    VALUES (1, 'AWM', '狙击枪'), (2, 'M416', '步枪');

本例中插入了两条记录,所以会产生两条类型为TRX_UNDO_INSERT_REC的undo日志:

  • 第一条undo日志的undo no为0,记录主键占用的存储空间长度为4,真实值为1。

image.png

  • 第二条undo日志的undo no为1,记录主键占用的存储空间长度为4,真实值为2。

image.png

roll_pointer隐藏列的含义

本质上就是一个指向记录对应的undo日志的一个指针。

image.png