Insert intention lock exploration

I recently made an attempt to insert a lock, and it feels very strange. I will show it here and write my own analysis results.
First create a table:

CREATE TABLE `t2` (
  `id` int(11) DEFAULT NULL,
  `class` int(11) DEFAULT NULL,
  KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Insert some data:

select * from t2;
+------+-------+
| id   | class |
+------+-------+
|    2 |     2 |
|   10 |    10 |
+------+-------+

First open a session as transaction 1:

begin;
select * from t2 where id>4 and id<5 for update;

Open another session as transaction 2:

begin;
select * from t2 where id=2 for update;--正常
select * from t2 where id=5 for update; --正常,gap和rec锁不冲突
select * from t2 where id=10 for update;--阻塞

You can see that the locked range here is (2,10], which is consistent with the lock range described by next-key lock.
Next, let's try insert:

insert into t2 values(2, 2);--阻塞
insert into t2 values(10, 10);--正常

Just the opposite of select for update. But this is only a superficial phenomenon, not the real situation.
We then create a table with a primary key:

CREATE TABLE `t3` (
  `id` int(11) NOT NULL,
  `phone` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

With the same data:

select * from t3;
+------+-------+
| id   | class |
+------+-------+
|    2 |     2 |
|   10 |    10 |
+------+-------+

Transaction 1 remains unchanged, use transaction 2 to try insert again:

insert into t3 values(2, 2);--主键冲突
insert into t3 values(10, 10);--阻塞

The primary key conflict is actually not a lock conflict, but after there is no conflict, the primary key check is entered, which leads to the primary key conflict.
It can be seen that next-key lock is satisfied again at this time, that is, the locked range is (2,10), which is too strange.
After a day of thinking, I feel that the hidden primary key in the t2 table may be a trick. In Innodb, if the primary key is not set, the primary key of a clustered index will be created by default, so I decided to create a table with a primary key and control the primary key by myself:

CREATE TABLE `t4` (
  `id` int(11) NOT NULL,
  `inx` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `inx` (`inx`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

The same data:

+----+------+
| id | inx  |
+----+------+
|  2 |    2 |
| 10 |   10 |
+----+------+

Transaction 1:

begin;
select * from t4 where inx>4 and inx<5 for update;

Transaction 1 does not move, and the rest are performed in transaction 2:

begin;
insert into t4 values(2, 2);--主键冲突
insert into t4 values(10, 10);--阻塞

This result is consistent with the result of the t3 table that contains the primary key and locks the primary key, but continuing the test, strange things happened:

insert into t4 values(1, 2);--成功
insert into t4 values(3, 2);--阻塞
insert into t4 values(11, 2);--阻塞
insert into t4 values(100, 2);--阻塞

That is, when inx=2, the range of the primary key id(2, +inf) is locked, but

insert into t4 values(11, 11);--又成功

I checked the scope of next-key lock from the beginning:

select * from t4 where id=2 for update;--正常
select * from t4 where id=10 for update;--阻塞
select * from t4 where inx=2 for update;--正常
select * from t4 where inx=10 for update;--阻塞
select * from t4 where id>2 and id< 3 for update;--阻塞
select * from t4 where id>9 and id< 10 for update;--阻塞
select * from t4 where inx>2 and inx< 3 for update;--阻塞
select * from t4 where inx>9 and inx<10 for update;--阻塞
select * from t4 where inx>10 and inx<11 for update; --正常
select * from t4 where id>10 and id<11 for update;--正常
select * from t4 where id>1 and id<2 for update; --正常
select * from t4 where inx>1 and inx<2 for update;--正常

The range of next-key lock can be determined: inx (2, 10], id (2, 10), this is correct. The key is for the edge values ​​2 and 10, when these two values ​​are locked and when not Is locked, now it is strange, let's look at the value of 10 again:

insert into t4 values(11, 10);--正常
insert into t4 values(1, 10);--阻塞
insert into t4 values(10, 10);--阻塞

So I found a rule:
when inx=2, id>inx will conflict, otherwise there will be no conflict.
When inx=10, id<=inx will conflict, otherwise there will be no conflict.
When inx is in the range (2, 10), it will conflict no matter what.

In this way, when a range lock is applied to an index, the primary key is more like a secondary index value. The main digit is the index, and the secondary digit is the primary key, or decimal offset. The summary formula:

inx_offset=inx+(id-inx)/max_num

This max_num can be understood as the maximum value that the primary key id can take, but it does not represent the current maximum value.
When the index inx=2, whether the insert falls within the range of (2, 10) depends on the primary key. When the primary key=2, the value is inx_offset=2+(2-2)/max_num=2, which is Outside (2, 10), that is, there is no conflict.
But when the primary key=3 or greater, the value becomes inx_offset=2+(3-2)/max_num=2.*>2, then this If the number is larger than 2, it falls into the range of (2,10) and conflicts.
Similarly, explain the case of index inx=10, if id=10, then inx_offset=10+(10-10)/max_num =10, falls in the range of (2,10), conflict;
inx=10, id=11, inx_offset=10+(11-10)/max_num>10, not in the range (2,10], no conflict ;
Inx=10, id=1, inx_offset=10+(1-10)/max_num<10, falling within the range of (2,10), conflict.
Now through observation and testing, this is the result, welcome everyone to give pointers .

Guess you like

Origin blog.csdn.net/wyb_robin/article/details/107129532
Recommended