• 锁执行的过程


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

    • 步骤一:首先快速地在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型记录锁。

     

  • 相关阅读:
    (☞゚ヮ゚)☞【精品C语言整理】☜(゚ヮ゚☜)女盆友缠着你让你教她写代码怎么办?安排,三万字博文带你走遍C语言,从此不再害怕编程
    常见的网络协议
    基于单片机的语音存储与回放系统设计
    我的私人笔记(安装hive)
    Java培训之java8新特性程序代码
    Spring MVC组件之HandlerMapping
    表单页面优化之小细节大改变
    js函数相关知识详解
    Python+Django+Html网页前后端指纹信息识别
    怎么修改jupyter lab 的工作路径而不是直接再桌面路径打开
  • 原文地址:https://blog.csdn.net/m0_58438555/article/details/126214087