Related
I have very simple table:
CREATE TABLE `d` (
`id` int(11) DEFAULT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
without records:
select * from d;
Empty set (0,01 sec)
Then I try to open two transactions in different sessions:
Session #1:
begin;
Query OK, 0 rows affected (0,00 sec)
select * from d where id = 100 for update;
Empty set (0,00 sec)
Session #2:
begin;
Query OK, 0 rows affected (0,00 sec)
select * from d where id = 700 for update;
Empty set (0,00 sec)
Now I try to insert new record in Session #2 and session "freezes":
insert into d values (700);
And when I try to do the same (with another id field) in Session #1 it crashes:
insert into d values (100); --> ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction in Session #1
insert into d values (700); --> Query OK, 1 row affected (4,08 sec) in Session #2
How can I to fix the deadlock? InnoDB status is:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2017-07-06 15:59:25 0x70000350d000
*** (1) TRANSACTION:
TRANSACTION 43567, ACTIVE 15 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 4, OS thread handle 123145358217216, query id 89 localhost root update
insert into d values (700)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43567 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 43568, ACTIVE 7 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 3, OS thread handle 123145357938688, query id 90 localhost root update
insert into d values (100)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
This deadlock error is a bug in the MySQL InnoDB engine that has not been fixed for 12 years. (Bug #25847: https://bugs.mysql.com/bug.php?id=25847, workaround: How do I lock on an InnoDB row that doesn't exist yet?) It is not related to Unique Key. Running this query will also result in the same Deadlock error.
Session #1: CREATE TABLE t (id int) ENGINE=InnoDB;
Session #1: SET AUTOCOMMIT = 0;
Session #1: SELECT id FROM t WHERE id = 1 FOR UPDATE;
Session #2: SET AUTOCOMMIT = 0;
Session #2: SELECT id FROM t WHERE id = 2 FOR UPDATE;
Session #1: INSERT INTO t (id) VALUES (1); -- Hang
Session #2: INSERT INTO t (id) VALUES (2); -- Session #1: OK, Session #2: Deadlock found when trying to get lock; try restarting transaction
InnoDB Status is the same:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-10-24 00:25:31 0x1638
*** (1) TRANSACTION:
TRANSACTION 1287, ACTIVE 62 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 7, OS thread handle 9444, query id 143 localhost ::1 root update
INSERT INTO t (id) VALUES (1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 23 page no 3 n bits 72 index GEN_CLUST_INDEX of table `test`.`t` trx id 1287 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 1288, ACTIVE 19 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 9, OS thread handle 5688, query id 145 localhost ::1 root update
INSERT INTO t (id) VALUES (2)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 23 page no 3 n bits 72 index GEN_CLUST_INDEX of table `test`.`t` trx id 1288 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 23 page no 3 n bits 72 index GEN_CLUST_INDEX of table `test`.`t` trx id 1288 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
Note that starting with MySQL 8.0.1,
the performance schema exposes innodb data locks.
See https://dev.mysql.com/doc/refman/8.0/en/data-locks-table.html
See https://dev.mysql.com/doc/refman/8.0/en/data-lock-waits-table.html
In this example, after the first select in session 1 alone the locks are:
mysql> select * from performance_schema.data_locks \G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 1808:76
ENGINE_TRANSACTION_ID: 1808
THREAD_ID: 35
EVENT_ID: 13081
OBJECT_SCHEMA: test
OBJECT_NAME: d
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 139756088373592
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 1808:2:5:1
ENGINE_TRANSACTION_ID: 1808
THREAD_ID: 35
EVENT_ID: 13111
OBJECT_SCHEMA: test
OBJECT_NAME: d
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: id
OBJECT_INSTANCE_BEGIN: 139756088370552
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: supremum pseudo-record <--- HERE
2 rows in set (0.00 sec)
This is not a solution to the deadlock itself, but having visibility on the locks taken goes a long way to understand the problem.
Here, both SELECT FOR UPDATE lock the "suprenum" record, because id 100 and 700 are greater than the biggest ID in the table (it's empty).
Once more records are present (say at ID = 500), both queries should execute concurrently, as a different gap in IDs will be locked.
I suspect the deadlock happens because InnoDB is conservative on dealing with "gaps". Note that 100 and 700 are both in the same nebulous area of untouched land. InnoDB can't (or at least does not) deal with the fact that "100" and "700" are different. InnoDB would like to tag individual rows but there are no rows already in the table with those ids.
Transaction 2 was probably going to timeout (see innodb_lock_wait_timeout). When you poked Transaction 1 a second time with #2 still wanting a lock, InnoDB punted and gave up.
Bottom Line: Live with deadlocks. When they happen, start back at the BEGIN. You have found yet-another obscure case where an unnecessary deadlock happens.
Furthermore, I suspect that fixing the code for this case would slow down most other cases, and lead to a series of errors that would take several releases to discover and fix.
Table:
create table properties
(
id int auto_increment primary key,
other_id int null
);
create index index_properties_on_other_id
on properties (other_id);
TX 1:
start transaction;
SET #last_id = 1;
delete from `properties` WHERE `properties`.`other_id` = #last_id;
INSERT INTO `properties` (`other_id`) VALUES (#last_id);
commit
TX 2:
start transaction;
SET #last_id = 2;
delete from `properties` WHERE `properties`.`other_id` = #last_id;
INSERT INTO `properties` (`other_id`) VALUES (#last_id);
commit
Assume table is empty prior to running transactions.
My application has 2 use cases. Sometimes last_id would already be used by another row, hence it would be indexed prior; but sometimes it would be generated in same transaction by a previous insert query, and in that case I get a deadlock.
I need to run both transactions until after delete statement. And when I run insert on tx1, it waits to get a lock, then I run insert on tx2, tx2 gets a deadlock and rollsback.
mysql | LATEST DETECTED DEADLOCK
mysql | ------------------------
mysql | 2019-06-03 21:01:05 0x7f0ba4052700
mysql | *** (1) TRANSACTION:
mysql | TRANSACTION 320051, ACTIVE 12 sec inserting
mysql | mysql tables in use 1, locked 1
mysql | LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
mysql | MySQL thread id 286, OS thread handle 139687839577856, query id 17804 172.18.0.1 root update
mysql | INSERT INTO `properties` (`other_id`) VALUES (#last_id)
mysql | *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
mysql | RECORD LOCKS space id 1524 page no 4 n bits 72 index index_properties_on_other_id of table `properties` trx id 320051 lock_mode X insert intention waiting
mysql | Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
mysql | 0: len 8; hex 73757072656d756d; asc supremum;;
mysql |
mysql | *** (2) TRANSACTION:
mysql | TRANSACTION 320052, ACTIVE 8 sec inserting
mysql | mysql tables in use 1, locked 1
mysql | 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
mysql | MySQL thread id 287, OS thread handle 139687973168896, query id 17814 172.18.0.1 root update
mysql | INSERT INTO `properties` (`other_id`) VALUES (#last_id)
mysql | *** (2) HOLDS THE LOCK(S):
mysql | RECORD LOCKS space id 1524 page no 4 n bits 72 index index_properties_on_other_id of table `properties` trx id 320052 lock_mode X
mysql | Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
mysql | 0: len 8; hex 73757072656d756d; asc supremum;;
mysql |
mysql | *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
mysql | RECORD LOCKS space id 1524 page no 4 n bits 72 index index_properties_on_other_id of table `properties` trx id 320052 lock_mode X insert intention waiting
mysql | Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
mysql | 0: len 8; hex 73757072656d756d; asc supremum;;
mysql |
mysql | *** WE ROLL BACK TRANSACTION (2)
Status of locks after delete statements:
mysql | ---TRANSACTION 320066, ACTIVE 90 sec
mysql | 2 lock struct(s), heap size 1136, 1 row lock(s)
mysql | MySQL thread id 287, OS thread handle 139687973168896, query id 18076 172.18.0.1 root
mysql | TABLE LOCK table `properties` trx id 320066 lock mode IX
mysql | RECORD LOCKS space id 1524 page no 4 n bits 72 index index_properties_on_other_id of table `properties` trx id 320066 lock_mode X
mysql | Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
mysql | 0: len 8; hex 73757072656d756d; asc supremum;;
mysql |
mysql | ---TRANSACTION 320065, ACTIVE 95 sec
mysql | 2 lock struct(s), heap size 1136, 1 row lock(s)
mysql | MySQL thread id 286, OS thread handle 139687839577856, query id 18039 172.18.0.1 root
mysql | TABLE LOCK table `properties` trx id 320065 lock mode IX
mysql | RECORD LOCKS space id 1524 page no 4 n bits 72 index index_properties_on_other_id of table ``properties` trx id 320065 lock_mode X
mysql | Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
mysql | 0: len 8; hex 73757072656d756d; asc supremum;;
So two transactions are deleting/inserting different other_ids, I wasn't expecting them to get into a deadlock. I want to learn why exactly this is happening.
MySQL does not lock something that is not there, e.g. locks rows that you didn't delete. It also does not store that you tried to delete rows with the specific value "1". What it does instead is that it marks the space where the "1" should have been if it would have been there, and locks it with a gap lock, which has the following characteristics:
Gap locks in InnoDB are “purely inhibitive”, which means that their only purpose is to prevent other transactions from inserting to the gap. Gap locks can co-exist. A gap lock taken by one transaction does not prevent another transaction from taking a gap lock on the same gap. There is no difference between shared and exclusive gap locks. They do not conflict with each other, and they perform the same function.
In an empty table, the place that the 1 would have been is "anywhere in the table" (or anywhere from the start to the "supremum" mentioned in the deadlock) - which is consequently gaplocked by the delete. The same is true for the 2. And those locks do not conflict each other by definition.
But the insert does. The first insert will have to wait for the gaplock that the second transaction issued for its delete. If the second transaction now tries to also insert into the gap, this would require the gaplock from the first transaction to be lifted, but this cannot happen because the first transaction already waits for the second gaplock to be lifted. So you get a deadlock.
Once you fill your table, this will happen less often, as the gaplocks do not need to span the whole table anymore. If you e.g. already have other_id 1 and 3 in your table, delete/inserting value 2 and 4 will not deadlock each other.
In general, empty tables are a rarity, and you cannot and should not infer anything from such a special case to the normal behaviour. You basically have to accept edge cases:
Gap locks are part of the tradeoff between performance and concurrency
So in the general use case, you just need to be prepared that occasionally a deadlock may happen (and then repeat the transaction). If your expected use case is that you have a basically empty table, or are mostly adding at the end of the values, or oftentimes add 2 values into the same gap, you may need a different solution (and should ask a question on how to proceed in this specific use case). You may e.g. be able to use a unique index (that does not require gap locks), recode/hash your value to sit randomly in the index, or you let all transactions lock something that you know exists, so they wait for each other.
I use JMeter to test my program, somehow the total response number is stopping increasing, then I find out there is a deadlock in MySQL. I don't understand what's meaning of below log. Seems that transaction(2) owned a S lock and trying to own a X lock of the same table. Does this cause the deadlock? If so, why will this happen?
ATEST DETECTED DEADLOCK
------------------------
2019-01-02 14:38:27 0x70000f30a000
*** (1) TRANSACTION:
TRANSACTION 24004, ACTIVE 0 sec inserting
mysql tables in use 2, locked 2
LOCK WAIT 5 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2
MySQL thread id 11953, OS thread handle 123145549275136, query id 418447 localhost 127.0.0.1 username executing
INSERT INTO MRBS_SCHEDULE (ID,START_TIME,END_TIME,ROOM_ID,CREATE_BY,PRESIDE,REPEAT_ID,DESCRIPTION,NUM,TITLE,PRESIDE_EMAIL,PROJECTOR,CONFERENCE_CALL,CREATE_ID,BOOK_TIME,END_TYPE,EXPECTED_END_TIME) select null,'2019-01-03 19:53:00','2019-01-03 19:53:00',10113558,'d','d',12245755,'fdsfds',10,null,'d#sh.ff.com',0,0,10227622,'2019-01-02 14:38:27.358',0,'2019-01-03 19:53:00' from dual WHERE NOT EXISTS (SELECT * FROM MRBS_SCHEDULE ms where ms.START_TIME<'2019-01-03 19:53:00' and ms.END_TIME>'2019-01-03 19:53:00' and ms.ROOM_ID=10113558)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 55 page no 228 n bits 872 index IND_MRBS_SCHEDULE_END_TIME of table `meeting`.`mrbs_schedule` trx id 24004 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 24005, ACTIVE 0 sec inserting
mysql tables in use 2, locked 2
5 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2
MySQL thread id 11940, OS thread handle 123145557155840, query id 418448 localhost 127.0.0.1 username executing
INSERT INTO MRBS_SCHEDULE (ID,START_TIME,END_TIME,ROOM_ID,CREATE_BY,PRESIDE,REPEAT_ID,DESCRIPTION,NUM,TITLE,PRESIDE_EMAIL,PROJECTOR,CONFERENCE_CALL,CREATE_ID,BOOK_TIME,END_TYPE,EXPECTED_END_TIME) select null,'2019-01-03 19:54:00','2019-01-03 19:54:00',10113685,'z','z',12245756,'fdsfds',10,null,'z#sz.ff.com',0,0,10227544,'2019-01-02 14:38:27.397',0,'2019-01-03 19:54:00' from dual WHERE NOT EXISTS (SELECT * FROM MRBS_SCHEDULE ms where ms.START_TIME<'2019-01-03 19:54:00' and ms.END_TIME>'2019-01-03 19:54:00' and ms.ROOM_ID=10113685)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 55 page no 228 n bits 872 index IND_MRBS_SCHEDULE_END_TIME of table `meeting`.`mrbs_schedule` trx id 24005 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;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 55 page no 228 n bits 872 index IND_MRBS_SCHEDULE_END_TIME of table `meeting`.`mrbs_schedule` trx id 24005 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
Edit: Table MRBS_SCHEDULE primary key use Auto_increment
Deadlock can occur anytime and it is normal behavior. It happens when transaction 1 tries to update Table B using Table A but at the same time Transaction 2 tries to update Table A using Table B.
basicly Transactions holds theirselves and since nobody wants to give up, deadlock occurs.
you can set deadlock threshold for your db using innodb_lock_wait_timeout
From the documentation,
When deadlock detection is enabled (the default), InnoDB automatically detects transaction deadlocks and rolls back a transaction or transactions to break the deadlock. InnoDB tries to pick small transactions to roll back, where the size of a transaction is determined by the number of rows inserted, updated, or deleted.
InnoDB is aware of table locks if innodb_table_locks = 1 (the default) and autocommit = 0, and the MySQL layer above it knows about row-level locks. Otherwise, InnoDB cannot detect deadlocks where a table lock set by a MySQL LOCK TABLES statement or a lock set by a storage engine other than InnoDB is involved. Resolve these situations by setting the value of the innodb_lock_wait_timeout system variable.
Reference: https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-detection.html
I have very simple table:
CREATE TABLE `d` (
`id` int(11) DEFAULT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
without records:
select * from d;
Empty set (0,01 sec)
Then I try to open two transactions in different sessions:
Session #1:
begin;
Query OK, 0 rows affected (0,00 sec)
select * from d where id = 100 for update;
Empty set (0,00 sec)
Session #2:
begin;
Query OK, 0 rows affected (0,00 sec)
select * from d where id = 700 for update;
Empty set (0,00 sec)
Now I try to insert new record in Session #2 and session "freezes":
insert into d values (700);
And when I try to do the same (with another id field) in Session #1 it crashes:
insert into d values (100); --> ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction in Session #1
insert into d values (700); --> Query OK, 1 row affected (4,08 sec) in Session #2
How can I to fix the deadlock? InnoDB status is:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2017-07-06 15:59:25 0x70000350d000
*** (1) TRANSACTION:
TRANSACTION 43567, ACTIVE 15 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 4, OS thread handle 123145358217216, query id 89 localhost root update
insert into d values (700)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43567 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 43568, ACTIVE 7 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 3, OS thread handle 123145357938688, query id 90 localhost root update
insert into d values (100)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
This deadlock error is a bug in the MySQL InnoDB engine that has not been fixed for 12 years. (Bug #25847: https://bugs.mysql.com/bug.php?id=25847, workaround: How do I lock on an InnoDB row that doesn't exist yet?) It is not related to Unique Key. Running this query will also result in the same Deadlock error.
Session #1: CREATE TABLE t (id int) ENGINE=InnoDB;
Session #1: SET AUTOCOMMIT = 0;
Session #1: SELECT id FROM t WHERE id = 1 FOR UPDATE;
Session #2: SET AUTOCOMMIT = 0;
Session #2: SELECT id FROM t WHERE id = 2 FOR UPDATE;
Session #1: INSERT INTO t (id) VALUES (1); -- Hang
Session #2: INSERT INTO t (id) VALUES (2); -- Session #1: OK, Session #2: Deadlock found when trying to get lock; try restarting transaction
InnoDB Status is the same:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-10-24 00:25:31 0x1638
*** (1) TRANSACTION:
TRANSACTION 1287, ACTIVE 62 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 7, OS thread handle 9444, query id 143 localhost ::1 root update
INSERT INTO t (id) VALUES (1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 23 page no 3 n bits 72 index GEN_CLUST_INDEX of table `test`.`t` trx id 1287 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 1288, ACTIVE 19 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 9, OS thread handle 5688, query id 145 localhost ::1 root update
INSERT INTO t (id) VALUES (2)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 23 page no 3 n bits 72 index GEN_CLUST_INDEX of table `test`.`t` trx id 1288 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 23 page no 3 n bits 72 index GEN_CLUST_INDEX of table `test`.`t` trx id 1288 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
Note that starting with MySQL 8.0.1,
the performance schema exposes innodb data locks.
See https://dev.mysql.com/doc/refman/8.0/en/data-locks-table.html
See https://dev.mysql.com/doc/refman/8.0/en/data-lock-waits-table.html
In this example, after the first select in session 1 alone the locks are:
mysql> select * from performance_schema.data_locks \G
*************************** 1. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 1808:76
ENGINE_TRANSACTION_ID: 1808
THREAD_ID: 35
EVENT_ID: 13081
OBJECT_SCHEMA: test
OBJECT_NAME: d
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: NULL
OBJECT_INSTANCE_BEGIN: 139756088373592
LOCK_TYPE: TABLE
LOCK_MODE: IX
LOCK_STATUS: GRANTED
LOCK_DATA: NULL
*************************** 2. row ***************************
ENGINE: INNODB
ENGINE_LOCK_ID: 1808:2:5:1
ENGINE_TRANSACTION_ID: 1808
THREAD_ID: 35
EVENT_ID: 13111
OBJECT_SCHEMA: test
OBJECT_NAME: d
PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
INDEX_NAME: id
OBJECT_INSTANCE_BEGIN: 139756088370552
LOCK_TYPE: RECORD
LOCK_MODE: X
LOCK_STATUS: GRANTED
LOCK_DATA: supremum pseudo-record <--- HERE
2 rows in set (0.00 sec)
This is not a solution to the deadlock itself, but having visibility on the locks taken goes a long way to understand the problem.
Here, both SELECT FOR UPDATE lock the "suprenum" record, because id 100 and 700 are greater than the biggest ID in the table (it's empty).
Once more records are present (say at ID = 500), both queries should execute concurrently, as a different gap in IDs will be locked.
I suspect the deadlock happens because InnoDB is conservative on dealing with "gaps". Note that 100 and 700 are both in the same nebulous area of untouched land. InnoDB can't (or at least does not) deal with the fact that "100" and "700" are different. InnoDB would like to tag individual rows but there are no rows already in the table with those ids.
Transaction 2 was probably going to timeout (see innodb_lock_wait_timeout). When you poked Transaction 1 a second time with #2 still wanting a lock, InnoDB punted and gave up.
Bottom Line: Live with deadlocks. When they happen, start back at the BEGIN. You have found yet-another obscure case where an unnecessary deadlock happens.
Furthermore, I suspect that fixing the code for this case would slow down most other cases, and lead to a series of errors that would take several releases to discover and fix.
I've created an application which gathers prices and stores it in a database.
The application runs with two threads. But now and then a deadlock occurs. I think it is because the treads insert a bulk of prices to the same table.
The table
[tripid | date | duration | price | garant | updatetime | persons | accommodationid]
The indexes
unique (tripid, date, duration, price, persons)
foreign key 'accommodationid' to table 'accommodation.id'
The query
insert into prices (tripid, date, duration, price, persons, accomodationid)
values ( 1 , 2016-6-4, 8, 200,2,32),
... a whole lot more ...
( 1 , 2016-7-4, 8, 200,2,32)
on duplicate key update price = values(price);
Becouse there can be 1000 values to insert in 1 query it takes some time. I think the deadlock occurs when the second thread wants to insert another 1000 values but the first one is not completed.
The innodb status
------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-03-16 23:13:13 0xef8
*** (1) TRANSACTION:
TRANSACTION 3732195928, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1
MySQL thread id 415, OS thread handle 7328, query id 10956855 ---(ip)-- --name-- update
insert into `prices` (tripid,`date`,`duration`,`price`,`garant`,`updatetime`,`persons`,`accommodationid`) values
(179881,'2016-03-18',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-18',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-18',4,313,'0','2016-03-16','1','119'),(179881,'2016-03-18',5,366,'0','2016-03-16','1','119'),(179881,'2016-03-19',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-19',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-19',4,313,'0','2016-03-16','1','119'),(179881,'2016-03-19',5,366,'0','2016-03-16','1','119'),(179881,'2016-03-20',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-20',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-20',4,313,'0','2016-03-16','1','119'),(179881,'2016-03-20',5,366,'0','2016-03-16','1','119'),(179881,'2016-03-21',2,206,'0','2016-03-16','1','119'),(179881,'2016-03-21',3,260,'0','2016-03-16','1','119'),(179881,'2016-03-21',4,313,
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 247 page no 1457 n bits 136 index keyGroup of table `travel`.`prices` trx id 3732195928 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 3732195927, ACTIVE 0 sec inserting, thread declared inside InnoDB 4529
mysql tables in use 1, locked 1
15 lock struct(s), heap size 1136, 476 row lock(s), undo log entries 472
MySQL thread id 414, OS thread handle 3832, query id 10956852 ---(ip)-- --name-- update
insert into `prices` (tripid,`date`,`duration`,`price`,`garant`,`updatetime`,`persons`,`accommodationid`) values
(179880,'2016-03-18',2,185,'0','2016-03-16','1','1438'),(179880,'2016-03-18',3,217,'0','2016-03-16','1','1438'),(179880,'2016-03-18',4,249,'0','2016-03-16','1','1438'),(179880,'2016-03-18',5,279,'0','2016-03-16','1','1438'),(179880,'2016-03-18',6,312,'0','2016-03-16','1','1438'),(179880,'2016-03-18',7,343,'0','2016-03-16','1','1438'),(179880,'2016-03-18',8,375,'0','2016-03-16','1','1438'),(179880,'2016-03-19',2,185,'0','2016-03-16','1','1438'),(179880,'2016-03-19',3,217,'0','2016-03-16','1','1438'),(179880,'2016-03-19',4,249,'0','2016-03-16','1','1438'),(179880,'2016-03-19',5,279,'0','2016-03-16','1','1438'),(179880,'2016-03-19',6,312,'0','2016-03-16','1','1438'),(179880,'2016-03-19',7,343,'0','2016-03-16','1','1438'),(179880,'2016-03-20',2,185,'0','2016-03-16','1','1438'),(179880,'2016
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 247 page no 1457 n bits 72 index keyGroup of table `travel`.`prices` trx id 3732195927 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 247 page no 1457 n bits 136 index keyGroup of table `travel`.`prices` trx id 3732195927 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (1)
------------
The Question
How do I avoid the deadlock?
I seem to have fixed it by first locking the table, then do the huge query and unlock it so the second thread gets acces.
LOCK TABLES prices WRITE;
insert into prices (tripid, date, duration, price, persons, accomodationid)
values ( 1 , 2016-6-4, 8, 200,2,32),
... a whole lot more ...
( 1 , 2016-7-4, 8, 200,2,32)
on duplicate key update price = values(price);
UNLOCK TABLES;