MySQL InnoDB 的锁:一次从“守卫”到“交通指挥中心”的深度之旅

MySQL InnoDB 的锁。这个概念常常让人觉得复杂抽象,但我们需要抓住它的底层设计哲学

忘记那些代码和术语定义,我们先从最底层的问题开始思考:

思考一:为什么我们需要锁?

想象一下,你和你的朋友同时去取款机取钱。你取100,他取200。如果没锁,可能发生什么?

  • 你看到余额1000,他同时看到余额1000。
  • 你取100,系统计算:1000 - 100 = 900,还没写入。
  • 他取200,系统计算:1000 - 200 = 800,还没写入。
  • 你写入900。
  • 他写入800。

最终余额变成了800,而不是你预期的700。这就是典型的并发问题——数据不一致

锁,就是为了解决这个“并发冲突”而生的“守卫”。它确保在某个时刻,对某个资源的操作是“有秩序”的,不会出现混乱。

思考二:这个“守卫”长什么样?它在哪里站岗?

如果你接触过 Java 的 synchronized 关键字,你可能会觉得,锁就是每个对象上“自带”的一把小锁。对象被访问时,自己检查自己的锁状态。

但数据库的锁远非如此简单!

数据库要处理的数据量是海量的,并发事务可能是成千上万的。如果每个数据行、每个数据页都自带一把锁,会有以下问题:

  1. 管理噩梦:成千上万的小锁,怎么知道哪些被占用?哪些在等待?
  2. 效率低下:每次访问数据都要去数据本身那里检查锁状态,频繁的磁盘I/O(数据在磁盘上)。
  3. 死锁侦探难上加难:无法构建全局视图,发现复杂的循环等待(死锁)。

所以,InnoDB 采取了一种截然不同的、更高级的、更具“智慧”的设计:中心化的锁管理

数据库的“守卫”,不是分散在每个数据旁边的个体户,而是一个独立的、高智能的“中央交通指挥中心”——我们称之为 Lock Manager。

这个 Lock Manager 驻扎在内存中,它不直接触摸磁盘上的数据,而是:

  • 观察:所有事务对数据的请求,都会先汇报给 Lock Manager。
  • 记录:Lock Manager 维护着一张全球地图级别的“交通登记簿”。
  • 协调:它根据规则,决定放行哪个请求,暂停哪个请求。
  • 纠察:它甚至能发现“死锁”这种复杂堵车,然后主动干预。
思考三:中央指挥中心如何管理“交通”?核心数据结构

Lock Manager 的“交通登记簿”并非简单的一本大账本,它内部是高度优化的数据结构。

核心武器:哈希表与链表

  1. 资源抽象 (LockResource):

    • 本质:“交通管制区域的精确标识”。
    • 在数据库里,我们要锁的“东西”很多:可能是一张products),可能是表里特定的一行数据id=10),还可能是两行数据之间的**“空隙”**(值为 510 之间的区域)。
    • Lock Manager 会把这些“东西”标准化,生成一个唯一的、可哈希的 LockResource 对象。它就像一个坐标系,能精准定位到被锁的区域。
    • 例子
      • {Type: TABLE_LOCK, TableId: product_table_ID}
      • {Type: RECORD_LOCK, TableId: product_table_ID, IndexId: primary_key_ID, RecordIdentifier: id_10}
      • {Type: GAP_LOCK, TableId: product_table_ID, IndexId: price_index_ID, GapBoundaries: (75, 300)}
  2. 锁对象 (LockObject):

    • 本质:“通行凭证卡片”。
    • 每个事务(TxID)发起一个锁请求时,Lock Manager 内部就会创建一个 LockObject
    • 这张卡片记录着:
      • 哪种通行模式? (锁模式):
        • X_LOCK (Exclusive Lock):独占通行,别人都不能过,包括看。
        • S_LOCK (Shared Lock):共享通行,大家都可以看,但谁都不能改。
        • IX_LOCK (Intention Exclusive Lock):我准备独占某个区域的小部分通行权。
        • IS_LOCK (Intention Shared Lock):我准备共享某个区域的小部分通行权。
        • GAP_LOCK (Gap Lock):我把一条路中间的空地拦起来,不让新车插队。
        • INSERT_INTENTION_LOCK:我想插队进去,如果空地被拦了我就等着。
      • 哪个司机持有? (事务ID:TxID)
      • 现在是不是在排队? (isWaiting: true/false)
      • 这张卡片是给哪个区域的? (TargetResource: LockResource)
  3. 全局锁哈希表 (globalLockHashTable):

    • 本质:“交通地图上的地标索引”。
    • 这是一个巨大的哈希表,它的键就是 LockResource(某个被锁定的区域标识)。
    • 它的值是一个链表,链表里面串着所有对这个 LockResource 发出的 LockObject。这个链表就像某个路口前排队的车辆,有的已经通行,有的还在等待。
    • 例子:当你查询 id=10 的锁状态时,Lock Manager 快速哈希到 Resource_Record_id_10 的位置,然后遍历其链表,就能知道谁锁了它,锁的模式是什么。
  4. 事务锁列表 (transactionLocksMap):

    • 本质:“每个司机的个人行驶记录本”。
    • 这是一个映射表,键是事务ID (TxID)
    • 值是该事务当前持有的所有 LockObject 的列表
    • 它的作用是,当一个事务提交或回滚时,Lock Manager 能迅速找到并释放它所持有的所有锁,而不需要扫描整个 globalLockHashTable

数据结构可视化:

否: 首次锁定
是: 资源已被锁定
是: 兼容
否: 不兼容
是: 自己持有冲突锁
能升级且无其他冲突
不能升级或有其他冲突
否: 其他事务持有冲突锁
起始: SQL语句请求锁
步骤1: 解析请求
生成LockResource
(例如: id=10的行)
步骤2: 查globalLockHashTable
是否有该资源的LockObject链表?
步骤3: 创建新的LockObject
Mode: X_LOCK, TxId: CurrentTx, isWaiting: false
步骤4: 将LockObject添加到
globalLockHashTable[LockResource]
并加入transactionLocksMap[TxId]
步骤5: 通知事务: 锁已获取
继续执行
步骤3: 遍历现有LockObject链表
检查兼容性 (LockMode.areCompatible?)
步骤4: 持有冲突锁的是否是
当前事务 (TxId) 自己?
步骤5: 尝试锁升级?
(例如S->X)
更新/替换LockObject模式
步骤6: 创建LockObject
isWaiting: true
步骤7: 加入等待队列
启动死锁检测
步骤8: 通知事务: 进入等待/被回滚
死锁检测
(构建等待图, 发现循环则回滚)
思考四:这些锁在各种 SQL 语句下如何站岗?

现在,我们把 Lock Manager 和它的“指挥”规则,与具体的 SQL 语句结合起来:

  1. INSERT (插入数据):

    • 表级:IX_LOCK。声明意图:我准备在表里加行了。
    • 间隙锁:INSERT_INTENTION_LOCK。我要往某个空隙里插车,得先拿到这块空地的“待插入”停车位票。如果这个空位被其他事务的 GAP_LOCK 挡住了,就得等待。
    • 行级:X_LOCK。新插入的行,当然得我独占,直到事务提交。
  2. UPDATE / DELETE (修改/删除数据):

    • 表级:IX_LOCK。同插入。
    • 行级:X_LOCK。无论修改还是删除,都得独占目标行。
    • 间隙/Next-Key Lock (尤其在 REPEATABLE READ 隔离级别,对范围查询):
      • 扫描到的每一条索引记录都会加 X_LOCK
      • 扫描过程中经过的所有索引间隙都会加 GAP_LOCK
      • Next-Key LockRECORD_LOCKGAP_LOCK 的组合,它锁定的是一个“区间” (前一个记录, 当前记录],用于彻底防止幻读和重复读。
  3. SELECT ... FOR UPDATE (查询并锁定,以备更新):

    • 表级:IX_LOCK
    • 行级:X_LOCK。对所有匹配到的行加 X_LOCK
    • 间隙/Next-Key Lock:行为与 UPDATE 类似,对扫描路径上的所有间隙加 GAP_LOCK。即使查询结果为空,但如果涉及到范围扫描,相关间隙仍可能被锁定。
  4. SELECT ... FOR SHARE (查询并共享锁定):

    • 表级:IS_LOCK。声明意图:我准备在表里看几行了。
    • 行级:S_LOCK。对所有匹配到的行加 S_LOCK。允许多个事务同时读取,但阻止任何事务加 X_LOCK 进行修改。
    • 间隙/Next-Key Lock:行为与 SELECT ... FOR UPDATE 类似,对扫描路径上的所有间隙加 GAP_LOCK
  5. 普通 SELECT (SELECT * FROM ...):

    • 在 InnoDB 的默认 REPEATABLE READ 隔离级别下,普通查询不加行锁。它依赖于 MVCC (多版本并发控制) 机制。
    • MVCC 提供的是一个事务开始时的快照视图,数据被读取的是历史版本,从而避免了读写冲突,提高了并发性。所以,Lock Manager 在这里不介入。

MySQL InnoDB 的锁机制,是一个精巧并发控制系统。

  • 它不是对数据的物理修改,而是权限的逻辑管理。
  • 它不是分散的个体行动,而是中心化的统一调度。
  • 它通过抽象资源、类型化锁、集中管理、智能判断,实现了在极致并发下对数据完整性和事务隔离性的滴水不漏的守护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94581.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94581.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/94581.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CVE-2020-24557

一、漏洞原理 CVE-2020-24557 是 D-Link 路由器(如 DIR-816L2) 中存在的一个 命令注入(Command Injection) 漏洞,其核心原理为: ①路由器的管理界面(Web 或 CGI 接口)在处理某些用户…

proxychains——Linux代理工具

简介 ProxyChains是一个UNIX程序,通过预加载动态库(dlsym(),LD_PRELOAD)劫持动态链接程序中与网络相关的libc函数,将连接重定向至SOCKS4a/5或HTTP代理。仅支持TCP协议(不支持UDP/ICMP等)。它的工作方式基本…

精确调控建筑环境,楼宇自控系统大幅提升居住舒适度

在现代城市化进程中,建筑环境的舒适度已成为衡量生活质量的重要指标。随着科技的飞速发展,楼宇自控系统(Building Automation System, BAS)正以前所未有的精准度重新定义人与空间的互动关系。这套集成了物联网、大数据和人工智能的…

Echarts中的水波图、水球图、水半球实现的详细步骤(vue)

目录 一、实现效果 二、实现步骤 1. 安装ECharts和Liquid Fill插件 2. 创建一个组件 3.在创建的vue中引入ECharts和Liquid Fill插件 4.在组件中初始化ECharts和Liquid Fill插件 5.完整代码 一、实现效果 Echarts中的水位图(水波图、水球图、水半球)…

Vue父组件向子组件传递一个动态的值,子组件如何保持实时更新实时更新?

父组件 通过 :issueDeptId this.form109.issueDeptId传数据到子组件 <inv-info ehco-data"selectOutInvId" :purposeId this.form109.purposeId:issueDeptId this.form109.issueDeptId:projectNo this.form109.projectNo:invPhysicIds this.form109.issuePh…

如何通过主数据治理重构企业系统竞争力

在当前企业数字化转型持续深化的背景下&#xff0c;IT系统复杂度与数据规模呈指数级增长。CRM、ERP、HRM、供应链、电商平台等多系统并行运作已成为常态。然而&#xff0c;随之而来的主数据&#xff08;Master Data&#xff09;管理难题&#xff0c;正日益成为制约系统稳定性、…

c++ 中 原子锁、互斥锁、自旋锁的区别和详细用法

用最生活化的比喻来解释 C 中原子锁、互斥锁和自旋锁的区别和用法&#xff0c;让小白也能秒懂&#xff01;&#x1f604;想象你 ​​&#xff08;线程&#xff09;​​ 要去公共更衣室 ​​&#xff08;共享资源&#xff0c;如变量、数据结构&#xff09;​​ 换衣服。这个更衣…

RabbitMQ面试精讲 Day 12:镜像队列与Quorum队列对比

【RabbitMQ面试精讲 Day 12】镜像队列与Quorum队列对比 开篇&#xff1a;面试价值与核心要点 在RabbitMQ集群环境中&#xff0c;如何保证消息的高可用性是最常被问及的面试问题之一。今天我们将深入探讨RabbitMQ提供的两种高可用队列实现方案&#xff1a;经典镜像队列(Mirror…

Maven 常用命令详解

前言 Apache Maven 是 Java 项目管理和构建自动化工具&#xff0c;它通过一个项目对象模型&#xff08;POM, Project Object Model&#xff09;来管理项目的构建、报告和文档。Maven 的核心优势在于其强大的依赖管理、标准化的项目结构以及丰富的插件生态系统。掌握 Maven 的常…

Android Studio切换到经典UI,老UI

为什么要写这边文章&#xff0c;我自己的android studio年儿半载安装一次新的&#xff0c;所以记录一下。1、新android studio安装后都是新工作室风格的UI。2、菜单File ->Settings ,在Setting界面将Enable New UI 的勾选去掉&#xff0c;点击OK&#xff0c;重启Android Stu…

走进“Mesh无线自组网”:开启智能家居和智慧工厂

随着科技的迅猛发展&#xff0c;互联网已经渗透到我们生活的方方面面&#xff0c;而其中最引人瞩目的便是“智能家居”和“智慧工厂”。这些新兴领域的飞速发展&#xff0c;离不开底层网络技术的支持。众多的网络通信技术中&#xff0c;Mesh无线自组网以其独特的优势&#xff0…

力扣热题100——双指针

双指针两数之和&#xff08;有序数组&#xff0c;相向双指针&#xff09;问题&#xff1a;在有序数组中找到两个数&#xff0c;使它们的和等于目标值。思路&#xff1a;左指针从起点出发&#xff0c;右指针从终点出发&#xff0c;根据和与目标值的大小调整指针。 #include <…

AI Infra与LLM的联系与差异

一、定义与定位LLM&#xff08;大语言模型&#xff09; 定义&#xff1a;基于海量文本训练的深度学习模型&#xff0c;通过Transformer架构实现语言理解与生成&#xff0c;典型代表如GPT-4、通义千问等。定位&#xff1a;AI应用的核心能力层&#xff0c;直接面向用户提供文本生…

数据结构-双链表

学习完单链表&#xff0c;现在继续学习双链表一、双链表结构带头双向循环链表&#xff08;简称&#xff1a;双链表&#xff09;注意&#xff1a;这⾥的“带头”跟前面我们说的“头节点”是两个概念&#xff0c;实际前面的在单链表阶段称呼不严谨&#xff0c;但是为了同学们更好…

福彩双色球第2025090期篮球号码分析

明天是星期四&#xff0c;明天晚上双色球开奖。福彩双色球第2025090期篮球号码分析&#xff0c;上期开出号码05&#xff0c;数字形式是质数奇数2路球&#xff0c;小号0字头数字。本期篮球号码分析&#xff0c;篮球2尾数0212遗漏6期上次遗漏27期&#xff0c;篮球3尾数0313遗漏4期…

Python爬虫实战:研究Photon工具,构建企业信息收集系统

1. 引言 1.1 研究背景 在数字化时代,互联网作为全球最大的信息载体,涵盖商业情报、学术资源、公共信息等多个领域,对企业决策、学术研究和社会治理具有重要参考价值。传统信息获取方式依赖人工检索和简单脚本爬取,存在效率低下、覆盖范围有限、数据处理能力不足等问题。 …

Python Pandas.lreshape函数解析与实战教程

Python Pandas.lreshape 函数解析与实战教程 摘要 本教程旨在提供一份关于Pandas库中 pandas.lreshape 函数的全面使用教程和分析。lreshape 是一个用于数据重塑(Data Reshaping)的工具,具体而言,它擅长将“宽格式”(Wide Format)数据转换为“长格式”(Long Format)数…

vue3 el-dialog自定义实现拖拽、限制视口范围增加了拖拽位置持久化的功能

采用element-plus的拖拽功能代码,在此基础上增加了记忆拖拽上次拖拽位置的功能,开袋即食; 前提:每次关闭弹窗都要销毁; 解决了默认设置transform的偏移量后首次拖拽弹窗偏移量错误的问题修改。<template><el-dialogref="popupRefDialog":title="…

学习嵌入式之硬件——ARM体系

一、ARM内核基础知识1.ALU&#xff1a;算术逻辑单元&#xff1b;完成运算的电路2.通用寄存器&#xff1a;R0~R15R13&#xff08;SP&#xff09;&#xff1a;栈指针寄存器&#xff1a;指向栈顶的位置&#xff1b;并在函数调用、中断处理等场景中自动更新。R14&#xff08;LR&…

微信小程序中使用TensorFlowJS从环境搭建到模型训练及推理模型得到预测结果

1、小程序端环境准备app.json"plugins": {"tfjsPlugin": {"version": "0.2.0","provider": "wx6afed118d9e81df9"}}package.json"dependencies": {"tensorflow-models/posenet": "^2.2.…