We're here to help!

MySQL: Error: Lock wait timeout exceeded; try restarting transaction

Follow
Problem Description

The following conflict is seen in Dbvisit Replicate when replicating to MySQL:

OE.INVENTORIES at transaction 0010.002.00000317 at SCN 6673008
SQL text (with replaced bind values): update OE."INVENTORIES"
set QUANTITY_ON_HAND = 95435594
where (1=1)
and QUANTITY_ON_HAND = 95435595
and PRODUCT_ID = 587
and WAREHOUSE_ID = 56

Error: Lock wait timeout exceeded; try restarting transaction

If we log into MySQL we can see the actual lock:

mysql> SHOW ENGINE INNODB STATUS;
...
...
---TRANSACTION 0 18614, ACTIVE 21383 sec, process no 22126, OS thread id 1096927552 starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 14320
MySQL thread id 15, query id 2433 dbvlin702.dbvisit.co.nz 172.16.1.72 root Updating
update OE.INVENTORIES
set QUANTITY_ON_HAND = '95435594'
where (1=1)
and QUANTITY_ON_HAND = '95435595'
and PRODUCT_ID = '587'
and WAREHOUSE_ID = '56'
------- TRX HAS BEEN WAITING 193 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 192 n bits 376 index GEN\_CLUST\_INDEX of table OE/INVENTORIES trx id 0 18614 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 6; hex 000000000334; asc 4;; 1: len 6; hex 000000002735; asc '5;; 2: len 7; hex 80000001350110; asc 5 ;; 
3: len 8; hex 8000000000000001; asc ;; 4: len 8; hex 800000000000005b; asc [;; 5: len 8; hex 800000000339a203; asc 9 ;;

It can also be seen in the MySQL process list:

mysql> show processlist;
+----+------+-------------------------------+--------+---------+-------+----------+------------------------------------------------------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info|
+----+------+-------------------------------+--------+---------+-------+----------+------------------------------------------------------------------------------------------------------+
| 11 | root | dbvlin702.dbvisit.co.nz:29078 | dbvrep | Sleep | 18564 | | NULL | 
| 12 | root | dbvlin702.dbvisit.co.nz:29080 | dbvrep | Sleep | 301 | | NULL | 
| 13 | root | dbvlin702.dbvisit.co.nz:29088 | dbvrep | Sleep | 42358 | | NULL | 
| 14 | root | dbvlin702.dbvisit.co.nz:29089 | dbvrep | Query | 301 
| Updating | update OE.INVENTORIES set QUANTITY_ON_HAND = '95435594' where (1=1) and QUANTITY_ON_HAND = '95435595 | 
| 16 | root | dbvlin702.dbvisit.co.nz:44135 | dbvrep | Sleep | 18564 | | NULL | 
| 18 | root | localhost | OE | Query | 0 | NULL | show processlist |
+----+------+-------------------------------+--------+---------+-------+----------+------------------------------------------------------------------------------------------------------+
6 rows in set (0.00 sec)
Solution

The issue is due to an internally created index called GEN_CLUSTER_INDEX for InnoDB. This basically means there was NO explicit Primary Key on the InnoDB table and InnoDB was creating its own internal Primary Key. The underlying issue seems to be related to gap locks on indexes which is described here: http://www.mysqlperformanceblog.com/2012/03/27/innodbs-gap-locks/

This problem can be resolved by creating a primary key (PK) on the OE.INVENTORIES table:

mysql> alter table OE.INVENTORIES ADD PRIMARY KEY (PRODUCT_ID,WAREHOUSE_ID);
Query OK, 901457 rows affected (10.06 sec)
Records: 901457 Duplicates: 0 Warnings: 0
mysql> show index in OE.INVENTORIES;
+-------------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| INVENTORIES | 0 | PRIMARY | 1 | PRODUCT_ID | A | 1643| NULL | NULL | | BTREE | | 
| INVENTORIES | 0 | PRIMARY | 2 | WAREHOUSE_ID | A | 900821 | NULL | NULL | | BTREE | | 
+-------------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.01 sec)

If this does not work, then also set the transaction isolation to read committed (like Oracle). Edit /etc/my.cnf:

transaction-isolation = READ-COMMITTED
innodb_locks_unsafe_for_binlog = 1 # Optional

And restart mysql:

/etc/init.d/mysqld restart

Arjen Visser  August 26, 2013 17:48

Have more questions? Submit a request

Comments