【问题标题】:Why `select ... for update` statement locks more rows then total number of rows?为什么`select ... for update`语句锁定更多行而不是总行数?
【发布时间】:2020-12-15 11:24:33
【问题描述】:

我使用 mysql 5.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 行。因为查询返回 3 行。但是select * from information_schema.innodb_trx; 查询的trx_rows_locked数据是7,连总行数也是6。

另外,下面的查询只返回一行。但是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 有大概的数字。 以及如何获得正确的锁定行数?

【问题讨论】:

  • 也许:(1)两个唯一键; (2)“间隙锁定”
  • 能否详细解释一下
  • 你能显示select ... from innodb_trx的完整结果吗

标签: mysql database innodb


【解决方案1】:

trx_rows_locked确实是这个事务的record locks的大概个数,注意这个计数不是表中的行记录。由于delete-marked 记录可能会被删除,因此记录数将不准确。

select * from user where age = 1 for update;

使用全表扫描,我们可以通过explain看到执行计划,因此它锁定了所有的表,6条记录,加上一个supremum记录(实际上是一个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

【讨论】:

  • 感谢您的回答。但我还有一个问题。为什么select * from user where age = 1 for update 语句使用全表扫描? age 列具有非唯一索引。所以我想它应该使用 ref 类型。
  • 这是因为表中有3条age = 1的记录(50%在表中),MySQL认为使用全表扫描比二级索引快。
  • 更多详情请见Avoiding Full Table Scans
猜你喜欢
  • 2011-10-05
  • 2019-01-29
  • 1970-01-01
  • 2016-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
相关资源
最近更新 更多