MySQL的锁:

目录

锁的介绍:

并发事务访问相同数据可以分为以下几种情况:

都是进行读操作:

都是进行写操作:

有读操作也有写操作:

读锁、写锁:

读锁:

写锁:

按照锁粒度分类:

表锁:

读锁、写锁:

意向锁:

意向共享锁:

意向排他锁:

自增锁:

元数据锁:

行锁:

记录锁:

间隙锁:

临建锁(记录锁+间隙锁的组合):

插入意向锁:

页锁:

锁的态度:

悲观锁:

乐观锁:

隐式锁:

隐式锁的逻辑过程:

显式锁:

全局锁:

写操作的锁机制:

删除操作:

更新操作:

键值不变且空间也不变:

键值不变但是空间变化:

键值改变:

插入操作:

死锁:

死锁的四个必要条件:

死锁的避免方法:

死锁的处理方法:


锁的介绍:

在数据库中除了计算资源外,数据也是共享的资源,为了保证数据的一致性,需要对并发操作进行控制。

锁是计算机协调多个进程或者线程并发访问某一资源的机制。当多线程并发访问某个数据,需要保证在任何时刻最多只有一个线程在访问,保证数据的完整性和一致性。锁机制为实现MySQL的各个隔离级别提供了保证。锁冲突也是影响数据库并发访问性能的一个重要因素。

并发事务访问相同数据可以分为以下几种情况:

都是进行读操作:

因为读操作并不会对数据产生任何影响,所以不会引发问题,是允许这种情况的发生。

都是进行写操作:

在这种情况下,会产生脏写的问题,任何一种隔离级别都不允许脏写发生。所以在多个事务对同一数据进行操作时,需要排队执行。而排队的过程就通过锁来实现。

有读操作也有写操作:

这种情况就可能会发生脏写、脏读、不可重复读、幻读问题。

锁其实是一个内存中的结构,在事务执行前是没有锁的,也就是一开始没有锁结构和记录进行关联。当一个事务想对一条记录做改动时,会先看内存中是否存在与这条记录关联的锁结构,如果不存在,则生成一个锁结构与之关联。

比如:事务A对某条数据进行修改,就会生成一个锁结构与该记录关联。因为在这之前没有别的事务对这条记录枷锁,所以加锁成功,继续执行操作。在事务A提交之前,事务B对同一条记录进行修改操作,那么也会看是否有锁结构与该记录有关联,因为事务A对该数据进行操作,该数据已经有一个锁结构与之关联。虽然也会生成锁,但是此时事务B就会加锁失败。需要等待事务A提交之后,将事务A生成的锁结构释放掉之后,检查是否有别的事务在等待加锁。此时事务B就会加锁成功,就能对该条记录进行操作。

上述说一条记录加锁也就是在内存中创建一个锁结构与该记录关联。但是如果一个事务对多条记录进行加锁,会占用很大的空间。

所以在对不同记录加锁时,将以下的记录放到一个页结构中:

同一个事务中进行加锁,被加锁的记录在同一个页面中,加锁的类型是一样的,等待的状态相同。

读锁、写锁:

对于写-写、写-读或者读写这些情况可能引起一些问题,为了能让这三种情况中的操作相互阻塞,所以MySQL实现了一个由两种类型的锁组成的锁系统,通常被称为共享锁和排他锁,也叫读锁(S锁)和写锁(X锁)。

读锁:

用英文S表示,针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻塞。

读取一条记录时会获取该记录的读锁还要获取该记录的写锁来防止其他事务对该记录进行修改。

select ... lock in share mode;

当加上"lock in share mode",当前事务执行了该语句,那么就会为读到的记录加上读锁。其他事务也可以获取该记录,不会阻塞,但是不能获取该记录的写锁,如果想要获取写锁,那么则会排队阻塞,直到当前的事务提交之后,该记录上的读锁释放掉。

写锁:

用英文X表示,当前写操作没有完成前,会阻塞其他写锁和读锁,能确保只有一个事务执行写入,并防止其他用户读取正在写入的同一数据。

select ... for update;

当加上"for update",当前事务执行了该语句,则会为读取到的记录加上写锁,其他事务则不能对该记录同时进行修改操作,也不允许其他事务获取该记录的读锁。也就是其他事务要获取该记录的读锁或者写锁,都会阻塞,直到当前事务提交之后将这些记录上的写锁释放掉。如果等待获取锁的时间超过了innodb_lock_wait_timeout参数的值则会结束。

注意:当执行"select ... for update;"语句会将所有扫描的行加上锁,所以要确保使用索引,避免全表扫描。

按照锁粒度分类:

从数据操作的粒度划分,可以分为表级锁、页级锁、行锁。

为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好,但是对于资源的消耗会更大。因此数据库系统需要在并发响应和系统性能两方面进行平衡。

每个层架的锁数量是有限的,因为锁会占用内存空间,锁空间的大小是有限的。当锁的数量超过了这个层级的阈值时,就会用更大粒度的锁代替多个粒度小的锁,也就是锁升级。

表锁:

读锁、写锁:

表锁会锁定整张数据表,并不依赖于存储引擎,是最基本的锁策略。表锁的开销相对来说是最小的,因为粒度比较大,对于资源的消耗更小。由于表锁一次会将整个表锁定,所以可以避免死锁问题。但是锁的粒度大会带来锁资源争用的概率高,导致并发率大打折扣。

InnoDB引擎不会为对增删改查语句进行对表加上读锁或者写锁。在执行对DDL语句时对表结构进行修改,那么其他事务对整个表的增删改查则会发生阻塞。同理当事务对某个表执行增删改查时,其他事务对表进行DDL语句进行表结构的修改也会发生阻塞。这是通过Server层使用的一种元数据锁结构实现的。

一般情况下,不会使用InnoDB存储引擎的表级读锁和写锁。

lock tables 表名 read;
lock tables 表名 write;

MyISAM执行查询语句前,会给涉及的表加上读锁,对于增删改操作,会给涉及的表加上写锁。

意向锁:

InnoDB支持多粒度锁,允许行级锁与表级锁共存,意向锁就是一种表锁。意向锁不会与行级锁冲突,只会与表级读锁和写锁冲突。实现了行锁和表锁共存,并且满足了事务的隔离性要求。

意向共享锁:

事务有意向对表中的某些行加共享锁(读锁)。

意向排他锁:

事务有意向对表中的某些行加排他锁(写锁)。

意向锁是由存储引擎维护的,无法手动操作意向锁,在数据行加共享锁或者排他锁之前,InnoDB会先获取该数据行所在数据表的对应意向锁。

当给某一行数据加了排他锁,数据库会自动给大一级空间,如数据页或者数据表加上意向锁,其他事务想获取数据表的排他锁时,需要排队。如果事务是想获取数据表中的某些记录的共享锁,就需要在数据表上添加意向共享锁。如果是事务想要获取数据表中的某些记录的排他锁,就需要在数据表上添加意向排他锁。

意向锁之间时互相兼容的,除了意向共享锁和共享锁之外与普通的共享锁或者排他锁是互斥的。

自增锁:

当对含有自增列的表中进行插入数据时,需要获取的一种表级锁。在多个事务并发情况下,当一个事务对该表进行插入时,就会加上一个自增锁,为自增列分配递增的值,其他事务都需要等待阻塞,只有当该事务提交之后,把自增所释放掉,其他事务才能继续对该表进行插入。所以并发性不高。InnoDB通过innodb_autoinc_lock_mode参数的值来提供不同的锁机制和,提高了SQL语句的可伸缩性和性能。

1.innodb_autoinc_lock_mode=0,传统锁定模式。

2.innodb_autoinc_lock_mode=1,连续锁定模式。

3.innodb_autoinc_lock_mode=2,交错锁定模式。

元数据锁:

也称为DML锁,当对一个表做增删改查操作时,加元数据读锁,当对表的结构进行更新时,加元数据写锁。这里指的是从元数据锁的角度,读锁之间不互斥,所以可以多个线程同时对一张表增删改查。读写锁之间和写锁之间是互斥的,用于保证数据表结构的安全性,解决了DML和DDL操作之间的一致性。不需要显示使用,在访问一个表时会自动加上。

行锁:

也称为记录锁,行级锁只在存储引擎层实现。

锁定力度小,发生锁冲突的概率低,可以实现的并发度更高,但是对于锁的开销比较大,加锁会比较慢,容易出现死锁。

记录锁:

只把一条记录锁上,对该记录周围的数据没有影响,基于主键或者唯一索引精确匹配。

记录锁是有读写锁之分的,和读锁写锁是一样的。

间隙锁:

用于解决幻读,给某条记录加上间隙锁,就不允许别的事物在该记录前面的间隙插入新记录,也就是该记录和其前面记录的主键值之间不允许插入新记录。

临建锁(记录锁+间隙锁的组合):

当想即对某条记录进行加锁,又想赋值其他事务在该记录前面的间隙插入新记录,就可以使用临建锁。临建锁是在InnoDB存储引擎中事务级别在可重复读的情况下使用的数据库锁。InnoDB中默认使用的是临建锁。可以预防大部分幻读和范围数据修改。

插入意向锁:

一种‌特殊的间隙锁‌,与普通间隙锁不同,它允许‌同一间隙内不同位置的并发插入‌。解决普通间隙锁在高并发插入时的性能问题,减少不必要的阻塞。

一个事务插入一条记录时,需要判断插入位置是否加了锁,如果有的话,那么该事务需要进行等待。而在InnoDB中规定事务在等待时需要在内存中生成一个锁结构,就会加上插入意向锁,表示有事务想在某个间隙中插入新记录。在insert时产生,不是意向锁,在插入一条记录前,有insert产生的一种间隙锁,用于表示插入意向,多个事务对同一区间插入位置不同的多条记录时,不需要互相等待。插入意向锁不会阻止别的事务继续获取该记录上的任何类型的锁。

页锁:

页锁锁定的数据资源比行锁要多,当使用页锁时,会出现数据浪费的现象。页锁的开销时在表锁和行锁之间,会出现死锁的情况,并发度一般。

锁的态度:

悲观锁:

对数据被其他事务的修改持保守态度,通过数据库自身的锁机制来实现,从而保证数据库操作的排他性。共享资源每次只给一个线程使用,其他线程阻塞,用完之后再将资源给其他线程。

乐观锁:

不采用数据库自身的锁机制,通过程序来实现,可以采用版本号机制或者CAS机制实现。不用每次都对数据上锁,但是在更新时会判断在此期间是否有事务更新这个数据。读操作多,写操作少,不存在死锁场景。

隐式锁:

由MySQL引擎自动施加和释放,无需手动操作。通过事务ID(trx_id)标记数据行的锁定状态。例如插入新记录时,系统自动用当前事务ID标记该行,其他事务访问时会检测此ID是否活跃,从而触发锁等待或转换‌。

隐式锁的逻辑过程:

InnoDB中每条记录都有一个隐含的trx_id字段,这个字段存在于聚簇索引中的B+树中。在一条操作记录之前,首先根据记录中的trx_id检查该事务是否是活动的事务,如果是那么将隐式锁转换为显式锁,也就是为该事务添加一个锁。检查是否有锁冲突,如果有,那么创建锁之后将锁结构中的is_waiting置为true,如果没有,则直接将该事务的id写入到trx_id字段,如果没有,那么则进行等待阻塞,直到超时或者被唤醒,然后会将事务id写入到trx_id字段中。

对于非聚簇索引,本身没有trx_id隐藏列,但是非聚簇索引的页面的页头结构中有一个属性记录修改当前页的最大事务id,如果记录的事物id小于当前活跃的事务id,那么说明记录的事务已经提交了,相反,那么则需要在页面中定位到对应的二级索引记录,进行回表进行上述的逻辑过程。

显式锁:

需开发者通过SQL语句主动声明加锁与释放‌。

全局锁:

全局锁也就是对整个数据库的实例进行加锁。当需要整个库处于只读时,其他线程对该数据库的实例进行数据更新语句、数据定义语句等就会被阻塞。全局锁可以用于全库逻辑备份。

写操作的锁机制:

删除操作:

先通过B+树索引(主键或者二级索引)定位目标记录位置,对其执行锁定读,也就是加上排他锁,防止其他事务的访问。然后将记录的删除标识从0置为1,让该条记录移入垃圾链表。

更新操作:

键值不变且空间也不变:

通过B+树进行定位目标记录位置,将其执行锁定读,在原记录的位置进行修改操作。

键值不变但是空间变化:

通过B+树进行定位目标记录位置,将其执行锁定读,然后将该记录移入垃圾链表中,再重新插入一条新纪录。新插入的记录由隐式锁进行保护。

键值改变:

在原记录上做了删除操作之后再进行一次插入操作,加锁需按照删除操作和插入操作规则进行。

插入操作:

新记录插入时不立即加显式锁,通过隐式锁的结构来保护新记录不会被其他事务访问。

死锁:

死锁是数据库并发控制中常见的问题,指两个或多个事务互相持有并等待对方释放资源,导致无限期阻塞的状态‌。在MySQL中,死锁主要发生在InnoDB存储引擎的行级锁场景下。

死锁的四个必要条件:

1.互斥条件:资源只能被一个事务独占使用,其他事务无法同时访问同个资源。

2.请求与保持条件:事务在持有至少一个资源的同时,请求新的资源且不释放已持有资源。

3.不可剥夺条件:资源只能由持有它的事务主动释放,不能被强制剥夺。

4.循环等待条件:事物之间形成了环形等待链,如事务A等待事务B,而事务B等待事务A。

死锁的避免方法:

合理设计索引,减少扫描的行,从而降低锁竞争。

尽量减少事务的内部操作,避免大事务占用锁,缩短锁定资源的时间,减少锁冲突的概率。

避免长时间持有锁的SQL语句在事务前面部分,所有事务以相同顺序访问资源,避免交叉操作。

死锁的处理方法:

1.等待超时,当其中一个事务超时,就将其回滚,另外的事务就能继续进行。

2.使用死锁检测进行死锁处理,InnoDB提供了wait-for graph算法来实现死锁检测,每当加锁请求无法立即满足需要等待时,就会触发wait-for graph算法。通过事务等待链表中记录的事务信息和锁的信息链表绘制一个事务等待的有向图,当有向图出现了环,表明出现了死锁的情况。此时InnoDB就会选择undo日志量最小的事务进行回滚,让其他事务继续执行。每一个新的阻塞线程,都要判断是否导致了死锁出现,耗费时间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93664.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93664.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一道同分排名的SQL题

1 概述遇到这样一道题:(1) 有一张学生课程分数表,字段有:ID、名称、性别、科目、分数。(名称换为学号更能标识唯一学生,但名称好阅读,故这里先认为名称可以唯一标识学生。)(2) 用一个SQL&#x…

ICCV 2025 | Reverse Convolution and Its Applications to Image Restoration

标题:Reverse Convolution and Its Applications to Image Restoration作者:Xuhong Huang, Shiqi Liu, Kai Zhang, Ying Tai, Jian Yang, Hui Zeng, Lei Zhang单位:Nanjing University, The Hong Kong Polytechnic University, OPPO Research…

mysql启动超时

mysql启动超时: 管理员打开CMD后允许net start MySQL57, 启动超时检查错误日志 MySQL 启动失败的具体原因通常记录在错误日志中。 日志路径(根据你的安装方式可能不同): 默认位置:C:\ProgramData\MySQL\MyS…

Flink Stream API 源码走读 - window 和 sum

本文核心观点 核心观点:WindowedStream 是一个"假流",它比 KeyedStream 更虚,只是一个 API 的过渡器,不是真正意义上的 DataStream,需要调用函数回归。 虚拟化时刻:从真实流到虚拟流 KeyedStream…

蓝牙 GFSK RX Core 架构解析

GFSK RX Core分为以下几个模块: 1.Frequency offset compensation CORDIC 2.A low pass filter 3.A power estimator for packet detection,RSSI and digital gaion computation for DPSK path 4.A demodulator implemented as Phase Shift Discriminator 5.A drequ…

微电网管控系统中python多线程缓存与SQLite多数据库文件连接池实践总结(含源码)

1. 引言 在分散的微电网能源管理场景中,系统采用集中式云平台模式,为100个独立微电网用户提供高并发数据写入服务面临三大挑战:用户数据隔离、I/O性能瓶颈、多线程安全性。本文揭示一种新式的分片锁+三级缓存+sqlite多数据库文件连接池架构,在保持SQLite轻量级优势的同时,…

InfluxDB 开发工具链:IDE 插件与调试技巧(一)

引言 ** 在当今数字化时代,时间序列数据的处理与分析在众多领域中都扮演着至关重要的角色。无论是物联网设备产生的海量传感器数据,还是金融市场中实时波动的交易数据,又或是服务器运维过程中不断产生的性能指标数据,这些都属于…

计算机网络-IPv6

1、IPv6基础IPv4与IPv6的对比:问题IPv4的缺陷IPv6的优势地址空间IPv4地址采用32比特标识,能提供的地址数量是43亿,分配很不均衡。针对IPv4的地址短缺问题,有几种解决方案:无类别域间路由CIDR(Classless Int…

整体设计 之“凝聚式中心点”原型 --整除:智能合约和DBMS的深层融合 之2

摘要(CSDN的AI助手自动生成的)本文提出了一种基于"整除"数学原型的智能合约与DBMS融合架构设计,将SQL查询语句的四个关键段(SELECT、FROM、WHERE、BY)分别映射到整除运算的四个要素(商、被除数、…

【赵渝强老师】TiDB表数据与键值对的映射关系

TiDB实例将表中的每一行数据映射成RocksDB中的键值对,则需要考虑如何构造Key和Value。首先,OLTP场景下有大量针对单行或者多行的增、删、改、查等操作,要求数据库具备快速读取一行数据的能力。因此,对应的Key最好有一个唯一ID&…

带操作系统的延时函数

delay.c:#include "delay.h"/*** brief 微秒级延时* param nus 延时时长,范围:0~233015* retval 无*/ void delay_us(uint32_t nus) {uint32_t ticks;uint32_t tcnt 0, told, tnow;uint32_t reload SysTick->LOAD; //重…

ES Module 和 CommonJS的区别

ES Module(ESM,ES6 模块系统)和 CommonJS 是 JavaScript 中两种主流的模块规范,分别用于现代前端和 Node.js 环境(早期),它们在语法、加载机制、特性等方面有显著区别。以下是详细对比&#xff…

猫头虎AI分享|一款智能量化交易系统:QuantCell,从数据收集到策略执行全流程自动化

猫头虎AI分享|一款智能量化交易系统:QuantCell,从数据收集到策略执行全流程自动化 在当今金融市场中,量化交易系统已经成为越来越多投资者和机构的重要选择。无论是股票、期货还是加密货币,自动化交易与人工智能的结合…

直播美颜SDK架构揭秘:动态贴纸功能的实现原理与性能优化

如今,美颜SDK 已经不再只是“磨皮、美白”的基础工具,而是逐渐进化为一个涵盖 人脸识别、实时特效、动态贴纸交互 的复杂技术体系。尤其是 动态贴纸功能 的加入,让主播与观众之间的互动更加生动有趣,也成为提升用户粘性与平台差异…

Docker安装CDC

Docker安装CDC拉取镜像离线形式安装上传文件并创建docker-compose.yml把镜像加载到docker中启动容器连接数据库创建账号,并给账号授权设置wal_level确认wal_level的值创建链接查询连接状态使用kafdrop消息中看不到修改之前的信息怎么办补充拉取镜像 docker pull co…

如何在win服务器中部署若依项目

一、安装jdk的环境: 这一步很简单,直接拿到安装包双击安装即可。 二、配置jdk的环境变量默认安装的路径为:C:\Program Files (x86)\Java\jdk1.7.0_51安装完成之后进行环境变量配置右击计算机(此电脑)点击属性点击高级系…

CSS从入门到精通完整指南

第一部分:CSS基础入门1.1 什么是CSSCSS(层叠样式表,Cascading Style Sheets)是用于描述HTML文档外观和格式的样式语言。CSS将内容与表现分离,让HTML专注于内容结构,CSS专注于视觉效果。1.2 CSS语法结构选择…

重温k8s基础概念知识系列二(Pod)

文章目录1、Pod概念2、K8s 中的 Pod 的两种用法3、定义Pod4、Pod的创建资源5、Pod 模板6、容器探针7、总结干货8、 K8s Pod 经典面试题速查表Pod是Kubernetes中最小的单元: 1、Pod概念 Pod 是可以在 Kubernetes中创建和管理的、最小的可部署的计算单元。它由一组、一…

设计模式之静态代理

一些个人理解 顾名思义,就是代理一个对象。 那么,既然要代理一个东西,就要传入它吧? 【1】所以将代理对象当作属性【【2】往往通过构造方法传入被代理的目标对象】。 既然要代理,那必然要和代理对象拥有相同的功能吧? 所以实现了…

牛津大学xDeepMind 自然语言处理(1)

牛津大学xDeepMind 自然语言处理 Natural Language Processing 词向量与词汇语义学 Word Vectors and Lexical Semantics 词语表示的基本问题与分布语义思想 传统词语表示(如独热向量)存在稀疏、正交、语义弱的问题,无法表达语义相似性。分布…