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 .