当前位置: 首页 > article >正文

开发易忽视的问题:InnoDB 行锁设计与实现

开发易忽视的问题:InnoDB 行锁设计与实现

存储模型和锁机制

存储结构

  • 数据页

    • InnoDB 将表的数据存储在数据页中,每个页默认大小为 16KB。
    • 数据页中存储多个行记录,行记录按照主键顺序存放。

    行格式

    • InnoDB 支持多种行格式,包括 Compact、Redundant、Dynamic 和 Compressed。这些格式影响数据的存储方式,但对行锁机制影响不大。

    索引组织表

    • InnoDB 是一种索引组织表(Index-Organized Table),即数据按主键聚集存储,这意味着行数据实际上存储在 B+ 树的叶子节点上。
    • 非主键索引以主键作为叶子节点指向的数据行的指针。

行锁实现

  • 基于索引的锁定

    • 行级锁是基于索引实现的,具体来说,InnoDB 锁定的是索引上的记录,而不是实际的数据行。
    • 当事务要锁定一行时,它实际上是在锁定该行所在索引的一个“间隙”或确切的索引键。

    锁的类型

    • Record Lock:锁定单个索引记录。
    • Gap Lock:锁定索引记录之间的间隙,用于防止幻影读,通常用于范围查询。
    • Next-Key Lock:结合了 Record Lock 和 Gap Lock,在索引记录及其前面的间隙上加锁。这样可以防止其他事务插入新的索引记录。

    锁的存储

    • 锁信息并不是直接存储在行本身,而是存储在内存中的锁表中。
    • 锁表包含有关每个锁的信息,如锁的类型、被锁定的事务等。

    压缩行锁

    • 为了减少锁的开销和提高效率,InnoDB 使用了一种称为锁压缩(Lock Compression)的技术,使得对于连续范围内的锁,仅需要维护较少数量的锁对象。

    意向锁

    • 在表级别实现的一种锁,它表示某个事务想要在行级别获得锁。这种设计使得 InnoDB 能够快速判断是否可以安全地对整个表进行锁定。

实际操作

  • 锁定机制依赖于索引,因此合理设计索引可以有效地利用行级锁。
  • 当没有适当的索引来支持查询时,InnoDB 可能会退化到锁定更多的行甚至整个表。

锁表分析

  • InnoDB 的锁表是用来管理和维护所有活动事务的锁信息的关键组件。锁表并不是一个物理存储的表,而是一种内存数据结构,其设计与实现旨在高效地处理并发事务,确保数据一致性和完整性。

锁表的设计和实现

  • 锁结构

    • 每个锁都有一个锁结构,用于表示特定事务对某个资源(比如行或间隙)的锁定。
    • 锁结构包含的信息包括:被锁定的资源、锁类型(如共享锁、排他锁)、拥有锁的事务ID等。

    锁的存储

    • 锁信息保存在内存中,具体来说,是通过一个全局的哈希表来维护所有当前活动的锁。
    • 这个哈希表以被锁定的资源为键,以相应的锁结构链表为值。这样可以快速查找某个资源上的锁。

    锁类型

    • InnoDB 支持多种类型的锁,如前面提到的 Record Lock、Gap Lock 和 Next-Key Lock。每种锁类型在锁表中都有不同的表现形式和处理逻辑。
    • 锁的类型决定了如何对其他事务进行阻塞或允许并发访问。

    锁兼容性

    • 锁表需要处理锁的兼容性问题,即检测新请求的锁是否与现有锁冲突。共享锁与共享锁兼容,但排他锁则与其他任何锁冲突。
    • 通过锁兼容矩阵,InnoDB 可以有效地决定是否授予新的锁请求。

    死锁检测

    • InnoDB 实现了自动死锁检测机制,通过分析锁表中的锁依赖图来判断是否存在死锁循环
    • 一旦发现死锁,InnoDB 会主动回滚其中一个事务,以解除死锁状态。

    锁等待队列

    • 对于因锁冲突而无法立即获得的锁请求,InnoDB 将其放入锁等待队列中。
    • 当锁释放时,InnoDB 会检查锁等待队列,并尝试授予队列中合适的锁请求。

    性能优化

    • 为了减少锁开销,InnoDB 使用了一些优化技术,比如锁压缩。这种技术在可能的情况下将多个相邻范围的锁合并成一个,从而减少锁对象的数量。

案例分析

  • 假设我们有一个名为 employees 的表,其中包含如下字段:id(主键),namesalary。该表的数据如下:

  • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 现在我们有两个事务对这个表进行操作:

    • 事务A:将 Bob 的薪水增加 1000。
    • 事务B:尝试读取 Bob 和 Carol 的信息。

操作步骤及锁机制

  • 事务A开始

    • 执行 START TRANSACTION;
    • 执行 UPDATE employees SET salary = salary + 1000 WHERE name = 'Bob';
    • 假如name字段没有添加索引,InnoDB 使用主键索引进行全表扫描,来定位 Bob 的行并获取行级锁(Record Lock)在 id=2 上,因为这是精确的行更新。

    事务B开始

    • 执行 START TRANSACTION;
    • 执行 SELECT * FROM employees WHERE name IN ('Bob', 'Carol') FOR UPDATE;
    • 因为事务A已经持有了 id=2 的排他锁,事务B将在此处被阻塞,等待事务A释放对 Bob 的锁。

    锁表存储与管理

    • 当事务A执行更新时,InnoDB 会在内存中的锁表中创建一个记录锁条目,标记该行(id=2)已被锁定,并且事务A拥有一个排他锁。
    • 锁表的哈希结构会以 id=2 为键,指向一个链表,该链表包含事务A的锁信息。
    • 当事务B尝试获取锁时,会检查锁表,发现冲突,因此会将其请求放入等待队列中,关联到相同的哈希键(即 id=2)。

    事务A提交或回滚

    • 事务A完成后,执行 COMMIT;ROLLBACK;
    • InnoDB 释放 id=2 的排他锁,从锁表中移除相应的锁条目。
    • 此时,事务B会从等待队列中唤醒,重新尝试获取锁,现在它能够获取到 id=2 的共享锁或排他锁(视具体操作而定)。

    事务B继续

    • 事务B成功获得锁后,可以读取 Bob 和 Carol 的信息。
    • 完成后执行 COMMIT; 释放所有锁。

结论

  • 通过这个案例,我们可以看到 InnoDB 如何使用行级锁和锁表来管理并发访问。锁表是一个中间存储,用于跟踪当前所有活动事务的锁状态

http://www.kler.cn/news/313001.html

相关文章:

  • Pycharm中虚拟环境依赖路径修改
  • LeetCode 面试经典150题 67.二进制求和
  • istio中使用serviceentry结合egressgateway实现多版本路由
  • JFinal整合Websocket
  • 大模型中常见 loss 函数
  • 关于“华为杯”第二十一届中国研究生数学建模竞赛赛题下载及提交作品的重要提醒
  • pytorch实现RNN网络
  • Vue使用qrcodejs2-fix生成网页二维码
  • 解决 GitLab CI/CD 中的 `413 Request Entity Too Large` 错误
  • 生信初学者教程(五):R语言基础
  • 【计算机网络篇】电路交换,报文交换,分组交换
  • BGP实验
  • Percona发布开源DBaaS平台;阿里云RDS发布全球多活数据库(GAD);Redshift支持自然语言生成SQL
  • Pyspark dataframe基本内置方法(4)
  • 【有啥问啥】弱监督学习新突破:格灵深瞳多标签聚类辨别(Multi-Label Clustering and Discrimination, MLCD)方法
  • QT 将文字矢量化,按照设置的宽和高绘制
  • 3657A/B/AM/BM矢量网络分析仪
  • CSS - 通用左边图片,右边内容,并且控制长度溢出处理模板(vue | uniapp | 微信小程序)
  • python画图|曲线分段设置颜色基础教程
  • 什么是3D展厅?有何优势?怎么制作3D展厅?
  • 蓝星多面体foc旋钮键盘复刻问题详解
  • JVM java主流的追踪式垃圾收集器
  • docker 镜像,导入导出,
  • 【数据结构入门】排序算法之三路划分与非比较排序
  • 基于OpenCV的YOLOv5图片检测
  • 寄存器二分频电路
  • Serverless架构
  • 【C/C++语言系列】实现单例模式
  • golang学习笔记23——golang微服务中服务间通信问题探讨
  • 【ShuQiHere】 探索 IEEE 754 浮点数标准:以 57.625 和 -57.625 为例