Why commit does not cause deadlock - mysql

I have read mysql reference manual about the InnoDB locks. I got that :
(from:
http://dev.mysql.com/doc/refman/5.5/en/innodb-locks-set.html)
INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.
Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6 each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.
If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock. This can occur if another session deletes the row. Suppose that an InnoDB table t1 has the following structure:
CREATE TABLE t1 (i INT, PRIMARY KEY (i)) ENGINE = InnoDB;
Now suppose that three sessions perform the following operations in order:
Session 1:
START TRANSACTION;
INSERT INTO t1 VALUES(1);
Session 2:
START TRANSACTION;
INSERT INTO t1 VALUES(1);
Session 3:
START TRANSACTION;
INSERT INTO t1 VALUES(1);
Session 1:
ROLLBACK;
The first operation by session 1 acquires an exclusive lock for the row. The operations by sessions 2 and 3 both result in a duplicate-key error and they both request a shared lock for the row. When session 1 rolls back, it releases its exclusive lock on the row and the queued shared lock requests for sessions 2 and 3 are granted. At this point, sessions 2 and 3 deadlock: Neither can acquire an exclusive lock for the row because of the shared lock held by the other.
I made a experiment and I found is is the fact.
My question is :
(1)I found that, if I commit session 1, and deadlock did not happen. Why? When I commit session 1, The X row lock will also be released. So I do not understand.
Thx.

I suspect this is an edge case that is not worth making more efficient. Note that you had to get 3 sessions trying to grab the same row -- a rare happening. And you did a ROLLBACK -- also rare. So the deadlock that occurred is overkill, but not worth fixing. For this reason, one must be prepared to handle deadlocks everywhere.
FYI, If this were 3 nodes of a Galera cluster, there would be errors on COMMIT that the code would have to handle. I suspect there are even more combinations of strange things going on if you apply this transaction (with rollback or commit) multiple times to multiple nodes.
Now to your question... Presumably the deadlock did not happen because one of the threads got the exclusive lock and the other was hit with a "wait" instead of a "deadlock".

Related

Exclusive Lock (Row Level) - MySql need an example

I want to lock a specific row in MySQL (InnoDB), so that specific row can't be read by another connection.
start transaction;
Select apples from fruit where apples = 'golden';
commit;
Now I want to apply an exclusive lock
exclusive lock
A kind of lock that prevents any other transaction from locking the same row.
https://dev.mysql.com/doc/refman/5.5/en/innodb-locking.html#innodb-shared-exclusive-locks
I'm assuming, the lock get's applied, then the second connection trying to access the same row, has to wait till the first connection releases the lock with a "commit;"
My question is, how do I adjust my SQL Statement to apply that lock? Or does this automatically happen with the start transaction?
I've been trying to find a good example and haven't.
START TRANSACTION;
Select apples
from fruit
where apples = 'golden'
FOR UPDATE;
...
COMMIT;
The SELECT ... FOR UPDATE statement first locks the row, then returns it to you.
Anyone else trying to do the same thing to the same row at the same time will wait for your COMMIT or ROLLBACK and then they will receive the row... with any changes you made.
If you have no intention of changing the row, you just want to make sure nobody else does can, then you can SELECT ... LOCK IN SHARE MODE.
Note that, either way, it's technically not the "row," it's actually the index record that is being locked, but in InnoDB, "everything is an index" (even a table with no indexes at all does in fact still have one index, siently created by the system) so the net result is the same.

mysql multiple locks, single row

This article says:
Tx 1: lock A, then B
Tx 2: lock B, then A
Because InnoDB starts transactions on the internally, you -are- going
to experience deadlocks.
No way of escaping it.
I'm seeing error code 1213 when many threads are trying to insert a row with the same PK, but I don't understand how there could be two locks. Isn't there just the single lock on that row?
If it's read lock then there can be more than one since read locks are shared but there can be only one write lock at any time. Every DML operation (insert/update/delete) will operate on implicit transaction and will hold a row level lock on that row and thus if any other thread trying to perform a write operation on that particular row it has to wait for the existing lock to be released since write locks are exclusive lock.
Scenario you have posted Tx 1: lock A, then B Tx 2: lock B, then A will definitely result into deadlock and that's why locking happens to in two phase. Growing phase when the transaction will acquire all the needed locks to perform the transaction and Shrinking phase when it will start releasing locks held by that transaction.

Mysql deadlock single insert transactions

In the following case I am seeing a deadlock detected, retry transaction error:
Multiple insertions are occurring concurrently. Each insertion is done within a transaction for only a single row. (Each transaction only does one insert between its begin and commit.) The table has 3 columns, one of which is the PK. The PK is not auto-incremented.
How is it possible for a deadlock to occur if each transaction is only holding a single lock? I thought that a single row insertion would only require a single row level lock.
Try using on mysql shell: show processlist This will give you details of what queries are under execution among other things. This would be a good starting point.

LOCK IN SHARE MODE locks entire table

Documentation:
SELECT ... LOCK IN SHARE MODE sets a shared mode lock on any rows that are read. Other sessions can read the rows, but cannot modify them until your transaction commits. If any of these rows were changed by another transaction that has not yet committed, your query waits until that transaction ends and then uses the latest values.
However, some experimentation suggests that it locks more than the rows that are read.
CREATE TABLE example (a int);
START TRANSACTION;
SELECT a FROM example WHERE a = 0 LOCK IN SHARE MODE;
And then on another connection
INSERT INTO example VALUES (1);
The later connection blocks on the lock.
It would seems that LOCK IN SHARE MODE locks more than "any rows that are read".
What exactly does LOCK IN SHARE MODE lock?
Make sure you have an index on the a column. Otherwise, in order to evaluate WHERE a = 0, it has to read every row in the table, and it will then set a lock on each row as it reads it.
ALTER TABLE example ADD INDEX (a);

Does SQL INSERT involve any read/write lock?

This is an extremely simple question, but i really can't find any useful answer on Google.
I've been reading that there's a READ LOCK on SELECT, and WRITE LOCK on UPDATE statements. However, i'm trying to find out if there's any kind of LOCK when we INSERT into a table?
The assumption is that the table is using InnoDB as the engine.
When inserting, InnoDB creates a so called "gap lock".
The manual describes this quite well:
A type of gap lock called an insertion intention gap lock is set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6 each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting
In addition to that, any unique index will be locked for the provided values to make sure that two different transactions don't insert the same value (that's slightly different than the gap lock if I'm not mistaken).
For innoDB mysql uses row level locking.
When inserting there is no row to lock because You are creating it.
Adding to the gap lock description, Mysql INNODB does a ROW-LOCK on the row that is being inserted.