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

6.Mysql锁机制与优化实践以及MVCC底层原理剖析

Mysql锁机制与优化实践以及MVCC底层原理剖析

    • 📋 知识体系总览
    • 一、锁机制基础
      • ✅1. 锁的概念与分类
      • ✅2. 读锁与写锁
        • 读锁(共享锁,S锁 Shared)
        • 写锁(排它锁,X锁 eXclusive)
      • ✅3. 意向锁(Intention Lock)
      • 📊 小结:锁分类对比表
    • 二、表锁
      • ✅1. 表锁特性与基本操作
    • 三、页锁
      • ✅1. 页锁特性
    • 四、行锁
      • ✅1. 行锁特性
      • ✅2. 行锁升级为表锁的问题
      • ✅3. RR级别行锁升级为表锁的原因分析
      • 📊 表锁 vs 行锁对比
    • 五、间隙锁与临键锁
      • ✅1. 间隙锁(Gap Lock)
      • ✅2. 临键锁(Next-Key Locks)
      • 📊 三种行级锁对比
    • 六、锁等待与死锁分析
      • ✅1. 锁等待状态分析
      • ✅2. INFORMATION_SCHEMA锁相关数据表
      • ✅3. 死锁问题分析
      • 📊 锁问题排查命令速查表
    • 七、锁优化实践
    • 八、MVCC多版本并发控制机制
      • ✅1. MVCC概念与原理
      • ✅2. undo日志版本链
      • ✅3. read view机制详解
      • ✅4. 版本链比对规则
      • ✅5. 删除数据的特殊处理
      • ✅6. RR与RC隔离级别的MVCC实现差异
      • 📊 RR vs RC隔离级别MVCC实现对比
      • 📊 MVCC总结
    • 📋 全文总结
      • ✅1. 锁分类体系
      • ✅2. 行锁升级问题
      • ✅3. 间隙锁与幻读
      • ✅4. 锁优化核心原则
      • ✅5. MVCC实现原理
      • ✅6. 死锁排查
      • ✅7. 关键SQL速记

📋 知识体系总览

Mysql锁机制与MVCC底层原理 ├── 一、锁机制基础 │ ├── ✅1. 锁的概念与分类 │ ├── ✅2. 读锁(共享锁)与写锁(排他锁) │ ├── ✅3. 意向锁(IS/IX) │ └── 📊 小结:锁分类对比表 ├── 二、表锁 │ └── ✅1. 表锁特性与基本操作 ├── 三、页锁 │ └── ✅1. 页锁特性 ├── 四、行锁 │ ├── ✅1. 行锁特性 │ ├── ✅2. 行锁升级为表锁的问题 │ └── ✅3. RR级别行锁升级原因分析 ├── 五、间隙锁与临键锁 │ ├── ✅1. 间隙锁(Gap Lock) │ └── ✅2. 临键锁(Next-Key Locks) ├── 六、锁等待与死锁分析 │ ├── ✅1. 锁等待状态分析 │ ├── ✅2. INFORMATION_SCHEMA锁相关表 │ └── ✅3. 死锁问题分析 ├── 七、锁优化实践 ├── 八、MVCC多版本并发控制机制 │ ├── ✅1. MVCC概念与原理 │ ├── ✅2. undo日志版本链 │ ├── ✅3. read view机制详解 │ ├── ✅4. 版本链比对规则 │ ├── ✅5. 删除数据的特殊处理 │ └── ✅6. RR与RC隔离级别的MVCC实现 └── 📋 全文总结

一、锁机制基础

✅1. 锁的概念与分类

锁是计算机协调多个进程或线程并发访问某一资源的机制。

在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

📝 核心:锁是数据库保证并发访问一致性的核心机制,锁冲突直接影响数据库并发性能。

锁从不同维度的分类:

  • 从性能上分:乐观锁(用版本对比或CAS机制)和悲观锁,乐观锁适合读操作较多的场景,悲观锁适合写操作较多的场景,如果在写操作较多的场景使用乐观锁会导致比对次数过多,影响性能
  • 从对数据操作的粒度分:表锁页锁行锁
  • 从对数据库操作的类型分:读锁写锁(都属于悲观锁),还有意向锁

✅2. 读锁与写锁

读锁(共享锁,S锁 Shared)

针对同一份数据,多个读操作可以同时进行而不会互相影响,比如:

select*fromTwhereid=1lockinsharemode
写锁(排它锁,X锁 eXclusive)

当前写操作没有完成前,它会阻断其他写锁和读锁,数据修改操作都会加写锁,查询也可以通过for update加写锁,比如:

select*fromTwhereid=1forupdate

📝 关键理解:读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞。

✅3. 意向锁(Intention Lock)

又称I锁,针对表锁,主要是为了提高加表锁的效率,是mysql数据库自己加的。当有事务给表的数据行加了共享锁或排他锁,同时会给表设置一个标识,代表已经有行锁了,其他事务要想对表加表锁时,就不必逐行判断有没有行锁可能跟表锁冲突了,直接读这个标识就可以确定自己该不该加表锁。特别是表中的记录很多时,逐行判断加表锁的方式效率很低。而这个标识就是意向锁。

意向锁主要分为:

  • 意向共享锁(IS锁):对整个表加共享锁之前,需要先获取到意向共享锁
  • 意向排他锁(IX锁):对整个表加排他锁之前,需要先获取到意向排他锁

📝 面试要点:意向锁是MySQL内部自动维护的,用户无法手动干预。它的存在就是为了让表锁的加锁判断从O(n)降到O(1)。

行锁分两种,表锁也分两种,组合起来一共四种情况,结果完全不同:

表里持有的行锁对应意向锁你想加表读锁(LOCK TABLES t READ)你想加表写锁(LOCK TABLES t WRITE)
锁(S,如LOCK IN SHARE MODEIS能加❌ 冲突,等待
锁(X,如FOR UPDATE/UPDATEIX❌ 冲突,等待❌ 冲突,等待

一句话记忆:只有"表里是行读锁 + 你想加表读锁"这一种组合不冲突,其余三种都会冲突等待

📊 小结:锁分类对比表

维度类型说明
性能乐观锁版本对比/CAS,适合读多写少
性能悲观锁先加锁再操作,适合写多读少
操作粒度表锁锁整张表,开销小,并发度低
操作粒度页锁锁一个页,介于表锁和行锁之间
操作粒度行锁锁一行,开销大,并发度高
操作类型读锁(S锁)共享,不阻塞读,阻塞写
操作类型写锁(X锁)排他,阻塞读和写
操作类型意向锁(I锁)表级标识,提高加表锁效率

二、表锁

✅1. 表锁特性与基本操作

每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。

-- 建表SQLCREATETABLE`mylock`(`id`INT(11)NOTNULLAUTO_INCREMENT,`NAME`VARCHAR(20)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=MyISAMDEFAULTCHARSET=utf8;-- 插入数据INSERTINTO`test`.`mylock`(`id`,`NAME`)VALUES('1','a');INSERTINTO`test`.`mylock`(`id`,`NAME`)VALUES('2','b');INSERTINTO`test`.`mylock`(`id`,`NAME`)VALUES('3','c');INSERTINTO`test`.`mylock`(`id`,`NAME`)VALUES('4','d');-- 手动增加表锁locktable表名称read(write),表名称2read(write);-- 查看表上加过的锁showopentables;-- 删除表锁unlocktables;

📝 关键理解:MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。

三、页锁

✅1. 页锁特性

只有BDB存储引擎支持页锁,页锁就是在页的粒度上进行锁定,锁定的数据资源比行锁要多,因为一个页中可以有多个行记录。当我们使用页锁的时候,会出现数据浪费的现象,但这样的浪费最多也就是一个页上的数据行。页锁的开销介于表锁和行锁之间,会出现死锁。锁定粒度介于表锁和行锁之间,并发度一般。

四、行锁

✅1. 行锁特性

每次操作锁住一行数据。**开销大,加锁慢;**会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。

InnoDB相对于MYISAM的最大不同有两点:

  • InnoDB支持事务(TRANSACTION)
  • InnoDB支持行级锁

📝 注意:InnoDB的行锁实际上是针对索引加的锁(在索引对应的索引项上做标记),不是针对整个行记录加的锁。并且该索引不能失效,否则会从行锁升级为表锁。(RR级别会升级为表锁,RC级别不会升级为表锁)

✅2. 行锁升级为表锁的问题

比如我们在RR级别执行如下sql:

select*fromaccountwherename='lilei'forupdate;--where条件里的name字段无索引

则其它Session对该表任意一行记录做修改操作都会被阻塞住。

✅3. RR级别行锁升级为表锁的原因分析

因为在RR隔离级别下,需要解决不可重复读和幻读问题,所以在遍历扫描聚集索引记录时,为了防止扫描过的索引被其它事务修改(不可重复读问题) 或 间隙被其它事务插入记录(幻读问题),从而导致数据不一致,所以MySQL的解决方案就是把所有扫描过的索引记录和间隙都锁上,这里要注意,并不是直接将整张表加表锁,因为不一定能加上表锁,可能会有其它事务锁住了表里的其它行记录。

📝 面试要点:InnoDB在RR级别下,无索引的查询条件会导致行锁升级,本质是MySQL对扫描到的所有记录加锁来保证隔离性,实际效果类似表锁,但机制上不同于表锁。

📊 表锁 vs 行锁对比

特性表锁行锁
开销
加锁速度
死锁不会
粒度
锁冲突概率
并发度
适用场景整表迁移高并发OLTP
存储引擎MyISAM/MemoryInnoDB

五、间隙锁与临键锁

✅1. 间隙锁(Gap Lock)

间隙锁,锁的就是两个值之间的空隙,间隙锁是在可重复读隔离级别下才会生效。

Mysql默认级别是repeatable-read,有幻读问题,间隙锁是可以解决幻读问题的。

假设account表里数据如下,那么间隙就有 id 为 (3,10),(10,20),(20,正无穷) 这三个区间。

在Session_1下面执行如下sql:

select*fromaccountwhereid=18forupdate;

则其他Session没法在这个(10,20)这个间隙范围里插入任何数据。

如果执行下面这条sql:

select*fromaccountwhereid=25forupdate;

则其他Session没法在这个(20,正无穷)这个间隙范围里插入任何数据。

也就是说,只要在间隙范围内锁了一条不存在的记录会锁住整个间隙范围,不锁边界记录,这样就能防止其它Session在这个间隙范围内插入数据,就解决了可重复读隔离级别的幻读问题。

📝 核心:间隙锁只在RR级别生效,只锁间隙不锁边界记录,专门用于解决幻读问题。

✅2. 临键锁(Next-Key Locks)

Next-Key Locks是行锁与间隙锁的组合。

相当于把当前记录锁住(行锁),同时锁住当前记录前面的间隙(间隙锁),形成(left_gap, record]的左开右闭区间。

📝 面试要点:InnoDB在RR级别下的默认加锁算法就是Next-Key Lock,它可以同时解决不可重复读(通过行锁)和幻读(通过间隙锁)问题。

📊 三种行级锁对比

锁类型锁范围作用生效级别
Record Lock单行记录防止其他事务修改/删除RC/RR
Gap Lock间隙(不锁边界)防止其他事务在间隙插入仅RR
Next-Key Lock记录+前间隙防止修改/删除+插入仅RR

六、锁等待与死锁分析

✅1. 锁等待状态分析

通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况:

showstatuslike'innodb_row_lock%';

对各个状态量的说明如下:

  • Innodb_row_lock_current_waits: 当前正在等待锁定的数量
  • Innodb_row_lock_time: 从系统启动到现在锁定总时间长度
  • Innodb_row_lock_time_avg: 每次等待所花平均时间
  • Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花时间
  • Innodb_row_lock_waits: 系统启动后到现在总共等待的次数

对于这5个状态变量,比较重要的主要是:

  • Innodb_row_lock_time_avg(等待平均时长)
  • Innodb_row_lock_waits(等待总次数)
  • Innodb_row_lock_time(等待总时长)

📝 关键理解:当等待次数很高,而且每次等待时长也不小的时候,我们需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。

✅2. INFORMATION_SCHEMA锁相关数据表

-- 查看事务select*fromINFORMATION_SCHEMA.INNODB_TRX;-- 查看锁,8.0之后需要换成这张表performance_schema.data_locksselect*fromINFORMATION_SCHEMA.INNODB_LOCKS;-- 查看锁等待,8.0之后需要换成这张表performance_schema.data_lock_waitsselect*fromINFORMATION_SCHEMA.INNODB_LOCK_WAITS;-- 释放锁,trx_mysql_thread_id可以从INNODB_TRX表里查看到killtrx_mysql_thread_id-- 查看锁等待详细信息showengineinnodbstatus;

✅3. 死锁问题分析

settx_isolation='repeatable-read';
  • Session_1执行:select * from account where id=1 for update;
  • Session_2执行:select * from account where id=2 for update;
  • Session_1执行:select * from account where id=2 for update;
  • Session_2执行:select * from account where id=1 for update;

查看近期死锁日志信息:show engine innodb status;

大多数情况mysql可以自动检测死锁并回滚产生死锁的那个事务,但是有些情况mysql没法自动检测死锁,这种情况我们可以通过日志分析找到对应事务线程id,可以通过kill杀掉。

📊 锁问题排查命令速查表

命令用途版本注意
show status like 'innodb_row_lock%'行锁等待统计通用
SELECT * FROM INNODB_TRX查看当前事务通用
SELECT * FROM INNODB_LOCKS查看锁信息8.0用performance_schema.data_locks代替
SELECT * FROM INNODB_LOCK_WAITS查看锁等待8.0用performance_schema.data_lock_waits代替
kill <thread_id>释放指定事务的锁通用
show engine innodb status查看锁等待/死锁详情通用

七、锁优化实践

  • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能减少检索条件范围,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
  • 尽可能用低的事务隔离级别

📝 面试要点:锁优化的核心思路——通过索引减少锁范围、通过事务拆小减少锁时长、通过降低隔离级别减少锁种类。


八、MVCC多版本并发控制机制

✅1. MVCC概念与原理

Mysql在可重复读隔离级别下如何保证事务较高的隔离性,同样的sql查询语句在一个事务里多次执行查询结果相同,就算其它事务对数据有修改也不会影响当前事务sql语句的查询结果。

这个隔离性就是靠MVCC(Multi-Version Concurrency Control)机制来保证的,对一行数据的读和写两个操作默认是不会通过加锁互斥来保证隔离性,避免了频繁加锁互斥,而在串行化隔离级别为了保证较高的隔离性是通过将所有操作加锁互斥来实现的。

Mysql在读已提交可重复读隔离级别下都实现了MVCC机制。

📝 核心:MVCC实现了读写不冲突的并发控制,读操作不加锁(快照读),写操作加锁(当前读),两者互不阻塞。这是InnoDB高并发的核心秘密。

✅2. undo日志版本链

undo日志版本链是指一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会保留修改前的数据undo回滚日志,并且用两个隐藏字段trx_idroll_pointer把这些undo日志串联起来形成一个历史记录版本链

📝 关键理解:每行数据有隐藏字段trx_id(最后修改该行的事务ID)和roll_pointer(指向上一个版本的undo日志指针),通过这两个字段将所有历史版本串联成一条链。

✅3. read view机制详解

可重复读隔离级别,当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view,该视图在事务结束之前永远都不会变化(如果是读已提交隔离级别在每次执行查询sql时都会重新生成read-view),这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id)组成,事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。

✅4. 版本链比对规则

  1. 如果 row 的 trx_id 落在绿色部分(trx_id<min_id),表示这个版本是已提交的事务生成的,这个数据是可见的;
  2. 如果 row 的 trx_id 落在红色部分(trx_id>max_id),表示这个版本是由将来启动的事务生成的,是不可见的(若 row 的 trx_id 就是当前自己的事务是可见的);
  3. 如果 row 的 trx_id 落在黄色部分(min_id <=trx_id<= max_id),那就包括两种情况
    a. 若 row 的 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见(若 row 的 trx_id 就是当前自己的事务是可见的);
    b. 若 row 的 trx_id 不在视图数组中,表示这个版本是已经提交了的事务生成的,可见。

✅5. 删除数据的特殊处理

对于删除的情况可以认为是update的特殊情况,会将版本链上最新的数据复制一份,然后将trx_id修改成删除操作的trx_id,同时在该条记录的头信息(record header)里的(deleted_flag)标记位写上true,来表示当前记录已经被删除,在查询时按照上面的规则查到对应的记录如果delete_flag标记位为true,意味着记录已被删除,则不返回数据。

✅6. RR与RC隔离级别的MVCC实现差异

readview和可见性算法其实就是记录了sql查询那个时刻数据库里提交和未提交所有事务的状态。

  • 要实现RR隔离级别,事务里每次执行查询操作readview都是使用第一次查询时生成的readview,也就是都是以第一次查询时当时数据库里所有事务提交状态来比对数据是否可见,当然可以实现每次查询的可重复读的效果了。
  • 要实现RC隔离级别,事务里每次执行查询操作readview都会按照数据库当前状态重新生成readview,也就是每次查询都是跟数据库里当前所有事务提交状态来比对数据是否可见,当然实现的就是每次都能查到已提交的最新数据效果了。

📝 重要警告:begin/start transaction命令并不是一个事务的起点,在执行到它们之后的第一个修改操作或加排它锁操作(比如select…for update)的语句,事务才真正启动,才会向mysql申请真正的事务id,mysql内部是严格按照事务的启动顺序来分配事务id的。

📊 RR vs RC隔离级别MVCC实现对比

特性RR(可重复读)RC(读已提交)
read-view生成时机事务第一次查询时生成每次查询重新生成
read-view是否变化事务期内不变每次查询都变
可见性基准第一次查询时的快照每次查询时的最新状态
效果可重复读每次读到最新已提交数据
解决幻读✅(通过MVCC + 间隙锁)

📊 MVCC总结

MVCC机制的实现就是通过read-view机制与undo版本链比对机制,使得不同的事务会根据数据版本链对比规则读取同一条数据在版本链上的不同版本数据。


📋 全文总结

✅1. 锁分类体系

从性能(乐观/悲观)、粒度(表/页/行)、类型(读锁/写锁/意向锁)三个维度全面理解MySQL锁体系。InnoDB的行锁基于索引实现。

✅2. 行锁升级问题

RR级别下,无索引的查询条件会导致行锁升级为锁住所有扫描到的记录,效果类似表锁。解决:确保查询走索引。

✅3. 间隙锁与幻读

间隙锁只在RR级别生效,通过锁住间隙防止插入来解-决幻读问题。Next-Key Lock是行锁+间隙锁的组合。

✅4. 锁优化核心原则

减少锁范围(走索引)→减少锁时长(缩短事务)→减少锁种类(降低隔离级别)。

✅5. MVCC实现原理

undo日志版本链 + read-view可见性比对 = MVCC。RR级别下read-view在事务内不变,RC级别下每次查询重新生成。

✅6. 死锁排查

show engine innodb status查看死锁信息,多数情况下MySQL自动检测并回滚,特殊情况需手动kill。

✅7. 关键SQL速记

-- 共享锁(读锁)SELECT...LOCKINSHAREMODE;-- 排他锁(写锁)SELECT...FORUPDATE;-- 行锁状态查看SHOWSTATUSLIKE'innodb_row_lock%';-- 死锁与锁等待信息SHOWENGINEINNODBSTATUS;
http://www.jsqmd.com/news/1090583/

相关文章:

  • 本地离线批量 OCR 实战:证件、发票、铭牌一键提取结构化信息
  • 如何在Amlogic电视盒上安装Debian系统:2025年终极开源解决方案
  • 如何用AI瞄准助手Aimmy快速提升游戏水平:终极配置指南
  • 英雄联盟皮肤库终极指南:一站式获取所有官方皮肤与炫彩资源
  • 3分钟掌握Chrome画中画扩展:免费提升多任务效率的终极指南
  • 重塑硬件价值:OpenCore Legacy Patcher如何让老Mac重获新生
  • 惠普暗影精灵性能控制终极指南:OmenSuperHub开源工具完全解析
  • 大数据专业课难度高?挂科率到底什么水平?过来人告诉你!
  • Web3 漏洞复盘:Verus 跨链桥 1158 万美元“数据结构错位“事件
  • Plus 到底值不值 20 美金?
  • Android13文件访问权限重构:从MANAGE_EXTERNAL_STORAGE到细粒度媒体权限的实战解析
  • Superpowers - 编码Agent 工程技能插件集详细介绍
  • 【硕博生必看 | 会议征稿通知 | 双一流高校主协办 | 权威出版社出版 | EI 、Scopus稳定检索 | 另合作期刊推荐】2026年8月国际学术会议列表清单 | 2026年8月全领域学术会议速览
  • 如何将Amlogic电视盒变身高性能Linux服务器:2025终极开源方案
  • 抖音去水印终极指南:5分钟搭建你自己的无水印视频解析工具
  • 变频器与伺服系统的噪声战争:02 PWM为什么像一把高速砍刀?
  • 服务注册与发现:Eureka, Nacos, Consul
  • AI模型能力发布机制解析:从 gated release 到可控部署实践
  • BiliTools跨平台工具箱:高效管理哔哩哔哩资源的完整解决方案
  • Legacy iOS Kit深度解析:旧款iOS设备降级与越狱的终极解决方案
  • 跨平台B站视频下载解决方案:BilibiliDown一站式离线工具
  • 高效AI专著生成方案:4款AI工具推荐,快速搞定20万字专著写作!
  • 5分钟精通AMD Ryzen处理器调试:SMUDebugTool终极指南
  • 如何快速捕获网页媒体资源:猫抓浏览器扩展完整使用指南
  • 深度学习优化
  • ESP32 C3开发实战 -7(BLE加密连接Bond)
  • 时光回溯:为互联网记忆打造的数字保险箱
  • upload-labs靶场通关实战:20种文件上传漏洞的深度剖析与利用
  • 终极指南:3分钟解决Windows软件运行库依赖问题
  • 认知颠覆型:别只看排行!一文揭秘oem卫浴五金洁具工厂真实实力