Bug #90210 deadlock when concurrency update the same index
Submitted: 26 Mar 2018 2:22 Modified: 3 Apr 2018 3:07
Reporter: Hwang knico Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server: InnoDB storage engine Severity:S2 (Serious)
Version:5.6.32+ OS:Any (CentOS,Ubuntu,MAC)
Assigned to: CPU Architecture:Any (intel64,MAC)
Tags: deadlock, innodb, lock

[26 Mar 2018 2:22] Hwang knico
Description:
i found mysql will report deadlock when i 'select for update' even ‘update’ with same record in concurrency thread, with the error status of innodb:

=====================================
2018-03-23 15:22:19 0x7f0824272700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 16 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 249 srv_active, 0 srv_shutdown, 1559184 srv_idle
srv_master_thread log flush and writes: 1558877
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 460
OS WAIT ARRAY INFO: signal count 376
RW-shared spins 0, rounds 441, OS waits 193
RW-excl spins 0, rounds 1371, OS waits 25
RW-sx spins 1, rounds 28, OS waits 0
Spin rounds per wait: 441.00 RW-shared, 1371.00 RW-excl, 28.00 RW-sx
------------------------
LATEST DETECTED DEADLOCK
------------------------
2018-03-23 15:21:54 0x7f08241df700
*** (1) TRANSACTION:
TRANSACTION 349110, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 143, OS thread handle 139672943212288, query id 460994 10.5.17.15 loanuser statistics
select 
     
    ID, NAME
   
    from  t_deadlock 
    where ID = 'T1-9' for update
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 118 page no 3 n bits 80 index PRIMARY of table "test"."t_deadlock" trx id 349110 lock_mode X locks rec but not gap waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 54312d39; asc T1-9;;
 1: len 6; hex 0000000553b4; asc     S ;;
 2: len 7; hex 3a0000021701fb; asc :      ;;
 3: len 1; hex 47; asc G;;

*** (2) TRANSACTION:
TRANSACTION 349111, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 141, OS thread handle 139672942409472, query id 460996 10.5.17.15 loanuser statistics
select 
     
    ID, NAME
   
    from  t_deadlock 
    where ID = 'T1-9' for update
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 118 page no 3 n bits 80 index PRIMARY of table "test"."t_deadlock" trx id 349111 lock mode S locks rec but not gap
Record lock, heap no 11 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 54312d39; asc T1-9;;
 1: len 6; hex 0000000553b4; asc     S ;;
 2: len 7; hex 3a0000021701fb; asc :      ;;
 3: len 1; hex 47; asc G;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 118 page no 3 n bits 80 index PRIMARY of table "loan"."t_deadlock" trx id 349111 lock_mode X locks rec but not gap waiting
Record lock, heap no 11 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 54312d39; asc T1-9;;
 1: len 6; hex 0000000553b4; asc     S ;;
 2: len 7; hex 3a0000021701fb; asc :      ;;
 3: len 1; hex 47; asc G;;

*** WE ROLL BACK TRANSACTION (2)
------------
TRANSACTIONS
------------
Trx id counter 349120
Purge done for trx's n:o < 349120 undo n:o < 0 state: running but idle
History list length 46
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421148215175928, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421148215175008, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
1693 OS file reads, 2440 OS file writes, 1097 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.81 writes/s, 0.62 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 1 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
Hash table size 34673, node heap has 2 buffer(s)
Hash table size 34673, node heap has 3 buffer(s)
Hash table size 34673, node heap has 2 buffer(s)
Hash table size 34673, node heap has 0 buffer(s)
0.19 hash searches/s, 0.56 non-hash searches/s
---
LOG
---
Log sequence number 186437447
Log flushed up to   186437447
Pages flushed up to 186437447
Last checkpoint at  186437438
0 pending log flushes, 0 pending chkp writes
858 log i/o's done, 0.38 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137428992
Dictionary memory allocated 1833906
Buffer pool size   8191
Free buffers       6459
Database pages     1724
Old database pages 639
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 1613, created 111, written 1469
0.00 reads/s, 0.00 creates/s, 0.37 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1724, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=990, Main thread ID=139672864941824, state: sleeping
Number of rows inserted 44210, updated 197, deleted 0, read 958895
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

How to repeat:
use multi-thread 'select for update' and update a record with same key(primary or unique key)

Suggested fix:
i don't understand why use S locker first when 'select for update' or 'update', maybe for other connection to read share, but i think the X locker need to lock at the point S locker locking, or the X locker could insert the locker order in first when the connection hold the S lock?
[28 Mar 2018 12:56] MySQL Verification Team
Hi!

Yes, you can have the deadlock over the same row. This is due to the lock upgrade procedure.

One transaction holds a record lock that is a gap lock, but not waiting. Next transaction comes and asks immediately for a gap lock waiting lock. Then the first transaction comes to upgrading the lock , but can't as second transaction was first one in the queue. Second transaction can not get it as there is already there a transaction holding a gap lock, but not waiting. You can not get a waiting lock if some other transaction holds a non-waiting lock.

Hence, you have a deadlock.

It is possible that this problem is solved, but it requires a very large effort. We already have several feature requests on this issue, like the one in #21356.
[28 Mar 2018 13:44] Hwang knico
oh,it looks like be realized long long ago,and not fix yet,so mysql server with the problem in all version now? no one report it when falling trouble in product environment?
[28 Mar 2018 13:48] MySQL Verification Team
Hi,

You do not seem to have read carefully what I wrote.

This feature is reported long time ago, but it is not implemented yet. Scheduling for this feature is still unknown.
[3 Apr 2018 3:07] Hwang knico
thanks for your reply, of cause i catch your point, i am very very care about how others avoid the effecting of the feature, and did you have some suggestion to me, thanks again
[3 Apr 2018 14:03] MySQL Verification Team
Hi,

There is no way to avoid this problem, except making very large changes in your queries, so that very few rows are affected in each of them.

Besides, current behaviour follows all existing standards for row locking. New behaviour would break the standard and might have many unforeseen consequences. In short, it is very complicated and will require departure from the standards pertaining to the locking.

This is a reason why it will take lots of time to design this new feature ......