锁执行的过程

本文详细介绍了数据库中的加锁机制,特别是在B+树中进行区间扫描时的处理步骤。针对不同的隔离级别,如READCOMMITTED和REPEATABLEREAD,分析了在二级索引和聚簇索引上的加锁行为,以及索引下推如何减少I/O操作。同时,通过实例展示了SELECT、UPDATE和DELETE语句在不同隔离级别下的加锁细节,揭示了事务处理中的锁管理策略。
摘要由CSDN通过智能技术生成

可以把锁定读的执行看成是依次读取若干个扫描区间中的记录

  • 步骤一:首先快速地在B+树叶子节点中定位到该扫描区间总的第一条记录作为当前记录
  • 步骤二:为当前记录加锁---在隔离级别不大于READ COMMITTED时,会为当前记录加记录锁。在隔离级别不小于REPEATABLE READ 时,会为当前记录加next-key锁
  • 步骤三:判断索引下推的条件是否成立---索引下推是把与被使用索引有关的搜索条件下推到存储引擎中判断,而不是返回server层再判断。索引下推只是为了减少回表次数,也就是减少读取完整的聚簇索引记录的次数,从而减少I/O操作。所以只适用二级索引,不适用于聚簇索引。索引下推仅适用于SELECT语句,不适用于UPDATE、DELETE这些需要改动记录的语句。
    • 在存在索引条件下推的条件时,如果当前记录符合索引下推的条件,则跳到步骤四继续执行;如果不符合,则直接获取到当前记录所在单向链表的下一条记录,将该记录作为新的当前记录,并跳回步骤二。另外,步骤三还会判断当前记录是否符合形成扫描区间的边界条件,如果不符合,则跳过步骤四和步骤五,直接向server层返回一个”查询完毕“的信息。---这一步不会释放锁
  • 步骤四:如果读取的是二级索引记录,则需要进行回表操作,获取到对应的聚簇索引记录并给该聚簇索引记录加记录锁
  • 步骤五:判断边界条件是否成立--如果该记录符合边界条件,则跳到步骤6执行,否则在隔离级别不大于READ COMMITTER时,就要释放掉加在该记录上的锁。在隔离级别不小于RR时,不释放加在该记录上的锁,并且向server层返回一个查询完毕的信息
  • 步骤六:server层判断其他搜索条件是否成立--除了索引下推中的条件以外,server层还需要判断其他搜索条件是否成立。如果成立,则将该记录发送到客户端,否则在隔离级别不大于READ COMMITTER时,就要释放掉加在该记录上的锁。在隔离级别不小于RR时,不释放加在该记录上的锁
  • 步骤七:获取当前记录所在单向链表的下一条记录,并讲其作为新的当前记录,并跳回到步骤二

实例---如果走的是二级索引,存在索引下推的情况分析

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for hero
-- ----------------------------
DROP TABLE IF EXISTS `hero`;
CREATE TABLE `hero`  (
  `number` int NOT NULL,
  `name` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `country` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  PRIMARY KEY (`number`) USING BTREE,
  INDEX `idx_name`(`name` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of hero
-- ----------------------------
INSERT INTO `hero` VALUES (1, 'l刘备', '蜀');
INSERT INTO `hero` VALUES (3, 'z诸葛亮', '蜀');
INSERT INTO `hero` VALUES (8, 'c曹操', '魏');
INSERT INTO `hero` VALUES (15,'x荀彧', '魏');
INSERT INTO `hero` VALUES (20, 's孙权', '吴');

SET FOREIGN_KEY_CHECKS = 1;

SELECT * FROM hero FORCE INDEX(idx_name) WHERE name > 'c曹操' AND name <='x荀彧'AND country !='吴' LOCK IN SHARE MODE

在隔离级别不大于RC时的加锁情况

对name值为'l刘备'的二级索引记录进行分析

  • 步骤一:读取在('c曹操','x荀彧']扫描区间的第一条二级索引记录,也就是name值为'l刘备'的二级索引记录
  • 步骤二:为name值为'l刘备'的二级索引记录加S型记录锁
  • 步骤三:'l刘备'符合索引下推的条件
  • 步骤四:因为是二级索引记录,所以需要对记录执行回表操作,找到相应的聚簇索引记录,也就是number为1的聚簇索引记录,然后为该聚簇索引记录加上一个S型记录锁
  • 步骤五:符合边界条件
  • 步骤六:server层判断'l刘备'的二级索引符合条件country!='吴',然后发送给客户端,并且不释放加在该记录上的锁
  • 步骤七:获取刘备所在单链表的下一条记录,也就是孙权的二级索引记录

对name值为's孙权'的二级索引记录进行分析

  • 步骤二:加S型记录锁
  • 步骤三:符合索引下推条件
  • 步骤四:回表 找到对应的聚簇索引记录,给对应的聚簇索引记录加S型记录锁,也就是 number = 20
  • 步骤五:符合边界条件
  • 步骤六:server层判断country的条件不符合,释放掉加在二级索引记录上以及对应的聚簇索引记录上的锁
  • 步骤七:获取孙权所在单链表的下一条记录,也就是荀彧的二级索引记录

对name值为'x荀彧'的二级索引记录进行分析

  • 步骤二:加S型记录锁
  • 步骤三:符合索引下推条件
  • 步骤四:回表 找到对应的聚簇索引记录,给对应的聚簇索引记录加S型记录锁,也就是 number = 15
  • 步骤五:符合边界条件
  • 步骤六:server层判断country的条件符合,将其发送给客户端,并且不释放加在该记录上的锁
  • 步骤七:获取所在单链表的下一条记录,也就是'z诸葛亮'的二级索引记录

对name值为'z诸葛亮'的二级索引记录进行分析

  • 步骤二:加S型记录锁
  • 步骤三:不符合索引下推条件,且不符合边界条件条件。不再去找下一条记录,直接向server层报告"查询完毕"信息
  • 步骤四:跳过
  • 步骤五:跳过
  • 步骤六:server层收到存储引擎层报告的"查询完毕"信息,结束查询

需要注意的是:对于 孙权的二级索记录以及对应的number=20的聚簇索引记录,都是先加锁后释放锁。对应'z诸葛亮'的二级索引在步骤三被判断不符合边界条件,而且该步骤并不会释放加在该记录上的锁,而是直接向server层报告'查询完成'信息,因此导致整个语句在执行结束后也不会释放加在name值为'z诸葛亮'的二级索引记录上的锁

在隔离级别不小于RR时的加锁过程

对name值为'l刘备'的二级索引记录进行分析

  • 步骤二:这里与RC不同不同的是加的是S型next-key锁
  • 步骤三:'l刘备'符合索引下推的条件
  • 步骤四:因为是二级索引记录,所以需要对记录执行回表操作,找到相应的聚簇索引记录,也就是number为1的聚簇索引记录,然后为该聚簇索引记录加上一个S型记录锁----这里与RC加的锁相同
  • 步骤五:符合边界条件
  • 步骤六:server层判断'l刘备'的二级索引符合条件country!='吴',然后发送给客户端,并且不释放加在该记录上的锁
  • 步骤七:获取刘备所在单链表的下一条记录,也就是孙权的二级索引记录

对name值为's孙权'的二级索引记录进行分析

  • 步骤二:加S型next-key锁
  • 步骤三:符合索引下推条件
  • 步骤四:回表 找到对应的聚簇索引记录,给对应的聚簇索引记录加S型记录锁,也就是 number = 20
  • 步骤五:符合边界条件
  • 步骤六:server层判断country的条件不符合,但是由于现在的隔离级别不小于RR,所以不释放掉加在该记录上的锁
  • 步骤七:获取孙权所在单链表的下一条记录,也就是荀彧的二级索引记录

对name值为'x荀彧'的二级索引记录进行分析

  • 步骤二:加S型next-key锁
  • 步骤三:符合索引下推条件
  • 步骤四:回表 找到对应的聚簇索引记录,给对应的聚簇索引记录加S型记录锁,也就是 number = 15
  • 步骤五:符合边界条件
  • 步骤六:server层判断country的条件符合,将其发送给客户端,并且不释放加在该记录上的锁
  • 步骤七:获取所在单链表的下一条记录,也就是'z诸葛亮'的二级索引记录

对name值为'z诸葛亮'的二级索引记录进行分析

  • 步骤二:加S型next-key锁
  • 步骤三:不符合索引下推条件,且不符合边界条件条件。不再去找下一条记录,直接向server层报告"查询完毕"信息
  • 步骤四:跳过
  • 步骤五:跳过
  • 步骤六:server层收到存储引擎层报告的"查询完毕"信息,结束查询

在隔离级别不小于RR的情况下,该语句在执行过程中对'l刘备,S孙权,X荀彧,Z诸葛亮'的二级索引记录都加上S型next-key锁对number值为1、15、20的聚簇索引记录加S型正经记录锁。

SELECT...FOR UPDATE语句的加锁过程

  • SELECT...FOR UPDATE加锁过程与SELECT...LOCK IN SHARE MODE语句类型,只不过为记录加的是X锁。

Update语句加锁分析

Update语句加锁的方式与SELECT...FOR UPDATE类似,不同的是,如果更新了二级索引列,那么所有被更新的二级索引记录在被更新之前都需要加入X型正经记录锁。

UPDATE hero SET name = 'cao曹操' where number > 1 AND number <=15 AND country = '魏'

因为会更新name列,所有在更新前需要为idx_name二级索引中对应的记录加锁。

在不大于RC级别下该语句会给 'c曹操'和'x荀彧'的聚簇索引记录和二级索引记录加锁,对于number = 3的记录来说,由于它不符合country='魏'这个条件,所以先加锁后释放锁。对于number = 20的聚簇索引记录来说,由于不符合边界条件,所以也是先加锁后释放锁。

在不小于RR级别下,number列 number = 3、8、15、20列会被加上next-key锁,二级索引列name ='c曹操'、'x荀彧'会被加上X型的记录锁

加锁分析:

UPDATE hero SET name = 'cao曹操' where number > 1 AND number <=15 AND country = '魏'

  • 首先定位到第一个不下于1的记录为number = 3,给该记录加上next-key锁
  • 因为是聚簇索引不存在索引下推和回表
  • server判断符合country条件不符合。

  • 再找到number = 8,给该记录加上next-key锁
  • server判断符合country条件符合所以准备更新列,然后去锁对应的二级索引。

  • 再找到对应的number = 15 ,给该记录加上next-key锁
  • server判断符合country条件符合锁对应的二级索引

  • 再找到number = 20,给该记录加上next-key锁
  • 由于不符合边界条件,所以直接结束。

Delete语句加锁分析

对于Delete语句,加锁的方式与SELECT...FOR UPDATE语句类似,只不过如果表中包含二级索引,那么二级索引记录在被删除之前都需要加上X型记录锁。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值