【问题标题】:innodb full table lock during simple insert简单插入期间的innodb全表锁
【发布时间】:2019-08-15 13:19:10
【问题描述】:

我们的代码可以

insert into user (email, name) values ((),()) // 1000 rows at a time, without specifying primary key value

这样的插入在多台服务器上并行运行并且具有高并发性(有时甚至在 2 个并行),其中许多由于锁定等待超时而失败。 Show innodb status 显示如下

 mysql tables in use 1, locked 1
2592 lock struct(s), heap size 319696, 109498 row lock(s), undo log entries 51785
MySQL thread id 1623383, OS thread handle 47552100853504, query id 1459767730 10.250.232.11 admin update
insert into user (account_id, email, first_name, manager_id) values (....)
TABLE LOCK table `user` trx id 1952681605 lock mode IX
RECORD LOCKS space id 2217 page no 1034274 n bits 280 index usertbl_email_account_id_unique_idx of table `user` trx id 1952681605 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

RECORD LOCKS space id 2217 page no 273318 n bits 280 index usertbl_email_account_id_unique_idx of table `user` trx id 1952681605 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 33 PHYSICAL RECORD: n_fields 3; compact format; info bits 32
 0: len 30; hex 647368656e2b31303037314061646f6265746573742e636f6d2020202020; asc dummy+10071@dummy.com     ; (total 64 bytes);
 1: len 4; hex 000004c3; asc     ;;
 2: len 4; hex 01953758; asc   7X;;


 TABLE LOCK table `account` trx id 1952681605 lock mode IS
 RECORD LOCKS space id 1954 page no 617 n bits 72 index PRIMARY of table `account` trx id 1952681605 lock mode S locks rec but not gap
 Record lock, heap no 2 PHYSICAL RECORD: n_fields 40; compact format; info bits 0
  0: len 4; hex 000004c3; asc     ;;
  1: len 6; hex 000070666e53; asc   pfnS;;
  2: len 7; hex 020000100b1b33; asc       3;;
  3: len 30; hex 396236396235306633656561343536373938343064616637323762316534; asc 9b69b50f3eea45679840daf727b1e4; (total 32 bytes);
  4: len 1; hex 61; asc a;;
  5: SQL NULL;
  6: len 5; hex 496e646961; asc ;;
  7: SQL NULL;
  8: SQL NULL;

我们在 mysql 8.0.13 上,innodb_autoinc_lock_mode 设置为引擎默认(2 交错),这是最轻松的模式。 我们在代码中没有使用 MySQL 级别的 LOCK TABLE,所以我们很困惑为什么 innodb 在 IX 模式下会锁定完整的用户表? 表格如下所示

CREATE TABLE user (
  `user_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `account_id` INT UNSIGNED NOT NULL,
  `email` CHAR(64) NOT NULL,
  `first_name` VARCHAR(63) NOT NULL,
  `manager_id` INT UNSIGNED NOT NULL
  PRIMARY KEY (`user_id`),
  INDEX `usertbl_account_id_fk_idx` (`account_id` ASC),
  INDEX `usertbl_user_id_account_id_idx` (`user_id` ASC, `account_id` ASC, `first_name` ASC) COMMENT 'To server as a FK into other tables.',
  UNIQUE INDEX `usertbl_email_account_id_unique_idx` (`email` ASC, `account_id` ASC)
 CONSTRAINT `usertbl_account_id_fk`
    FOREIGN KEY (`account_id`)
    REFERENCES `account` (`account_id`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
 CONSTRAINT `usertbl_manager_id_fk_idx`
    FOREIGN KEY (`manager_id` , `account_id`)
    REFERENCES `user` (`user_id` , `account_id`)
    ON DELETE RESTRICT
    ON UPDATE CASCADE)
ENGINE = InnoDB;

usertbl_user_id_account_id_idx 是必需的,因为很少有表在用户表中具有外键,如下所示

CREATE TABLE `course_author` (
  `course_id` INT UNSIGNED NOT NULL,
  `author_id` INT UNSIGNED NOT NULL,
  `account_id` INT UNSIGNED NOT NULL,
  `first_name` VARCHAR(63) NOT NULL,
  PRIMARY KEY (`course_id`, `author_id`),
  INDEX `catbl_author_id_fk_idx` (`author_id` ASC, `account_id` ASC) COMMENT 'For searching courses of an author' ,
  CONSTRAINT `catbl_author_fk`
    FOREIGN KEY (`author_id` , `account_id`, `first_name`)
    REFERENCES `user` (`user_id` , `account_id`, `first_name`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

【问题讨论】:

  • 请提供完整的SHOW CREATE TABLE。 A_I 列必须是某个索引中的第一个。
  • 感谢 Rick,已添加相关详细信息。是的,user_id 在索引 usertbl_user_id_account_id_idx 中排在第一位。这个索引是否被锁定?删除所有以 user_id 作为第一列(不是第二列或更远)的索引会解决锁定问题吗?
  • 您在(manager_id, account_id) 上有一个 FK,但我没有看到这个 FK 的索引 - 也许这是(其中一个)问题?
  • usertbl_user_id_account_id_idx 索引将用作其在 user_id、account_id 上的索引
  • “高并发”——有多“高”? SHOW GLOBAL STATUS LIKE 'Max_used_connections'; 是什么?

标签: mysql innodb table-locking table-lock


【解决方案1】:

我假设你问的是这些:

TABLE LOCK table `user` trx id 1952681605 lock mode IX

TABLE LOCK table `account` trx id 1952681605 lock mode IS

IX 和 IS 锁是阻止其他表锁的表锁,例如来自 ALTER TABLE 或 DROP TABLE 或 CREATE TRIGGER 等的锁。这些称为元数据锁。

针对表的任何 SELECT/INSERT/UPDATE/DELETE 都会获取元数据锁,因此在您查询表时,不会有其他会话尝试 ALTER 或 DROP 表。但是这些元数据锁不会互相阻塞;多个会话仍然可以对同一个表执行并发的非 DDL 查询,而不必相互干扰,除非它们在同一行上发生冲突。

INSERT to user 导致account 上的 IS 元数据锁定的原因是从 useraccount 的外键引用。您正在插入通过外键引用account 表的行,因此在您的事务完成之前不得更改或删除account 表。

【讨论】:

  • 对了,我指的是“TABLE LOCK table usertrx id 1952681605 lock mode IX”这是否意味着整个用户表都被锁定在X模式下?
  • 它锁定表以阻止像 ALTER 或 DROP 这样的元数据操作。但它不会针对并发的 SELECT/INSERT/UPDATE/DELETE 语句锁定表。见dev.mysql.com/doc/refman/8.0/en/metadata-locking.html
【解决方案2】:

PRIMARY KEY 启动二级索引几乎没有用处; DROP INDEX usertbl_user_id_account_id_idx。注意:PRIMARY KEY 一个索引。

您说“作为 FK 服务到其他表中。” -- 让我们看看其他表。

请提供更多SHOW ENGINE INNODB STATUS; -- 你提供的一行是不够的。

【讨论】:

  • 在问题中添加了两个细节
猜你喜欢
  • 2017-10-05
  • 1970-01-01
  • 1970-01-01
  • 2010-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-10
相关资源
最近更新 更多