百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

MySQL锁详解(mysql锁有几种方式)

sinye56 2024-10-14 16:10 5 浏览 0 评论

锁是数据库系统与文件系统区别的一个关键特性。锁是用于管理对共享资源的并发访问,对于锁,不同的数据库有不同的实现方式。在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_TYPERECORDLOCK_MODEX,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系列专栏》持续更新中,关注我不迷路[送心]

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: