I ran your samples, and you are right, this one doesn’t block.
Here is why I am convinced that a select (some sort of select anyway) does take a shared lock:
Every month or so I’m spending time solving deadlocks in poor code in mysql5.7 because a client stack (jpa) is always reading an entity (row) before the code can hope to update it (managed entity). Thread 1 takes a S lock, followed by an attempt to escalate it to X lock upon update. The thing is, another thread 2 was attempting an update before thread 1, and thread 2 is waiting for the X lock but can’t get it. Now Thread 1 wants the lock too but it is after thread 2, yet holding the S lock. Deadlock. Yes, SHOW ENGINE INNODB STATUS shows the last detected deadlock well with the situation (pasted below) among to update on a “thing” row.
(FYI, the default isolation is repeatable-read, in all this.)
So, I have a hard time believing that millions of developers and users of hibernate didn’t figure out how to avoid locking for nothing. Something is missing. All I know is that the code asked to “find” the “thing” (by PK mind you) and then updates it. (My usual fix is to “select …for update” since it’s obviously the intent).
For such deadlock to occur, the S lock wasn’t taken “just before” the X lock. A window of time had to expose the txn to collisions. So that’s a hint that the jpa “find” issued a statement (we know it’s a select) on the “thing” table.
At this point, since there is NOTHING in the select SQL to explain this shared lock, I’m hoping for a magical mysql “mode” that nobody (in mysql nor jpa community) talks much about. (FYI, it’s on AWS RDS, but I get those on a local mysql 5.7 too).
*** (1) TRANSACTION:
TRANSACTION 175274419, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 8 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 2
MySQL thread id 627088, OS thread handle 22952098592512, query id 365172765 192.168.1.100 mydatabase updating
update thing set needs_checkup=0 where id=1129
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 361 page no 25 n bits 144 index PRIMARY of table `mydatabase`.`thing` trx id 175274419 lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 175274420, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
8 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 2
MySQL thread id 627077, OS thread handle 22952240928512, query id 365172766 192.168.1.100 mydatabase updating
update thing set needs_checkup=0 where id=1129
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 361 page no 25 n bits 144 index PRIMARY of table `mydatabase`.`thing` trx id 175274420 lock mode S locks rec but not gap
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 361 page no 25 n bits 144 index PRIMARY of table `mydatabase`.`thing` trx id 175274420 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (2)
I think this hijacks the thread as much as I could… LOL
Wasn’t my intention. But it lends itself to a deeper analysis of the lock, and hopefully a bit about replication in all that.