Quantcast
Viewing all articles
Browse latest Browse all 35

Simple conditional update causing a deadlock hints at MySQL bug

I have a very simple "inventory management" update that under load causes InnoDB to deadlock and rollback one of the threads. There doesn't appear to be any ordering issue here, so I'm at a loss how to resolve it. MySQL version is 5.6.33-0

update productset available_qty = available_qty - 1where sku = arg_sku and available_qty > 0

Here is the full deadlock dump:

InnoDB: transactions deadlock detected, dumping detailed information.*** (1) TRANSACTION:TRANSACTION 50428499, ACTIVE 0 sec starting index readmysql tables in use 1, locked 1LOCK WAIT 10 lock struct(s), heap size 1184, 4 row lock(s), undo log entries 3update productset available_qty = available_qty - 1where sku = arg_sku and available_qty > 0*** (1) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 596 page no 96 n bits 152 index `PRIMARY` of table `mydbname`.`product` trx id 50428499 lock_mode X locks rec but not gap waiting*** (2) TRANSACTION:TRANSACTION 50428498, ACTIVE 0 sec starting index readmysql tables in use 1, locked 19 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 4update productset available_qty = available_qty - 1where sku = arg_sku and available_qty > 0*** (2) HOLDS THE LOCK(S):RECORD LOCKS space id 596 page no 96 n bits 152 index `PRIMARY` of table `mydbname`.`product` trx id 50428498 lock mode S locks rec but not gap*** (2) WAITING FOR THIS LOCK TO BE GRANTED:  RECORD LOCKS space id 596 page no 45 n bits 680 index `sku` of table `mydbname`.`product` trx id 50428498 lock_mode X locks rec but not gap waiting*** WE ROLL BACK TRANSACTION (2)

From what I'm reading, the interaction between a shared (S) lock and an exclusive (X) lock causes my concurrent updates to deadlock. Is there anything I can do to avoid this?

Update:

I believe this is a bug in MySQL.

Two concurrent threads acquire Shared-locks and try to acquire eXclusive locks to do the update. They wait for the other to release the Shared-lock first. There is no ordering issue in the way locks are acquired; the mixing of types of locks is the issue. MySQL should be able to "upgrade" the existing shared-lock to eXclusive. If another thread wants to do the same, the first thread should make progress by obtaining eXclusive lock, do the update, then release the lock so the other Shared-lock can be upgraded, and so on.

Workaround

My UPDATE statement was modified to use the PRIMARY KEY (from previous UNIQUE KEY). In addition, I'm first calling a SELECT matching the condition of the UPDATE and skip the UPDATE entirely if the SELECT comes back empty, thus avoiding the eXclusive lock whenever possible. All of this is in a stored proc so clients aren't affected.


Viewing all articles
Browse latest Browse all 35

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>