在InnoDB存储引擎中,事务id是用来标识事务的唯一数字标识,每个事务都有一个唯一的事务id。在mvcc、锁等很多场景中,我们都能看到事务id的影子。
那么,事务id是什么时候被分配的呢?其实是在执行第一条语句的时候分配的,准确的说,是执行第一条写语句或锁定读语(SELECT ... FOR UPDATE)句时执行的。

下面来做一些小测验,验证一下事务id的分配时机。
设有一张t2表:

CREATE TABLE `t2` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `c` int DEFAULT NULL,
  `d` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

表中有一条记录:

insert into t2 (id,c,d) values (1,11,3);

现在开启一个事务:

begin; -- time0
select * from t2 where id=1; -- time1
select * from t2 where id=1 for share; -- time2
select * from t2 where id=1 for update; -- time3

我们可以通过查询information_schema.innodb_trx表来查看当前事务的信息:

select trx_id, trx_state, trx_started, trx_mysql_thread_id from information_schema.innodb_trx\G

在各个不同的时间点查询,结果如下:

  • time0:
    innodb_trx_id_0
  • time1:
    innodb_trx_id_1
  • time2:
    innodb_trx_id_2
  • time3:
    innodb_trx_id_3
    我们可以看到,在执行begin语句的时候,查询结果为空,这个时候并没有真正开启事务。而在执行第一条select语句的时候(time0),分配了一个
    事务id 281479752323760,在time3,事务id变成了1036864。其实,事务id是一个递增值,在time1的时候,分配的是一个虚拟的事务id,真正
    的事务id是在time3的时候分配的。

其实,关于这个话题,在mysql官方手册中,是这样描述的(见8.5.3):

A transaction ID is only needed for a transaction that might perform write operations or locking reads such as SELECT ... FOR UPDATE.

大意是:

只有在执行写操作或锁定读(SELECT ... FOR UPDATE)语句时,才需要事务id。

本文由mdnice多平台发布


guoqiang
1 声望0 粉丝

从事开发工作多年,使用过php和golang,目前主要使用golang开发后端服务。