MySQL8 表级锁
yuyutoo 2024-12-12 15:54 3 浏览 0 评论
MySQL8表级锁
MySQL中表级锁主要有表锁(注意区分表级锁)、意向锁、自增锁、元数据锁。
表锁
表锁是一种表级锁,他是一种显式锁,通常需要用户手动加锁和解锁(也存在特殊情况,比如行级锁升级为表级别),表锁分为两种,一种是读锁(S锁),一种是写锁(X锁),只能在一个会话中加锁和解锁,不能获取其他会话的锁以及释放其他会话的锁。表锁不仅仅能锁定表,也能锁定视图。
语法说明
加锁和解锁的语法如下:
# 加锁
LOCK TABLES
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
# 锁类型
lock_type: {
READ [LOCAL]
| [LOW_PRIORITY] WRITE
}
# 解锁
UNLOCK TABLES
LOCOL:他的作用是在满足MySIAM表并发插入条件的情况下,允许其他用户在表尾并发插入记录。
LOW_PRIORITY :代表低优先级,已过期,后续版本中会删除,因为WRITE锁通常具有更高的优先级 比锁,以确保尽快处理。
表锁的兼容性如下:
当前锁/其他锁 | 读锁 | 写锁 |
读锁 | 兼容 | 互斥 |
写锁 | 互斥 | 互斥 |
也就是说,多个客户端可以对同一个表同时加读锁,但是不能同时加写锁,或者混合读写锁,举例说明:
兼容性验证
读锁和读锁
打开两个会话,同时对一个表加上表读锁。
查看锁状态,通过show open tables from d1查看数据库d1的表的锁状态。
因为两个会话都对表t1加了表读锁,所以In_use字段的值为2。
当然也可以同时对多个表进行加锁
使用unlock tables解锁。
读锁和写锁
查看锁状态:
可以看到只加上了一把锁,并且无论先加表读锁还是表写锁都没有关系,后加的锁都无法成功加锁,一直阻塞中,只有当之前的锁解锁后,后面的加锁语句才能成功。
使用unlock tables解锁。
写锁和写锁
可以确认写锁和写锁是排斥的。
使用unlock tables解锁。
通过FLUSH语句加表锁
还有一种加表锁的方式,就是使用FLUSH TABLES tbl_name [, tbl_name] ... WITH READ LOCK,此语句会首先获取一个指定表的排他锁,因此他会等待这些表的事务完成,然后刷新这些表的表缓存,重新打开表,最后获取表锁(读取),接下来再将元数据排他锁降级为元数据共享锁,那么其他会话就只能读取表而不能修改表。
上图中我使用FLUSH TABLES tbl_name [, tbl_name] ... WITH READ LOCK语句对t1加了锁,然后我通过show open tables from t1查询了打开的表,是能查到结果的(此处不用管t2表,因为也没操作t2表。),我们知道flush的作用是清理缓存并关闭表,此处能查到恰巧说明了该语句又重新打开了表。
上面我们提到了元数据锁的概念,此处只需要知道它的作用是维护表元数据的一致性,为了避免DML与DDL冲突,保证读写的正确性。
无论使用LOCK TABLSES还是使用FLUSH TABLES tbl_name WITH READ LOCK都会加上元数据锁,我们查看下元数据锁的表查看下。
解锁方式
上面我们已经说明通过UNLOCK TABLES可以手动解锁,除了此种方式还有其他的解锁方式。
- 断开会话
- 会话断开会自动释放表锁,验证下(这里我通过写锁和写锁的互斥性来验证)。
- 当会话1中断开MySQL的连接,会话2则不再阻塞,加锁成功。
- 开启事务
- 当前加了表锁,但是当开启事务的时候,会自动解锁。
元数据(MDL)锁
概念
元数据锁(Metadata Lock,简称MDL)是表级锁中的一种,MDL锁主要作用是维护表元数据的数据一致性,为了避免DML与DDL冲突,保证读写的正确性。
元数据锁不仅仅可以应用到表上,也可以应用到schemas、存储过程、函数、触发器、计划事件、表空间上。
隐式加解锁,无需用户控制,系统自动完成。
DDL、DML、DQL以及表级锁都会加元数据锁。
查看MDL锁
我们可以通过PERFORMANCE_SCHEMA中的metadata_locks查看锁情况
因为我们查询了metadata_locks表,所以系统自动加了元数据锁,查询也加锁吗?是的。
列解释:
列名 | 解释 | |
OBJECT_TYPE | 当前锁类型:取值有GLOBAL,SCHEMA,TABLE,FUNCTION,PROCEDURE,TRIGGER,EVENT,COMMIT,USER LEVEL LOCK,TABLESPACE,LOCKING SERVICE | |
OBJECT_SCHEMA | 对象所在的SCHEMA名字 | |
OBJECT_NAME | 对象名称 | |
LOCK_TYPE | 锁的具体类型,取值有:INTENTION_EXCLUSIVE,SHARED,SHARED_HIGH_PRIO,SHARED_READ,SHARED_WRITE,SHARED_UPGRADABLE,SHARED_NO_WRITE,SHARED_NO_READ_WRITE,EXCLUSIVE, | |
LOCK_STATUS | 锁的状态,取值有:PENDING,GRANTED,VICTIM,TIMEOUT,KILLED,PRE_ACQUIRE_NOTIFY,POST_RELEASE_NOTIFY |
MDL锁种类
MDL锁的种类主要分为共享只读锁SHARED_READ_ONLY,共享写锁SHARED_NO_READ_WRITE,共享读锁SHARED_READ,共享写锁SHARED_WRITE,排他锁EXCLUSIVE。
有些奇怪呀?常见共享锁和排他锁的规则是读读兼容,读写、谢谢排斥嘛,这里为什么共享写锁?在MDL锁中这确实是比较特殊的存在,在MDL锁中,共享读锁(SHARED_READ)和共享写锁(SHARED_WRITE)确实是兼容的,他们跟排他锁EXCLUSIVE是互斥的。
DQL
DQL主要包含普通SELECT快照读,同时也可以针对SELECT加锁实现最新读。
首先看下SELECT快照读,会加什么样的元数据锁。
可以看到对于DQL快照读会加上元数据锁中的SHARE_READ锁,叫做元数据共享读锁。
再看下使用SELECT ... FROM TABLE FOR SHARE/UPDATE语句会加上什么锁。
FOR SHARE:
可以看到对于SELECT ... FOR SHARE语句加上了SHARE_READ锁,这跟不使用FOR SHARE是意向的效果。
FOR UPDATE:
对于SELECT ... FOR UPDATE语句,加上了元数据锁中的共享写锁(SHARED_WRITE)。
通过上面可以知道对于不同的DQL加的元数据锁的类型:
语句(DQL) | MDL锁类型 | 说明 |
SELECT、SELECT...[FOR SHARE | LOCK IN SHARE MODE] | 共享读锁(SHARED_READ) | 对于普通SELECT和加了共享锁的SELECT来说,都会创建MDL共享读锁 |
SELECT...FOR UPDATE | 共享写锁(SHARED_WRITE) | 对于加了排他锁的SELECT语句说,会加MDL共享写锁。 |
上面我们提出来为什么共享读和共享写是兼容的,到这里其实我们就能够理解了,比如针对一个表有如下两个语句操作。
SELECT * FROM t1 WHERE id = 1;
SELECT * FROM t1 WHERE id = 2 FOR UPDATE;
对同一个表id不同的两个语句执行了两种查询,每种查询都会创建元数据锁,但是两个SQL创建的锁是不同的。
表锁
给一张表加表锁的同时系统也会加上元数据锁。
加表级读锁的时候,系统会自动创建一个共享MDL读锁
加表级写锁的时候,系统会自动创建一个MDL写锁(SHARED_NO_READ_WRITE)。
DML
看下DML中INSERT,UPDATE,DELETE语句会加什么样的MDL锁。
可以看到对于INSERT操作,系统加上了MDL共享写锁(SHARED_WRITE),验证完毕提交事务。
对于UPDATE操作,系统也是加上了MDL共享写锁(SHARED_WRITE),验证完毕提交事务。
对于DELETE操作,也是加上了MDL共享写锁(SHARED_WRITE),验证完毕提交事务。
总结:
语句种类(DML) | MDL锁类型 |
INSERT、UPDATE、DELETE | SHARED_WRITE |
DDL
对于DDL语句,系统会自动加上MDL排他锁(EXCLUSIVE),此排他锁会阻塞所有的DQL、DML以及其他的DML。
总结:通过上面的例子我们可以得到如下结论。
SQL命令 | 锁类型 | |
SELECT、SELECT...[FOR SHARE | LOCK IN SHARE MODE]SS | SHARE_READ | MDL共享读 |
INSERT、UPDATE、DELETE、SELECT...FOR UPDATE | SHARE_WRITE | MDL共享写 |
ALTER TABLE、CREATE INDEX等DDL语句 | EXCLUSIVE | MDL排他锁 |
LOCK TABLES 表明 READ/WRITE | SHARED_READ_ONLY/SHARED_NO_READ_WRITE | 共享只读锁/共享只写锁 |
MDL锁类型兼容性
上面介绍了针对不同的SQL命令所加的MDL所类型,接下来查看各种锁类型的兼容性。
- SHARE_READ和SHARE_READ
- SHARE_READ和SHARE_WRITE
- SHARE_READ和EXCLUSIVE
- SHARE_READ和SHARED_READ_ONLY
- SHARE_READ和SHARED_NO_READ_WRITE
- SHARE_WRITE和SHARE_WRITE
- SHARE_WRITE和EXCLUSIVE
- SHARE_WRITE和SHARED_READ_ONLY
- SHARE_WRITE和SHARED_NO_READ_WRITE
- EXCLUSIVE和SHARED_READ_ONLY
- EXCLUSIVE和SHARED_NO_READ_WRITE
SHARE_READ和SHARE_READ
SHARE_READ和SHARE_READ是兼容的,再查看下此时MDL锁情况。
SHARE_READ和SHARE_WRITE
可以看到未阻塞,再查看下MDL锁情况。
可以看到确实是有两个MDL锁。
SHARE_READ和EXCLUSIVE
查看锁情况:
可以看到MDL排他锁的状态是PENDING,所以SHARE_READ和EXCLUSIVE是互相排斥的。
SHARE_READ和SHARED_READ_ONLY
查看你下此时的加锁情况:
可以看到都获取到了锁,所以SHARE_READ和SHARED_READ_ONLY是兼容的。
SHARE_READ和SHARED_NO_READ_WRITE
查看下锁信息:
可以看到是状态是PENDING,所以SHARE_READ和SHARED_NO_READ_WRITE是排斥的。
SHARE_WRITE和SHARE_WRITE
查看MDL锁情况:
可以看到两个MDL共享读锁确实是兼容的。
SHARE_WRITE和EXCLUSIVE
查看下MDL锁情况:
从锁状态上看,也是EXCLUSIVE状态是PENDING,说明SHARE_WRITE和EXCLUSIVE是排斥的。
SHARE_WRITE和SHARED_READ_ONLY
查看MDL锁信息:
两图一直,处于阻塞状态,说明SHARE_WRITE和SHARED_READ_ONLY不兼容。
SHARE_WRITE和SHARED_NO_READ_WRITE
查看MDL锁信息:
同样是PENDING状态,说明SHARE_WRITE和SHARED_NO_READ_WRITE不兼容。
EXCLUSIVE和SHARED_READ_ONLY
查看MDL锁信息:
说明 EXCLUSIVE和SHARED_READ_ONLY是不兼容的。
EXCLUSIVE和SHARED_NO_READ_WRITE
查看下MDL锁情况:
PENDING状态印证了上面的alter xxx语句阻塞,表明EXCLUSIVE和SHARED_NO_READ_WRITE不兼容。
兼容性总结
通过上面的测试,我们可以得出如下结论:
语句种类 | MDL锁 | 兼容性说明 |
SELECT、SELECE...[FOR SHARE | LOCK IN SHARE MODE] | SHARE_READ | 与SHARE_READ和SHARE_WRITE兼容,与EXCLUSIVE互斥 |
INSERT、DELETE、UPDATE、SELECT...FOR UPDATE | SHARE_WRITE | 与SHARE_READ和SHARE_WRITE兼容,与EXCLUSIVE互斥 |
表锁(LOCK TABLES 表名 READ/WRITE) | SHARE_READ_ONLY / SHARE_NO_READ_WRITE | SHARE_READ_ONLY 与SHARE_READ兼容,与SHARE_WRITE互斥; SHARE_NO_READ_WRITE与SHARE_READ_ONLY 和SHARE_WRITE都互斥。 |
DDL (ALTER...、CREATE...等) | EXCLUSIVE | 与所有MDL锁互斥。 |
意向锁
概念
意向锁是另外一种表级锁,为了避免DML语句在执行的时候行锁与表锁冲突而设计的意向锁,通过意向锁使得在加表锁的时候无需检查每行数据是否加锁。
假设有如下一种表,已经在id=3的记录上有锁了。
此时如果想给表加上表级锁,就需要循环整个表记录,对于上面的表需要扫描3次才能获取到表内数据锁情况,如果表的数据两很大,则扫描次数也会很大,这无疑降低了效率。
所以MySQL设计:当我们执行DML的时候,同时给表加上一个意向锁。
这样在加表级锁的时候,发现有意向锁,就可以根据策略绝对是否能够加锁,无需扫描表数据。
加锁方式
意向锁无需认为手动加锁以及解锁,他是一种隐式锁,由MySQL自己控制。
意向锁表信息
意向锁的信息存储在SCHEMAPERFORMANCE_SCHEMA.data_locks下。
上图中我开启事务,并执行select * from t1 where id = 1 for share语句,该语句会自动加上一个意向共享锁(IS)。
也会加上一个行级锁中的记录锁。
data_locks表的列信息如下:
列名 | 说明 |
ENGINE | 存储引擎名字,比如INNODB |
ENGINE_LOCK_ID | 存储引擎持有或者请求到的锁的ID |
ENGINE_TRANSACTION_ID | 事务ID |
THREAD_ID | 创建锁的会话线程ID |
EVENT_ID | 引起锁的时间ID |
OBJECT_SCHEMA | 锁定表的SCHEMA名 |
OBJECT_NAME | 锁定的表名 |
PARTITION_NAME | 分区名 |
SUBPARTITION_NAME | 子分区名 |
INDEX_NAME | 锁定的索引名 |
OBJECT_INSTANCE_BEGIN | 锁的内存地址 |
LOCK_TYPE | 锁的类型,依赖存储引擎,在INNODB中,TABLE代表表级锁,RECORD代表行级锁 |
LOCK_MODE | 锁模式,依赖存储引擎,在INNODB中,取值主要有 S[,GAP], X[,GAP], IS[,GAP], IX[,GAP], AUTO_INC, and UNKNOWN,其中S代表共享锁、X代表排他锁,GAP代表间隙锁等。 |
LOCK_STATUS | 锁状态,依赖存储引擎,在INNODB中,GRANTED代表获取到了锁(持有锁),WAITING等待。 |
LOCK_DATA | 锁关联数据(如果没有则是NULL),在INNODB中,如果是一个行级锁(LOCK_TYPE=RECORD)则显示一个具体的值,否则显示NULL。如果是主键索引上的锁则值为主键。当锁是在辅助索引上时,则显示辅助索引的值,并附加上主键值。 |
详解
SELECT...FOR SHARE
查看锁情况:
SELECT...FOR SHARE会加上意向共享锁(IS),有了意向共享锁后能够使用lock tables t1 read加上表读锁吗?
并未阻塞,所以IS锁和表读锁是兼容的。
再看下IS和表写锁是否是兼容的:
从途中可以看到lock tables t1 write被阻塞住了,所有IS锁和表写锁是互斥的。
DML所加的意向锁都是IX锁(意向排他锁)
上图中执行了一个INSERT语句,查看下加的元数据锁类型:
可以看到加了一个意向排他锁(IX)。
接下来在看下他与表锁的兼容情况。
加表读锁,结果阻塞,无法成功,说明意向排他锁与表读锁是互斥的。
再看下能否加上表写锁。
可以看到也是无法加上表写锁的得到结论:
再看下UPDATE语句。
IX锁与表读锁互斥。
在看下IX锁与写锁兼容性:
IX锁与表写锁互斥。
综上可以得出如下结论:
意向共享锁(IS)与表读锁是兼容的,与表写锁是排斥的,意向排他锁(IX)与表锁(无论读锁还是写锁)都是互斥的,说明意向锁和表锁严格遵守读写锁的【读读共享】【读写排他】【写写排他】的特性。
自增锁
自增锁是表级锁中的一种,他是一种隐式锁,又系统自动加锁和解锁,该锁的唯一用处就是保证自动主键的数据一致性、准确性。
自增主键的表是很常见的一种表设计方式,比如:
CREATE TABLE `t1` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
ID是自增的,数据库必须保证,多个会话添加数据的时候,ID不能出现重复的情况,这就是通过自增锁实现的。
相关推荐
- TCP协议原理,有这一篇就够了
-
先亮出这篇文章的思维导图:TCP作为传输层的协议,是一个软件工程师素养的体现,也是面试中经常被问到的知识点。在此,我将TCP核心的一些问题梳理了一下,希望能帮到各位。001.能不能说一说TC...
- Win10专业版无线网络老是掉线的问题
-
有一位电脑基地的用户,使用...
- 学习计算机网络需要掌握以下几方面基础知识
-
计算机基础知识操作系统:了解常见操作系统(如Windows、Linux)的基本操作和网络配置,例如如何设置IP地址、子网掩码、网关和DNS服务器等,以及如何通过命令行工具(如ping、tr...
- 网络工程师的圣经!世界级网工手绘268张图让TCP/IP直接通俗易懂
-
要把知识通俗地讲明白,真的不容易。——读者说TCP/IP从字面意义上讲,有人可能会认为TCP/IP是指TCP和IP两种协议。实际生活当中有时候也确实就是这两种协议。然而在很多情况下,它只是...
- 三分钟了解通信知识TCP与IP协议(含“通信技术”资料分享)
-
TCP/IPTCP/IP分层模型①应用层...
- 网闸与防火墙:网络安全设备的差异与应用
-
在网络安全领域,网闸(安全隔离网闸,GAP)和防火墙(Firewall)是两类重要的防护设备。尽管它们都服务于网络安全防护,但在设计理念、技术原理、安全效能及适用场景等方面存在显著差异,以下从五个维度...
- S7-300的TCP/IP通信
-
一、首先在项目中创建2个S7-300的站点;二、硬件组态中,设置合适的TCP/IP地址,在同一网段内;...
- 西门子S7-1500 PLC的 MODBUS TCP通信
-
MODBUSTCP使MODBUS_RTU协议运行于以太网,MODBUSTCP使用TCP/IP和以太网在站点间传送MODBUS报文,MODBUSTCP结合了以太网物理网络和网络标准TC...
- 系统规划与管理师新版备考必备:第7章考点思维导图解析
-
备考系统规划与管理师的小伙伴们,福利又来啦!今天为大家带来《系统规划与管理师(第2版)》第7章考点的思维导图,助你高效梳理重点,让备考更有方向!...
- TCP/IP、Http、Socket 有何区别与联系?
-
HTTP协议对应于应用层,Socket则是对TCP/IP协议的封装和应用(程序员层面上)。HTTP是应用层协议,主要解决如何包装数据。而我们平时说的最多的Socket是什么呢?实际上...
- 西门子PLC串口协议与以太网通信协议对比
-
西门子plc品牌众多,通信协议的类型就更多了,具体可分为串口协议和以太网通信协议两大类。...
- 网络编程懒人入门(十三):一泡尿的时间,快速搞懂TCP和UDP的区别
-
本文引用了作者Fundebug的“一文搞懂TCP与UDP的区别”一文的内容,感谢无私分享。1、引言...
- 程序员必备的学习笔记《TCP/IP详解(一)》
-
为什么会有TCP/IP协议在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样...
- 一文读懂TCP/IP协议工作原理和工作流程
-
简述本文主要介绍TCP/IP协议工作原理和工作流程。含义TCP/IP协议,英文全称TransmissionControlProtocol/InternetProtocol,包含了一系列构成互联网...
- 如何在 Windows 10 和 Windows 11 上重置 TCP/IP 堆栈
-
传输控制协议/Internet协议,通常称为TCP/IP,是您的WindowsPC如何与Internet上的其他设备进行通信的关键部分。但是当事情出错时会发生什么?你如何解决它?幸运的...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)