We have simple table:
CREATE TABLE t1 (
c1 INT UNSIGNED NOT NULL AUTO_INCREMENT,
c2 INT,
PRIMARY KEY (c1)
)
ENGINE=INNODB
1st transaction:
set transaction isolation level read uncommitted;
start transaction;
insert into t1 set c2 = 1;
For some reason INSERT creates IX lock on t1 table. I expected no locks. Especially i did not expect only IX lock, without X lock on some row.
2nd transaction:
set transaction isolation level read uncommitted;
start transaction;
update t1 set c2 = 2 where c1 = 1;
Here UPDATE statement creates IX lock on table t1 and create X lock on updated row. As i expected in read uncommited isolation level. But this X lock is waiting for X lock that belongs to 1st transaction. Yes, somehow it appears right now.
Why INSERT statement creates X lock on inserted row? What purpose? Where can i get information about such behaviour?
Why it happened in such weird way? Why INSERT created only IX lock and X lock appeared later?
It is normal behaviour for any database to lock in exclusive mode (X) any row or page it modifies. Also, READ UNCOMMITTED referrers to SELECT, nothing to do with INSERT.
To release the lock either COMMIT or ROLLBACK the first transaction.
Related
As Locks Set by Different SQL Statements in InnoDB, INSERT section said,
INSERT sets an exclusive lock on the inserted row.
Prior to inserting the row, a type of gap lock called an insert intention gap lock is set.
If a duplicate-key error occurs, a shared lock on the duplicate index record is set.
I wonder why set a shared lock, as the subsequent doc said, this might lead to a deadlock. Why not simply request an exclusive lock?
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.
The below example copied from the MySQL document which demonstrate how to trigger the deadlock.
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;
CREATE TABLE 'test'.'t1' (
'id' INT NULL);
CREATE TABLE 'test'.'t2' (
'id' INT NULL);
INSERT INTO test.t1 VALUES(1);
INSERT INTO test.t2 VALUES(1);
example1:
sqlConnection1:
SET autocommit = 0;
START TRANSACTION;
UPDATE test.t1 set id = 1 WHERE id = 2;
sqlConnection2:
SET autocommit = 0;
START TRANSACTION;
LOCK TABLES test.t2 WRITE,test.t1 WRITE;
COMMIT;
UNLOCK TABLES;
sqlConnection1:
UPDATE test.t2 set id = 1 where id = 2;
COMMIT;
sqlConnection2:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restart transaction
example2:
sqlConnection1:
SET autocommit = 0;
START TRANSACTION;
UPDATE test.t1 set id = 1 WHERE id = 1;
sqlConnection2:
SET autocommit = 0;
START TRANSACTION;
LOCK TABLES test.t2 WRITE,test.t1 WRITE;
COMMIT;
UNLOCK TABLES;
sqlConnection1:
UPDATE test.t2 set id = 1 where id = 1;
COMMIT;
sqlConnection1:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restart transaction
example3:
deadlock not found after mysql5.6
question:
What causes the results to differ in three examples
There are several points in this problem.
When InnoDB tries to update a record it will put a RECORD LOCK or a GAP LOCK onto the range it is going to update. For records that are existent, InnoDB will put a record lock, otherwise a gap lock. For example, in example 1, InnoDB will add a gap lock, and in this case, it adds a [2,∞) gap lock onto the table.
MySQL's LOCK TABLES command actually implements in MySQL itself but not in InnoDB. Although MySQL's LOCK TABLE has a "deadlock-free design" in other table-lock-only storage engines like MyISAM, it cannot avoid the deadlocks in InnoDB since InnoDB has row-level locks. Fortunately, the InnoDB has a deadlock detection mechanism that can detect when an InnoDB row lock and a MySQL table lock occur together on the same table and do what you see -- rollback a "lighter" transaction to avoid deadlock, which is so-called "victim".
The InnoDB engine will generally decide the transaction with fewer rows affected to be the victim. In example 1, connection 1 puts two GAP LOCKS on rows [2,∞) for two tables, while connection 2 only acquires two WRITE LOCKS and does not get anything done. Thus, connection 2 is "lighter" and being selected as the "victim". In example 2, connection 1 only has two RECORD LOCKS while connection 2 still has two WRITE LOCKS, and connection 1 basically do not do anything. Thus InnoDB decides that connection 1 uses fewer resources (i.e. locks in this case), and chooses connection 1 as the victim.
Since MySQL 5.6, a new lock wait mechanism has been introduced to MySQL. When MySQL cannot acquire a table lock, it will wait as long as the interval lock-wait-timeout specified. quote from the MySQL document:
This variable[lock-wait-timeout] specifies the timeout in seconds for attempts to acquire metadata locks. The permissible values range from 1 to 31536000 (1 year). The default is 31536000.
This timeout applies to all statements that use metadata locks. These include DML and DDL operations on tables, views, stored procedures, and stored functions, as well as LOCK TABLES, FLUSH TABLES WITH READ LOCK, and HANDLER statements.
Thus, MySQL will not acquire a table lock when it sees InnoDB already has a lock on the table it is going to manipulate and wait until InnoDB gets its work done. For further information, you can refer to MySQL document(The following documents are for 8.0 version, but the basic principles are the same):
https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-detection.html
I'm trying to understand how next-key locking works with serializable isolation level in MySql 5.6.
There are next explanation in documentation:
"SELECT ... FROM is a consistent read, reading a snapshot of the database and setting no locks unless the transaction isolation level is set to SERIALIZABLE. For SERIALIZABLE level, the search sets shared next-key locks on the index records it encounters. However, only an index record lock is required for statements that lock rows using a unique index to search for a unique row."
So, If I have next table:
table simple2
(
id int primary key,
val int,
index (val)
);
insert into simple2 values (1,1),(2,2),(3,3),(5,5),(7,7),(15,15);
Then:
Transaction 1:
set transaction isolation level serializable;
start transaction;
select * from simple2 where val = 7;
Transaction 2:
set transaction isolation level serializable;
start transaction;
insert into simple2 values (10, 10); //blocked
I don't understand why I can't add (10,10).
Because, according to doc explanation, only row index and gap before index should be blocked by Transaction 1: (5, 7].
Mysql 5.6 Innodb repeatable-read isolation level.
T1 T2
select ... where id = 1 for update
select ... where id = 1 for update
T1 run first, then execute T2, they are in separate transactions.
The result is select for update of T2 is blocked.
According to https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html,
select for update will set IX lock on table, and IX is compatible with IX base on the lock compatibility matrix in this document.
Then why 2nd select for update is blocked by 1st?
I searched some posts about this question, now I also have following questions related with it:
select for update will set IX on table first, then set X on match index/row, right?
X and S lock can be table-level or row-level, right?
In lock type compatibility matrix of https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html, the X and S means table-level lock, not row-level, right?
2nd select for update blocked, because 1st select for update have set IX on table and X on matched index/row, so when 2nd select for update set IX on table, it is ok. But when it set X latter, it is blocked because it has been set X by 1st select for update, right?
"Then why 2nd select for update is blocked by 1st?" - because that is the whole point of having an exclusive(X) lock.
Yes.
Yes.
Yep.
Right.
1:
I was trying this and it was working fine:
start transaction;
select * from orders where id = 21548 LOCK IN SHARE MODE;
update orders set amount = 1500 where id = 21548;
commit;
According to the definition of LOCK IN SHARE MODE , it locks the table with IS lock and lock the selected rows with S lock.
When a row is locked with S lock.How can it be modified without releasing lock?
It needs X lock to modify it.Right? Or is it valid only for different connection transaction?
2:
//session1
start transaction;
select * from orders where id = 21548 FOR UPDATE;
Keep this session1 same and try this in the different session:
//session2
select * from orders where id = 21548; //working
update orders set amount = 2000 where id = 21548; //waiting
FOR UPDATE locks the entire table into IX mode and selected row into X mode.
As X mode is incompatible with S mode then how come select query in second session is getting executed?
One answer might be that select query is not asking for S lock that's why it's running successfully.But update query in the second session is also not asking for X lock , but as you execute it , it starts waiting for the lock held by session1.
I have read a lot of stuff regarding this but not able to clear my doubts.Please help.
Bill Karwin answered this question through email.He said:
The same transaction that holds an S lock can promote the lock to an X lock. This is not a conflict.
The SELECT in session 1 with FOR UPDATE acquires an X lock. A simple SELECT query with no locking clause specified does not need to acquire an S lock.
Any UPDATE or DELETE needs to acquire an X lock. That's implicit. Those statements don't have any special locking clause for that.
For more details on IS/IX locks and FOR UPDATE/LOCK IN SHARE MODE please visit
shared-and-exclusive-locks .