为什么 `select ... for update` 语句锁定的行多于总行数?

Why `select ... for update` statement locks more rows then total number of rows?

我用的是mysql5.6。而我运行下面的查询。

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `passport_id` varchar(20) NOT NULL,
  `age` int(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `passport_id` (`passport_id`),
  KEY `idx_user_age` (`age`)
) ENGINE=InnoDB ;

insert into user values(1, 'A', 'A1', 1), (2, 'B', 'B1', 1), (3, 'C', 'C3', 1), 
(4, 'A', 'A4', 2), (5, 'B', 'B5', 2), (6, 'C', 'C6', 2);

set autocommit = 0;
select * from user where age = 1 for update;

我希望上面的 select...for update 查询锁定 3 行。因为查询了returns3行。但是select * from information_schema.innodb_trx; 查询的trx_rows_locked数据是7条,连总行数是6条。

另外,下面查询returns只有一行。但是 trx_rows_locked 是 2.

select * from user where age = 1 and passport_id = 'A1' for update;

Mysql 文档解释 trx_row_locked 这样 The approximate number or rows locked by this transaction. 。但是我不明白为什么 trx_row_locked 有大概的数字。 我怎样才能得到正确的锁定行数?

trx_rows_locked确实是本次交易的大概record locks个数,注意不是table中的行记录数。由于 delete-marked 条记录可能会被删除,因此记录数将不准确。

select * from user where age = 1 for update;

使用完整的table扫描,我们可以通过explain看到执行计划,因此它锁定了所有table,6条记录,加上一个supremum record(其实就是一个gap lock),所以trx_rows_locked = 6 + 1 = 7

select * from user where age = 1 and passport_id = 'A1' for update;

使用唯一索引passport_id,因此InnoDB会在passport_id索引上获取记录锁,在PK上获取记录锁,所以trx_rows_locked = 2