MariaDB - “ERROR 1205 ... Lock wait timeout exceeded; ...” 执行影响不同行的多个并行更新时

Mar*_*oni 5 mysql mariadb mariadb-10.1

我们使用 MariaDB 10.1.19 托管旧数据库。我们有一个lpr表(在 InnoDB 上),我们想在其中从文本列移动gate到规范化外键gate_id。我们的代码并行执行 UPDATE,如下所示:

UPDATE lpr SET gate_id=1 WHERE gate_id IS null AND gate LIKE '%[1]%'
UPDATE lpr SET gate_id=2 WHERE gate_id IS null AND gate LIKE '%[2]%'
UPDATE lpr SET gate_id=3 WHERE gate_id IS null AND gate LIKE '%[3]%'
UPDATE lpr SET gate_id=4 WHERE gate_id IS null AND gate LIKE '%[4]%'
...
Run Code Online (Sandbox Code Playgroud)

由于受所有 UPDATE 影响的行是不相交的,并且表位于 InnoDB 上,我们不会期望锁争用,但我们得到错误:

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

对于所有更新。提高innodb_lock_wait_timeout到100〜并不能改变什么,以及设置FOREIGN_KEY_CHECKS为0。

gate列永远不会包含多个[VALUE]字符串。

如果我们手动执行单个 UPDATE,则不会发生此类错误。

SHOW FULL PROCESSLIST在更新期间执行 a不会显示任何锁定。

我们做错了什么?即使使用 InnoDB 也有表级锁吗?或者 UPDATE 锁定的行是否比 WHERE 子句严格选择的行多?

jyn*_*nus 9

我们做错了什么?即使使用 InnoDB 也有表级锁吗?

您的问题是缺少合适的索引来使用。InnoDB 执行next-key 锁定,这意味着它只会锁定它将更新的行,而且还会使用查找索引锁定中间的间隙。因为没有合适的索引可用于给定的过滤器,gate LIKE '%[1]%'所以它在技术上不会做表锁,但它会根据查询计划(锁定所有行)在每个单行间隙上设置锁。

我重新创建了您的结构,并SHOW ENGINE INNODB STATUS为我们提供了我们需要的所有信息:

mysql> create table lpr (gate_id int, gate varchar(10));
Query OK, 0 rows affected (0.21 sec)

mysql> insert into lpr values (1, 'wqer[1]sd');
Query OK, 1 row affected (0.11 sec)

mysql> insert into lpr values (2, 'wqer[2]sd');  
Query OK, 1 row affected (0.06 sec)

mysql> insert into lpr values (3, 'wqer[2]sd'); 
Query OK, 1 row affected (0.03 sec)

mysql> insert into lpr values (4, 'wqer[4]sd');  
Query OK, 1 row affected (0.05 sec)

mysql> insert into lpr values (5, 'wqer[5]sd');  
Query OK, 1 row affected (0.09 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE lpr SET gate_id=1 WHERE gate_id IS null AND gate LIKE '%[1]%';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0

mysql> SHOW ENGINE INNODB STATUS\G
...
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 4126, ACTIVE 8 sec
2 lock struct(s), heap size 1136, 6 row lock(s)
MySQL thread id 8, OS thread handle 140628280551168, query id 21 localhost root starting
SHOW ENGINE INNODB STATUS
Run Code Online (Sandbox Code Playgroud)

在另一个会话中:

mysql> UPDATE lpr SET gate_id=2 WHERE gate_id IS null AND gate LIKE '%[2]%'
    -> ;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

---TRANSACTION 4127, ACTIVE 8 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 9, OS thread handle 140628236515072, query id 28 localhost root updating
UPDATE lpr SET gate_id=2 WHERE gate_id IS null AND gate LIKE '%[2]%'
------- TRX HAS BEEN WAITING 8 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 4 page no 4 n bits 72 index GEN_CLUST_INDEX of table `enwiki`.`lpr` trx id 4127 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 6; hex 000000000200; asc       ;;
 1: len 6; hex 000000001011; asc       ;;
 2: len 7; hex 82000001060110; asc        ;;
 3: len 4; hex 80000001; asc     ;;
 4: len 9; hex 777165725b315d7364; asc wqer[1]sd;;
Run Code Online (Sandbox Code Playgroud)

您有多种选择:

  • 通过放宽事务隔离级别来提高并发性(但可能会发生鬼读):

    mysql> SET SESSION transaction_isolation='READ-COMMITTED';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> begin;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> UPDATE lpr SET gate_id=1 WHERE gate_id IS null AND gate LIKE '%[1]%';
    Query OK, 0 rows affected (0.00 sec)
    Rows matched: 0  Changed: 0  Warnings: 0
    
    Run Code Online (Sandbox Code Playgroud)

    在另一个会话上,现在会话成功:

    mysql> SET SESSION transaction_isolation='READ-COMMITTED';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> BEGIN;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> UPDATE lpr SET gate_id=2 WHERE gate_id IS null AND gate LIKE '%[2]%'
    -> ;
    Query OK, 0 rows affected (0.00 sec)
    Rows matched: 0  Changed: 0  Warnings: 0
    
    Run Code Online (Sandbox Code Playgroud)
  • 改进您的设计和/或查询,以便您只使用正确索引的查询来提高并发性。

更多关于并发和间隙锁定:https : //www.percona.com/blog/2012/03/27/innodbs-gap-locks/