阅读mysql加锁文档
This commit is contained in:
@@ -163,4 +163,118 @@ Next-Key Lock是为了解决幻读问题而引入的,如果事务T1已经锁
|
||||
但是,`如果查询的索引为unique索引,那么innodb则是会针对next-key lock进行优化,将其降级为record lock,紧锁住索引本身,而不对范围进行加锁。`
|
||||
|
||||
|
||||
#### insert-intention lock
|
||||
插入意向锁是在插入行数据前,由`插入操作`设置的间隙锁。多个事务在针对同一间隙进行插入操作时,`如果他们并不在同一位置进行插入,那么各个事务之间并不需要彼此等待`。
|
||||
|
||||
例如,多个事务需要在`(4, 7)`的间隙之间插入值,但是A事务插入5,B事务插入6,那么在获取待插入行的X锁之前,都需要通过insert intention lock来锁住`(4, 7)`的间隙。插入意向锁之间并不会相互阻塞。
|
||||
|
||||
|
||||
### 加锁示例
|
||||
首先,创建数据库并预制数据
|
||||
```sql
|
||||
-- ddl
|
||||
CREATE TABLE `t_learn_lock` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`content` varchar(32) NOT NULL DEFAULT '',
|
||||
`lv` int NOT NULL DEFAULT '-1',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_lv` (`lv`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
|
||||
|
||||
-- dml
|
||||
insert into t_lear_lock(id, content, lv) values
|
||||
(1, 'a', 3),
|
||||
(2, 'd', 7),
|
||||
(3, 'f', 9),
|
||||
(4, 'o', 13);
|
||||
```
|
||||
|
||||
会话的事务默认隔离级别为repeatable read
|
||||
|
||||
#### performance_schema.data_locks
|
||||
`performace_schema.data_locks`中存储了加锁情况,其表结构如下:
|
||||
- engine:该锁代表的存储引擎
|
||||
- engine_transaction_id: 存储引擎中标识事务的id
|
||||
- thread_id: `创建该锁的会话`的线程id
|
||||
- object_schema: 包含该表的schema
|
||||
- object_name:表名称
|
||||
- index_name: 被加锁的索引名称
|
||||
- lock_type: 加锁的类型,对于innodb,其值为record或table,代表行锁或表锁
|
||||
- lock_mode: 请求锁的方式,对于innodb,其格式为`S[,GAP], X[,GAP], IS[,GAP], IX[,GAP], AUTO_INC, and UNKNOWN`。
|
||||
- lock_data:锁相关的数据
|
||||
- lock_status: 请求锁的状态,在innodb中,可以为`GRANTED或WAITING`
|
||||
|
||||
> #### lock mode
|
||||
> 在lock mode字段中,除了auto_inc和unknown之外的值都代表gap lock
|
||||
>
|
||||
> 其取值如下:
|
||||
> - X: next_key_locking,`代表x和x之前的间隙`
|
||||
> - X,GAP:间隙锁,`代表x之前的间隙,不包含x记录本身`
|
||||
> - X, REC_NOT_GAP:`代表X记录本身,不包含之前的间隙`
|
||||
> - X,GAP,INSERT_INTENTION:插入意向锁,彼此之前不互斥
|
||||
|
||||
#### select ... 加锁情况
|
||||
在开启事务后,执行`select * from t_lear_lock`语句,默认`并不会为index record或表加上任何锁`,在可重复读的隔离级别下,默认会采用无锁的一致性读方式,来读取数据的历史版本快照,期间并不需要进行任何加锁操作。
|
||||
|
||||
#### select ... for update 加锁情况
|
||||
在开启事务后,如果执行`select ... for update`语句,那么分为如下几种场景:
|
||||
|
||||
##### select语句未命中索引
|
||||
若执行`select * from t_learn_lock where content < 'h' and content > 'e' for update for update;`语句,由于content字段上并没有添加索引,故而该查询语句并不会命中索引,possible_keys为空。
|
||||
|
||||
在未命中索引的情况下,`select ... for update`语句`会针对主键索引来进行加锁`,其加锁情况如下:
|
||||
|
||||
|INDEX_NAME|LOCK_TYPE|LOCK_MODE|LOCK_STATUS|LOCK_DATA|
|
||||
|----------|---------|---------|-----------|---------|
|
||||
||TABLE|IX|GRANTED||
|
||||
|PRIMARY|RECORD|X|GRANTED|supremum pseudo-record|
|
||||
|PRIMARY|RECORD|X|GRANTED|1|
|
||||
|PRIMARY|RECORD|X|GRANTED|2|
|
||||
|PRIMARY|RECORD|X|GRANTED|3|
|
||||
|PRIMARY|RECORD|X|GRANTED|4|
|
||||
|
||||
其中,`select * from t_learn_lock where content < 'h' and content > 'e' for update for update;`会查询出id为`3`的记录,但是由于未命中索引,`该语句会针对主键中所有的index record进行加锁`(包括 supremum prseudo-record)。
|
||||
|
||||
> ##### supremum preseudo-record
|
||||
> 该index record并不是一条真实存在的索引记录,其代表了`比索引中所有记录的值都大的一条虚拟记录`,类似有序链表中的尾节点。
|
||||
|
||||
##### select语句命中非unique索引
|
||||
当执行的select语句,其where条件命中非unique索引,`那么,其会针对命中索引以及主键索引都进行加锁`。
|
||||
|
||||
例如,`select * from t_learn_lock where lv between 4 and 8 for update;`,该语句会针对`idx_lv`索引记录和主键索引记录进行加锁,加锁状况如下:
|
||||
|
||||
|INDEX_NAME|LOCK_TYPE|LOCK_MODE|LOCK_STATUS|LOCK_DATA|
|
||||
|----------|---------|---------|-----------|---------|
|
||||
||TABLE|IX|GRANTED||
|
||||
|idx_lv|RECORD|X|GRANTED|7, 2|
|
||||
|idx_lv|RECORD|X|GRANTED|9, 3|
|
||||
|PRIMARY|RECORD|X,REC_NOT_GAP|GRANTED|2|
|
||||
|
||||
上述查询语句会命中`(2, 'd', 7)`这条记录,但是,其针对索引加锁的操作如下:
|
||||
- 对于`idx_lv`索引记录,不仅对语句查询出的`(7,2)`这条索引记录进行了加锁,还针对了下一条索引记录`(9,3)`进行了加锁
|
||||
- 对于`primary`主键索引记录,则只针对查询出的id为2的记录进行了加锁`(2)`
|
||||
|
||||
对于`idx_lv`中`(9,3)`这条记录进行加锁,主要是为了防止幻读,当对`(9, 3)`这条记录加锁后,如果后续其他事务想要向lv值为`(7,9)`的范围内进行加锁时,需要获取
|
||||
|
||||
|
||||
|
||||
|
||||
#### update ... 加锁情况
|
||||
##### update语句未命中索引的加锁情况
|
||||
类似于`select ... for update`未命中索引的情况,update操作在未命中索引的情况下,`也会针对主键索引的记录进行加锁,并且会获取主键索引中所有记录的锁`。
|
||||
|
||||
其加锁情况和`select ... for update`一样,例如`update t_learn_lock set content = 'fuck' where content = 'm';`,该语句并不会实际修改任何行记录,但是还是会锁住主键中所有的index record。
|
||||
|
||||
|INDEX_NAME|LOCK_TYPE|LOCK_MODE|LOCK_STATUS|LOCK_DATA|
|
||||
|----------|---------|---------|-----------|---------|
|
||||
||TABLE|IX|GRANTED||
|
||||
|PRIMARY|RECORD|X|GRANTED|supremum pseudo-record|
|
||||
|PRIMARY|RECORD|X|GRANTED|1|
|
||||
|PRIMARY|RECORD|X|GRANTED|2|
|
||||
|PRIMARY|RECORD|X|GRANTED|3|
|
||||
|PRIMARY|RECORD|X|GRANTED|4|
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user