MySQL锁详解 mysql各种锁
bigegpt 2024-10-21 03:46 2 浏览
锁是数据库系统与文件系统区别的一个关键特性。锁是用于管理对共享资源的并发访问,对于锁,不同的数据库有不同的实现方式。在InnoDB存储引擎中有非常多的锁设计,其设计思路与Oracle有诸多的相似之处,比如提供了一致性的非锁定读,行级别锁等。
锁相关数据结构
在InnoDB存储引擎中,锁是为事务服务的,涉及到的锁相关的表有innodb_trx、data_locks、data_locks_waits。
- innodb_trx:记录当前正在执行的事务信息(包括只读事务)。
- data_locks表:记录所有事务未释放的锁信息(如果事务发生严重的锁等待可以通过查询该表定位问题)。
- data_locks_waits表:记录data_locks中锁与锁之间等待及依赖关系表,同时也记录了所对应的事务信息。
我们通过一个示例来看一下三者之间的关系:
查询了user表的user_id(该列建立了唯一索引)
BEGIN;
SELECT * FROM user where user_id = "18" for update;
SELECT * FROM performance_schema.data_locks;
user_id的查询在表data_locks中会产生三条数据,第一条为表级别意向排他锁(IX),第二条与第三条都为行记录排他锁(X)(注:REC_NOT_GAP为排它非间隙锁),他们的区别是LOCK_DATA的不同,LOCK_DATA的值通常表示的是主键ID,如果我们使用到辅助索引查询时,那么锁住的范围会包含聚簇索引+辅助索引,此时的LOCK_DATA则为辅助索引字段值+主键ID(见上图圈红部分)。
如果我们开启三个事务,下图为三个事务存储在innodb_trx、data_locks、locks_waits三张表中的数据关系:
InnoDB存储引擎中的锁
MySQL的InnoDB存储引擎支持行锁以及表锁,在讲解锁实现前我们先要明确一个概念,本章讲解的锁区别于我们平常开发过程中经常使用到的临界资源锁(Latch,例如Java中的AQS、synchronized等),本章讲解的是MySQL作用于事务的锁,用于锁定表、页、行等粗粒度的资源。
行锁
InnoDB引擎实现了两种标准的行级锁,分别是共享行级锁、排它行级锁。
- 共享行级锁:又称S锁、Share Lock、读锁。
如语句:select ... lock in share mode等会加共享行锁。
- 排它行级锁:又称X锁、Exclusive Lock、写锁。
如语句:update、delete、insert、select ... for update等会加排它行锁。
S锁与X锁的兼容性如下:
S锁 | X锁 | |
S锁 | 兼容 | 不兼容 |
X锁 | 不兼容 | 不兼容 |
行锁的算法实现
InnoDB存储引擎存在三种行锁算法,分别为Record Lock、Gap_Lock、Next-Key Lock。
行锁(Record Lock)
Record Lock表示的是单行记录上的锁,例如文章起始的示例,执行for update时事务会为每一条数据加上X锁,如果使用到辅助索引,则会锁定聚簇索引记录与辅助索引记录。如下图data_locks表的LOCK_TYPE为RECORD,LOCK_MODE为X,REC_NOT_GAP(REC_NOT_GAP表示为行锁,非间隙锁),LOCK_DATA为:'18',18,表示锁住的是聚簇索引记录与辅助索引记录。
间隙锁(Gap Lock)
间隙锁顾名思义也就是锁住一个键值范围,其存在主要是为了解决幻读的问题。
幻读:当前事务读取一定范围内的数据时,其它事务在该范围内插入或者删除了记录,导致当前事务读取到数据发生了变更,如图产生幻觉一样。
间隙锁会为区间内的所有行加上X锁,如下图data_locks表的LOCK_TYPE为RECORD,LOCK_MODE为X,如果没有指定闭区间LOCK_DATA则为supremum pseudo-record。
临键锁(Next-Key Lock)
Next-Key Lock是Record Lock与Gap Lock的组合,其加锁原则如下(RR隔离级别下):
- 加锁的对象是一个前开后闭的区间;
- 查找过程中访问到的对象才加锁;
- 索引上的等值查询:命中唯一索引时退化为行锁;命中普通索引时,左右两边加Gap Lock+Record Lock;
- 索引上的等值查询,向右遍历时最后一个不满足等值条件的时候,Next-Key Lock 退化为间隙锁;
- 索引范围查询:
1)等值与范围分开判断;
2)索引在范围查询的时候都会访问到所在区间不满足条件的第一个值为止;
3)如果使用到了倒序排序,按照倒序排序后:检索范围的右边多加一个Gap 区间;如果左右两边再命中了等值条件,则需要再向同方向拓展一个外开里闭的区间。
以上原则较为抽象,我们通过一张图来做进一步的解释:
表锁
InnoDB还支持一种锁,其为表级锁,为了支持这两种不同粒度的锁,InnoDB支持一种额外的锁模式,称之为意向锁(Intention Lock)。
意向锁同样分为两种: 共享和排他
- 意向共享锁(IS, Intention Share Lock)
- 意向排他锁 (IX, Intention Exclusive Lock)
意向锁(Intention Lock)作为一种表级锁存在,是为了解决不同事务之间的锁冲突问题,其思路是要想获得行锁,那么必须要获取比行更大粒度的锁,我们来看一个锁的分层图:
意向锁之间的兼容性:
意向排它锁(IX) | 意向共享锁(IS) | |
意向排它锁(IX) | 兼容 | 兼容 |
意向共享锁(IS) | 兼容 | 兼容 |
意向锁不会与行级的共享/排他锁互斥!正因为如此,意向锁并不会影响到多个事务对不同数据行加排他锁时的并发性。
自增锁(AUTO_INC Locking)
最初的自增长锁采用的是特殊的表锁实现,称其为AUTO_INC Locking,为了提高插入的性能,该锁不是在事务执行完成时候释放,而是在自增长值插入成功后立即释放,但是这种实现在高并发下仍然效率不够高。因此自增长锁就有了轻量级的 Mutex(轻量锁)实现,当然这种实现是在高并发时才会启用,没有事务竞争时仍然是AUTO_INC Locking。
元数据锁(Metadata Lock)
元数据锁Metadata Lock,又称为MDL,它与行锁和表锁的区别仅仅是作用对象范围的不同,它的作用范围更广,包含了数据库、表、行、触发器以及外键等。
通常情况下,当我们修改表结构的时候才会出现MDL,如执行ALTER TABLE xxx ADD column 语句时。这个锁会阻塞整张表的所有后续事务,如果存在长事务或者表数据的修改非常频繁,很有可能会导致MySQL进程崩溃。我们来看一个在线的DDL过程,如下:
DDL(Data Definition Language):数据库结构相关的操作语言,关键字:create、alter、drop等。
DML(Data Manipulation Language):数据库数据的相关操作语言,关键字:select、update、delete、insert等。
- ALTER TABLE xxx ADD column语句获取MDL写锁;
- 获取成功后,将其降级为MDL读锁;
- 在执行真正的DDL操作之前,是可以执行DML语句的;
- 升级MDL读锁为写锁,此时所有DML语句会被阻塞;
- 执行完成,释放MDL写锁,DDL执行完成,阻塞的DML语句可以继续执行;
在这个过程中,在没有真正开始执行DDL语句前只是加了MDL读锁,DML语句是可以正常执行的,这也就降低了其它事务的阻塞时间。
插入意向锁(Insert Intention Lock)
插入意向锁是一种间隙锁形式的意向锁,其属于一种行锁,在插入语句发生等待时设置。如下图:
在执行插入操作前,事务A对要插入数据的间隙加了间隙锁,事务B提交了插入语句会进行等待,此时data_locks表中会插入一条如上图所示的数据,LOCK_MODE为X,GAP,INSERT_INTENTION。
锁相关问题
死锁
死锁是指两个及以上的事务在执行的过程中,因争夺锁资源而造成的一种相互等待的现象。
死锁解决方案
- 事务超时机制:解决死锁的最简单方法是超时机制,通过为事务设置等待超时时间来断开依赖链,如果事务超时,则回滚事务。回滚事务的选择也是非常重要的,我们可以通过FIFO队列,按顺序回滚,但如果回滚事务所占用的资源非常多(更新了很多行,写入了很多undo log),那么采用FIFO就不合适了,这是可以按照事务权重会滚。
- wait-for graph(等待图)死锁检查:这种方式是目前数据库普遍采用的方式,这是一种主动监测死锁的机制。在事务请求锁而发生等待时,我们可以通过锁相关的表(innodb_trx、data_locks、data_locks_waits)构建一张图,通过检测图是否有回路来判断是否存在死锁。
锁升级
锁升级(Lock Rscalation)是指将当前锁的粒度降低。例如可以把一张表的N个行锁升级为页锁,或者升级为表锁。锁升级的场景:
- InnoDB存储引擎的行锁锁住的是索引记录,如果没有命中索引,那么只能全表加间隙锁,这等同于表锁;
- 如果命中了索引,但是索引的选择性低(关于选择性的概念可以阅读我的上一篇文章),那么也会升级为全表加间隙锁;
总结
- InnoDB的行锁是针对索引记录加锁,如果没有命中索引,则会给全表的聚簇索引加间隙锁。
- 因为加锁对象是索引,所以可能出现两个事务访问的不同行记录,但是使用到了相同的索引键,也就是有索引键冲突,那么另外一个事务仍然会阻塞。
- 如果条件没有索引或者索引的选择性很低,那么会造成锁升级,也就是全表加间隙锁。
《MySQL系列专栏》持续更新中,关注我不迷路[送心]。
相关推荐
- Java 泛型大揭秘:类型参数、通配符与最佳实践
-
引言在编程世界中,代码的可重用性和可维护性是至关重要的。为了实现这些目标,Java5引入了一种名为泛型(Generics)的强大功能。本文将详细介绍Java泛型的概念、优势和局限性,以及如何在...
- K8s 的标签与选择器:流畅运维的秘诀
-
在Kubernetes的世界里,**标签(Label)和选择器(Selector)**并不是最炫酷的技术,但却是贯穿整个集群管理与运维流程的核心机制。正是它们让复杂的资源调度、查询、自动化运维变得...
- 哈希Hash算法:原理、应用(哈希算法 知乎)
-
原作者:Linux教程,原文地址:「链接」什么是哈希算法?哈希算法(HashAlgorithm),又称为散列算法或杂凑算法,是一种将任意长度的数据输入转换为固定长度输出值的数学函数。其输出结果通常被...
- C#学习:基于LLM的简历评估程序(c# 简历)
-
前言在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。准备不同的简历:image-20250528183949844查...
- 55顺位,砍41+14+3!季后赛也成得分王,难道他也是一名球星?
-
雷霆队最不可思议的新星:一个55号秀的疯狂逆袭!你是不是也觉得NBA最底层的55号秀,就只能当饮水机管理员?今年的55号秀阿龙·威金斯恐怕要打破你的认知了!常规赛阶段,这位二轮秀就像开了窍的天才,直接...
- 5分钟读懂C#字典对象(c# 字典获取值)
-
什么是字典对象在C#中,使用Dictionary类来管理由键值对组成的集合,这类集合被称为字典。字典最大的特点就是能够根据键来快速查找集合中的值,其键的定义不能重复,具有唯一性,相当于数组索引值,字典...
- c#窗体传值(c# 跨窗体传递数据)
-
在WinForm编程中我们经常需要进行俩个窗体间的传值。下面我给出了两种方法,来实现传值一、在输入数据的界面中定义一个属性,供接受数据的窗体使用1、子窗体usingSystem;usingSyst...
- C#入门篇章—委托(c#委托的理解)
-
C#委托1.委托的定义和使用委托的作用:如果要把方法作为函数来进行传递的话,就要用到委托。委托是一个类型,这个类型可以赋值一个方法的引用。C#的委托通过delegate关键字来声明。声明委托的...
- C#.NET in、out、ref详解(c#.net framework)
-
简介在C#中,in、ref和out是用于修改方法参数传递方式的关键字,它们决定了参数是按值传递还是按引用传递,以及参数是否必须在传递前初始化。基本语义对比修饰符传递方式可读写性必须初始化调用...
- C#广义表(广义表headtail)
-
在C#中,广义表(GeneralizedList)是一种特殊的数据结构,它是线性表的推广。广义表可以包含单个元素(称为原子),也可以包含另一个广义表(称为子表)。以下是一个简单的C#广义表示例代...
- 「C#.NET 拾遗补漏」04:你必须知道的反射
-
阅读本文大概需要3分钟。通常,反射用于动态获取对象的类型、属性和方法等信息。今天带你玩转反射,来汇总一下反射的各种常见操作,捡漏看看有没有你不知道的。获取类型的成员Type类的GetMembe...
- C#启动外部程序的问题(c#怎么启动)
-
IT&OT的深度融合是智能制造的基石。本公众号将聚焦于PLC编程与上位机开发。除理论知识外,也会结合我们团队在开发过程中遇到的具体问题介绍一些项目经验。在使用C#开发上位机时,有时会需要启动外部的一些...
- 全网最狠C#面试拷问:这20道题没答出来,别说你懂.NET!
-
在竞争激烈的C#开发岗位求职过程中,面试是必经的一道关卡。而一场高质量的面试,不仅能筛选出真正掌握C#和.NET技术精髓的人才,也能让求职者对自身技术水平有更清晰的认知。今天,就为大家精心准备了20道...
- C#匿名方法(c#匿名方法与匿名类)
-
C#中的匿名方法是一种没有名称只有主体的方法,它提供了一种传递代码块作为委托参数的技术。以下是关于C#匿名方法的一些重要特点和用法:特点省略参数列表:使用匿名方法可省略参数列表,这意味着匿名方法...
- C# Windows窗体(.Net Framework)知识总结
-
Windows窗体可大致分为Form窗体和MDI窗体,Form窗体没什么好细说的,知识点总结都在思维导图里面了,下文将围绕MDI窗体来讲述。MDI(MultipleDocumentInterfac...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)