MySQL锁详解(mysql各种锁)
mhr18 2024-10-04 17:20 81 浏览 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_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系列专栏》持续更新中,关注我不迷路[送心]。
相关推荐
- 一文读懂Prometheus架构监控(prometheus监控哪些指标)
-
介绍Prometheus是一个系统监控和警报工具包。它是用Go编写的,由Soundcloud构建,并于2016年作为继Kubernetes之后的第二个托管项目加入云原生计算基金会(C...
- Spring Boot 3.x 新特性详解:从基础到高级实战
-
1.SpringBoot3.x简介与核心特性1.1SpringBoot3.x新特性概览SpringBoot3.x是建立在SpringFramework6.0基础上的重大版...
- 「技术分享」猪八戒基于Quartz分布式调度平台实践
-
点击原文:【技术分享】猪八戒基于Quartz分布式调度平台实践点击关注“八戒技术团队”,阅读更多技术干货1.背景介绍1.1业务场景调度任务是我们日常开发中非常经典的一个场景,我们时常会需要用到一些不...
- 14. 常用框架与工具(使用的框架)
-
本章深入解析Go生态中的核心开发框架与工具链,结合性能调优与工程化实践,提供高效开发方案。14.1Web框架(Gin,Echo)14.1.1Gin高性能实践//中间件链优化router:=...
- SpringBoot整合MyBatis-Plus:从入门到精通
-
一、MyBatis-Plus基础介绍1.1MyBatis-Plus核心概念MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提...
- Seata源码—5.全局事务的创建与返回处理
-
大纲1.Seata开启分布式事务的流程总结2.Seata生成全局事务ID的雪花算法源码3.生成xid以及对全局事务会话进行持久化的源码4.全局事务会话数据持久化的实现源码5.SeataServer创...
- Java开发200+个学习知识路线-史上最全(框架篇)
-
1.Spring框架深入SpringIOC容器:BeanFactory与ApplicationContextBean生命周期:实例化、属性填充、初始化、销毁依赖注入方式:构造器注入、Setter注...
- OpenResty 入门指南:从基础到动态路由实战
-
一、引言1.1OpenResty简介OpenResty是一款基于Nginx的高性能Web平台,通过集成Lua脚本和丰富的模块,将Nginx从静态反向代理转变为可动态编程的应用平台...
- 你还在为 Spring Boot3 分布式锁实现发愁?一文教你轻松搞定!
-
作为互联网大厂后端开发人员,在项目开发过程中,你有没有遇到过这样的问题:多个服务实例同时访问共享资源,导致数据不一致、业务逻辑混乱?没错,这就是分布式环境下常见的并发问题,而分布式锁就是解决这类问题的...
- 近2万字详解JAVA NIO2文件操作,过瘾
-
原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。从classpath中读取过文件的人,都知道需要写一些读取流的方法,很是繁琐。最近使用IDEA在打出.这个符号的时候,一行代...
- 学习MVC之租房网站(十二)-缓存和静态页面
-
在上一篇<学习MVC之租房网站(十一)-定时任务和云存储>学习了Quartz的使用、发邮件,并将通过UEditor上传的图片保存到云存储。在项目的最后,再学习优化网站性能的一些技术:缓存和...
- Linux系统下运行c++程序(linux怎么运行c++文件)
-
引言为什么要在Linux下写程序?需要更多关于Linux下c++开发的资料请后台私信【架构】获取分享资料包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdf...
- 2022正确的java学习顺序(文末送java福利)
-
对于刚学习java的人来说,可能最大的问题是不知道学习方向,每天学了什么第二天就忘了,而课堂的讲解也是很片面的。今天我结合我的学习路线为大家讲解下最基础的学习路线,真心希望能帮到迷茫的小伙伴。(有很多...
- 一个 3 年 Java 程序员 5 家大厂的面试总结(已拿Offer)
-
前言15年毕业到现在也近三年了,最近面试了阿里集团(菜鸟网络,蚂蚁金服),网易,滴滴,点我达,最终收到点我达,网易offer,蚂蚁金服二面挂掉,菜鸟网络一个月了还在流程中...最终有幸去了网易。但是要...
- 多商户商城系统开发全流程解析(多商户商城源码免费下载)
-
在数字化商业浪潮中,多商户商城系统成为众多企业拓展电商业务的关键选择。这类系统允许众多商家在同一平台销售商品,不仅丰富了商品种类,还为消费者带来更多样的购物体验。不过,开发一个多商户商城系统是个复杂的...
你 发表评论:
欢迎- 一周热门
-
-
Redis客户端 Jedis 与 Lettuce
-
高并发架构系列:Redis并发竞争key的解决方案详解
-
redis如何防止并发(redis如何防止高并发)
-
开源推荐:如何实现的一个高性能 Redis 服务器
-
redis安装与调优部署文档(WinServer)
-
Redis 入门 - 安装最全讲解(Windows、Linux、Docker)
-
一文带你了解 Redis 的发布与订阅的底层原理
-
Redis如何应对并发访问(redis控制并发量)
-
oracle数据库查询Sql语句是否使用索引及常见的索引失效的情况
-
Java SE Development Kit 8u441下载地址【windows版本】
-
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)