当前位置: 首页 > 科技观察

MySQL锁定范围3——普通索引与普通字段

时间:2023-03-12 08:37:32 科技观察

前言前面已经介绍了主键索引的锁定范围和非主键唯一索引的锁定范围。主键索引:在加锁的时候,会先给表加一个意向锁,IX或者IS;如果有多个范围加锁,则分别加多把锁,每个范围有一把锁;(这个可以在id<20下练习)主键等效查询,当数据存在时,会在主键索引的值上加一个行锁X,REC_NOT_GAP;主键等效查询,当数据不存在时,会在查询条件的主键值所在的间隙上加一个间隙锁X,GAP;主键等价查询,范围查询情况比较复杂:8.0.17版本是区间前后开启,8.0.18及以后版本修改为区间前后开启;critical<=query,8.0.17会锁定下一个next-key的前开后关范围,8.0.18及以后的版本修复了这个bug。Non-primarykeyuniqueindex:非主键唯一索引等效查询,数据存在,forupdate会锁定在主键上,forshare在使用覆盖索引时只会锁定在自己的索引上;non-primarykeyindex等价查询,数据不存在,不管索引是否被覆盖,都相当于一个范围查询,只是在非主键索引上加了一个锁,或者间隙锁,并在开盘前开盘区间;查询非主键唯一索引的范围时,当不是覆盖索引时,对应的范围会加上前开后闭的区间,如果有数据会加行锁对应的主键;查询非主键唯一索引的范围时,如果是覆盖索引,则所有在最后一个关闭区间对应的主键上加行锁;非主键唯一索引加锁时,仍然存在next-key加锁下一个区间的bug。本篇我们就来看看普通索引和普通字段的锁定范围是多少?1数据库表数据CREATETABLE`t`(`id`intNOTNULLCOMMENT'主键',`a`intDEFAULTNULLCOMMENT'唯一索引',`c`intDEFAULTNULLCOMMENT'普通索引',`d`intDEFAULTNULL,PRIMARYKEY(`id`),UNIQUEKEY`uniq_a`(`a`),KEY`idx_c`(`c`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_0900_ai_ci数据库数据如下:思路和非主键的唯一索引是一样的,唯一不同的是是这里看到了c和d字段。因为前面的朋友应该对data_locks有一定的了解,这里我们直接分析data_locks的数据信息。2普通索引普通索引等价查询——数据存在于mysql>begin;选择*fromtwherec=210forupdate;直接分析data_locks表意向锁;指标idx_c增加了210区间的前开后锁;索引idx_c添加了215区间Gap锁,LOCK_MODE为X,GAP;主键加15行锁,LOCK_MODE为X,REC_NOT_GAP。主要原因是普通索引无法唯一锁定一条记录,所以必须锁定字段的前后范围。普通索引等价查询——数据不存在mysql>begin;select*fromtwherec=211forupdate;直接分析data_locks表的意向锁;215区间的gaplock被加到索引idx_c中。分析是因为数据不存在,只需要锁定215的缺口,因为215和210肯定不属于这个范围。普通索引范围查询mysql>begin;select*fromtwherec>210andc<=215forupdate;锁住idx_c索引的215前开后闭区间还可以理解,但是锁220就不太好理解了,应该是bug没有完全修复。普通字段普通字段比较好理解。对于普通字段,无论是哪个查询,都需要扫描所有的记录,所以直接在主键上加这个锁,所有区间都加锁。3总结本文在第一篇和第二篇的基础上,直接分析data_locks的信息来判断加锁范围。选择*fromperformance_schema.data_locks;LOCK_MODELOCK_DATAlockrangeX,REC_NOT_GAP1515该数据的行锁X,GAP1515该数据之前的间隙,不包括15X1515该数据的间隙,其中15LOCK_MODE=X是前后开-关闭间隔;X,GAP为前开后开区间(gaplock);X,REC_NOT_GAP是行锁。这样就得出了普通索引和普通字段的结论。普通索引普通索引等价查询,因为无法确定唯一性,所以即使定位到记录,也会向后查询,直到查询到一条不是该值的记录,从而锁定该值的范围;普通索引的锁也加载了。在索引上,如果涉及已有记录,会在主键上加行锁;普通索引的范围查询也存在next-key查询下一个区间的bug。普通字段普通字段查询会查询整张表,如果在这里加锁,主键的所有区间都会被加锁。本文转载自微信公众号“程序员小航”,可通过以下二维码关注。转载本文请联系程序员小航公众号。