mysql - same row update query ocuurs deadlock - mysql

In my application, a deadlock occurs when I run queries that are almost the same.
When I ran a SHOW ENGINE INNODB STATUS, result is same as follows. (InnoDB, mysql5.7)
*** (1) TRANSACTION:
TRANSACTION 41516159, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 7 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 25
MySQL thread id 266983, OS thread handle 47538359256832, query id 156835755 172.31.5.225 Test_Table updating
Update Test_Table SET updatedAt = '2021-11-01 23:59:59' WHERE PK = 1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 142 page no 542 n bits 0 index PRIMARY of table `Test_Table` trx id 41516159 lock_mode X locks rec but not gap waiting
Record lock, heap no 68 PHYSICAL RECORD: n_fields 23; compact format; info bits 0
*** (2) TRANSACTION:
TRANSACTION 41516160, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 7 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 25
MySQL thread id 266996, OS thread handle 47549115574016, query id 156835795 XXX.XX.XX.XX Test_Table updating Update Test_Table SET updatedAt = '2021-11-01 23:59:58' WHERE PK = 1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 142 page no 542 n bits 0 index PRIMARY of table `Test_Table` trx id 41516160 lock mode S locks rec but not gap Record lock, heap no 68 PHYSICAL RECORD: n_fields 23; compact format; info bits 0
I can't understand why update query make a s-lock.

Related

MySQL deadlock on same record

I'm getting MySQL deadlock errors from time to time. it seems that a transaction is holding and waiting on the same record lock. How could that possibly happen?
------------------------
LATEST DETECTED DEADLOCK
------------------------
2021-03-28 02:05:00 2b89b1836700
*** (1) TRANSACTION:
TRANSACTION 22774383040, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 4862793, OS thread handle 0x2b83dfb8a700, query id 18125913470 manhattanowner statistics
select * from tb_ride where id=3857302 for update
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 224 page no 70133 n bits 98 index `PRIMARY` of table `Manhattan`.`tb_ride` trx id 22774383040 lock_mode X locks rec but not gap waiting
Record lock, heap no 98 PHYSICAL RECORD: n_fields 26; compact format; info bits 0
*** (2) TRANSACTION:
TRANSACTION 22774382499, ACTIVE 1 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 376, 2 row lock(s)
MySQL thread id 4862794, OS thread handle 0x2b89b0e77700, query id 18125912089
select * from tb_ride where id=3857302 for update
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 224 page no 70133 n bits 98 index `PRIMARY` of table `Manhattan`.`tb_ride` trx id 22774382499 lock_mode X locks rec but not gap
Record lock, heap no 98 PHYSICAL RECORD: n_fields 26; compact format; info bits 0
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 224 page no 70133 n bits 98 index `PRIMARY` of table `Manhattan`.`tb_ride` trx id 22774382499 lock_mode X locks rec but not gap waiting
Record lock, heap no 98 PHYSICAL RECORD: n_fields 26; compact format; info bits 0

MySQL allows multiple next-key locks on same record

Is MySQL allows multiple next-key locks on same record/range from multiple sessions? As following example shows, the same delete statement won't block each other.
My understanding is MySQL support MVCC, which means it has snapshot for each sessions below. Am I right?
MySQL: 5.7
Isolation level: Repeatable Read
Database: innodb
CREATE TABLE `game_summaries` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`game_id` int(11) DEFAULT NULL,
`value` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_game_summaries_on_game_id` (`game_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
session 1
start transaction;
delete from game_summaries where game_id = 2;
session 2
start transaction;
delete from game_summaries where game_id = 2;
run show engine innodb status \G to get all locks
------------
TRANSACTIONS
------------
Trx id counter 2849
Purge done for trx's n:o < 2841 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 2848, ACTIVE 8 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 3, OS thread handle 139644825081600, query id 232 172.18.0.1 root starting
show engine innodb status
TABLE LOCK table `test`.`game_summaries` trx id 2848 lock mode IX
RECORD LOCKS space id 27 page no 4 n bits 72 index index_game_summaries_on_game_id of table `test`.`game_summaries` trx id 2848 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;;
---TRANSACTION 2847, ACTIVE 18 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 2, OS thread handle 139644825351936, query id 229 172.18.0.1 root
TABLE LOCK table `test`.`game_summaries` trx id 2847 lock mode IX
RECORD LOCKS space id 27 page no 4 n bits 72 index index_game_summaries_on_game_id of table `test`.`game_summaries` trx id 2847 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;;
According to https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html, the gap lock is allowed to be hold on a gap by different transactions, even you saw lock_mode X there.
It is also worth noting here that conflicting locks can be held on a gap by different transactions. For example, transaction A can hold a shared gap lock (gap S-lock) on a gap while transaction B holds an exclusive gap lock (gap X-lock) on the same gap. The reason conflicting gap locks are allowed is that if a record is purged from an index, the gap locks held on the record by different transactions must be merged.

Understanding deadlock in mysql 8.0.11

I found below deadlock in "Show engine innodb status" on myql 8.0.11.
As per my understanding both queries should lock only one row (the primary key user_id which is different for both queries). However the second query "*** (2) HOLDS THE LOCK(S): "RECORD LOCKS space id 5461 page no 76054 n bits 96 index PRIMARY of tableuser"`
and First query is waiting to acquire same lock. I did not understand why? even when both lock statement say "locks rec but not gap" which means both are locking only individual records (which should have been their primary key user_id )
Would like to know is my understanding (as explained above) correct? If yes then how to explain the below logs?
*** (1) TRANSACTION:
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
update `user` set `user`.`xxxx` = 1 where ( user_id = 4939334)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 5461 page no 76054 n bits 96 index PRIMARY of table `user` trx id 5385693398 lock_mode X locks rec but not gap waiting
Record lock, heap no 24 PHYSICAL RECORD: n_fields 20; compact format; info bits 0
*** (2) TRANSACTION:
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
update `user` set `user`.`xxxx` = 1 where ( user_id = 4917613)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 5461 page no 76054 n bits 96 index PRIMARY of table `user` trx id 5385693399 lock_mode X locks rec but not gap
Record lock, heap no 24 PHYSICAL RECORD: n_fields 20; compact format; info bits 0
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 5461 page no 63136 n bits 88 index PRIMARY of table `user` trx id 5385693399 lock_mode X locks rec but not gap waiting
Record lock, heap no 19 PHYSICAL RECORD: n_fields 20; compact format; info bits 0

MySQL Deadlock - accessing different primary key values also creating deadlock

I went through MySQL logs and found the reason for deadlock
1st thread is trying to execute below query. Which is waiting for thread 2.
UPDATE M_SAMP SET FLAG=0 WHERE M_ID IN (SELECT M_ID FROM MM_RVW_SAMP WHERE TARGET_M_ID IN(19))
The result of inner query (SELECT M_ID FROM MM_RVW_SAMP WHERE TARGET_M_ID IN(19)) is M_ID = 3562.
2nd thread is executing below query.
DELETE FROM M_SAMP WHERE M_ID=3455
There is no foreign key relation defined in M_SAMP and MM_RVW_SAMP tables. And M_ID is primary key of M_SAMP table. And engine is InnoDB. And this issue is repeating.
Can any one help me, how locks are granted because of which deadlock is happening?
Logs
------------------------
LATEST DETECTED DEADLOCK
------------------------
2015-02-23 10:52:28 de8
*** (1) TRANSACTION:
TRANSACTION 13275344, ACTIVE 0 sec fetching rows
mysql tables in use 2, locked 2
LOCK WAIT 25 lock struct(s), heap size 2408, 1394 row lock(s), undo log entries 1
MySQL thread id 1455, OS thread handle 0x11ac, query id 39660 xyz 192.168.1.108 userName Sending data
UPDATE M_SAMP SET FLAG=0 WHERE M_ID IN (SELECT M_ID FROM MM_RVW_SAMP WHERE TARGET_M_ID IN(19))
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 61569 page no 65 n bits 176 index `PRIMARY` of table `dbName`.`m_samp` trx id 13275344 lock_mode X waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 29; compact format; info bits 0
*** (2) TRANSACTION:
TRANSACTION 13275335, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
254 lock struct(s), heap size 27512, 21595 row lock(s), undo log entries 7
MySQL thread id 1458, OS thread handle 0xde8, query id 39664 xyz 192.168.1.108 userName updating
DELETE FROM M_SAMP WHERE M_ID=3455
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 61569 page no 65 n bits 176 index `PRIMARY` of table `dbName`.`m_samp` trx id 13275335 lock mode S locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 29; compact format; info bits 0
Thank you.
The answer to my question is given here (on dba.stackexchange) by #jynus.
Now I am using below update query.
UPDATE M_SAMP M
JOIN MM_RVW_SAMP MM
ON M.M_ID = MM. M_ID
SET M.FLAG = 1
WHERE MM.TARGET_M_ID = 19;

reproduce such deadlock in mysql

I got information from SHOW ENGINE INNODB STATUS
*** (1) TRANSACTION:
TRANSACTION 0 2799914, ACTIVE 1 sec, process no 4106, OS thread id 139808903796480 inserting
mysql tables in use 1, locked 1
LOCK WAIT 10 lock struct(s), heap size 1216, 7 row lock(s), undo log entries 3
MySQL thread id 4284, query id 2889649 localhost 127.0.0.1 test update
INSERT INTO shipping .....
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 436366 n bits 88 index `PRIMARY` of table `testdatabase`.`order` trx id 0 2799914 lock mode S locks rec but not gap waiting
Record lock, heap no 14 PHYSICAL RECORD: n_fields 213; compact format; info bits 0
..........;
*** (2) TRANSACTION:
TRANSACTION 0 2799913, ACTIVE 1 sec, process no 4106, OS thread id 139808905824000 starting index read, thread declared inside InnoDB 500
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1216, 5 row lock(s), undo log entries 4
MySQL thread id 4290, query id 2889711 localhost 127.0.0.1 test Updating
UPDATE order
........
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 436366 n bits 88 index `PRIMARY` of table `testdatabase`.`order` trx id 0 2799913 lock_mode X locks rec but not gap
Record lock, heap no 14 PHYSICAL RECORD: n_fields 213; compact format; info bits 0
..........
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 153737 n bits 88 index `PRIMARY` of table `testdatabase`.`order` trx id 0 2799913 lock_mode X locks rec but not gap waiting
Record lock, heap no 10 PHYSICAL RECORD: n_fields 213; compact format; info bits 0
......
*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS
there is a FK in ship referring to order primary key.
I think T2 has hold x-lock, why it still need wait for a x-lock.
Can some one help me to reproduce such deadlock in mysql?
Thanks.
I don't know your queries, but seems like you're inserting row into child table, and then doing an update of row in parent table.
If that's true, you've hit this issue in MySQL: http://bugs.mysql.com/bug.php?id=48652
If a FOREIGN KEY constraint is defined on a table, any insert, update, or delete that requires the constraint condition to be checked sets shared record-level locks on the records that it looks at to check the constraint. InnoDB also sets these locks in the case where the constraint fails.
On a single record of the first table you have:
S lock from transaction 1 set,
S lock from transaction 2 set,
X lock from transaction 1 requested, blocked by S lock from transaction 2,
X lock from transaction 2 requested, blocked by S lock from transaction 1
Possible solution is to first update parent table, then insert row into child table. Suppose we need to increment some counter upon child row insertion, then queries would be:
UPDATE <parent row> SET count = count + 1;
INSERT <child row>; /* if the INSERT fails, roll back the trx */
If you want to update parent row only after inserting child row, you can use FOR UPDATE statement to set a lock on parent row:
SELECT <parent row> FOR UPDATE;
INSERT <child row>; /* if the INSERT fails, roll back the trx */
UPDATE <parent row> SET count = count + 1;