【MySQL】锁详解——从结构分类到适用场景

news2024/12/28 3:22:16

我们要学习锁首先要了解下我们想了解的锁到底是什么🤔

而在MySQL中给某个数据加锁的本质其实就是在内存中创建一个锁结构与之关联,而这个锁结构就是我们常提到的MySQL的锁🔒

那么接下来的问题就是,这个锁结构长啥样呢?

锁的内部结构(InnoDB)

一张图详解锁结构( ̄∇ ̄)/

 为了节约资源,并非每个锁都有一个单独的锁结构与之对应,符合如下条件的记录就会放在同一个锁结构中

  • 在同一个事务/页面中进行的加锁操作

  • 加锁的类型一样

  • 等待状态一样

锁的分类

  • 按操作方式

    • 读锁/共享锁/S(Share Lock)

    • 写锁/排他锁/X(Exclusive Lock)

  • 按锁粒度(Lock Granularity)

    • 全局锁

    • 表级锁(Table Lock)

      • 表级别的共享锁和排他锁

      • 意向锁(Intention Lock)

      • 自增锁(AUTO-INC)

      • 元数据锁(MDL)

    • 页级锁

    • 行级锁

      • 记录锁(Record Locks)

      • 间隙锁(Gap Locks)

      • 临键锁(Next-Key Locks)

      • 插入意向锁(Insert Intention Locks)

  • 按思维方式/设计思想

    • 乐观锁(Optimistic Concurrency Control)

    • 悲观锁(Pessimistic Concurrency Control)

  • 按加锁方式

    • 隐式锁

    • 显式锁

共享锁 vs 排他锁

并发事务中"读-读"的情况一般不会引起什么问题,一般需要解决的就是对于"写-写"和"读-写"/"写-读"引起一些问题,主要有两种解决方式:加锁,或者需MVCC

既要允许"读-读"情况不受影响,又要使"写-写"、"读-写"或"写-读"情况中的操作相互阻塞,所以MySQL实现一个由两种类型的锁组成的锁系统来解决

这两种类型的锁通常被称为共享锁(Shared Lock,S Lock)和排他锁(Exclusive Lock,X Lock),也叫读锁(readlock)和写锁(write lock )

  • 共享锁/S(读锁)

    • 针对同一份数据,多个事务的读操作可以同时进行而不会互相影响,相互不阻阻塞

  • 排他锁/X(写锁)

    • 当前写操作没有完成前,它会阻断其他写锁和读锁

    • 这样就能确保在给定的时间里,只有一个事务能执行写入,并防止其他用户读取正在写入的同一资源

一句话概括就是,不同事务中,只有都是共享锁才互不影响不会阻塞,但凡有一个事务有排他锁,都会阻塞,对于InnoDB引擎来说,共享锁和排他锁可以加在表上,也可以加在行上

如何加锁

  • 加共享锁

    • SQL语句 + "LOCK IN SHARE MODE" / "FOR SHARE";

  • 加排他锁

    • SQL语句 + "FOR UPDATE";

需要注意的是:

  • 读操作(SELECT)可以加共享锁,也可以加排他锁

  • 写操作(DELETE、UPDATE、INSERT)只能加排他锁

    • INSERT

      • MySQL通过"隐式锁"来维护

    • DELETE

      • B+树上定位 -> 获取X锁 -> 执行delete mark操作

    • UPDATE

      • 修改键值:DELETE操作 + INSERT操作

      • 不修改键值:

        1. 不修改存储空间:B+树上定位 -> 获取X锁 -> 修改

        2. 修改存储空间:B+树上定位 -> 获取X锁 -> 删除原纪录(移入垃圾链表) -> 插入新纪录(INSERT操作)

补充:MySQL 8.0 新特性

  • nowait

    • 查询的数据已经加锁,立刻返回报错

  • skip locked

    • 立刻返回未被锁定的行

全局锁 vs 表级锁 vs 页级锁 vs 行级锁

数据库的并发度和锁的粒度息息相关,锁的粒度越小,并发度越高,但也越耗资源(锁的获取、检查、释放等操作),影响系统性能,因而我们需要根据实际的业务场景,在高并发响应与系统性能之间进行平衡

全局锁

对整个数据库实例进行加锁,加锁后整个数据库处于只读状态,命令如下:

Flush tables with read lock

表锁

  • 开销小,但并发性较差

  • 可以避免死锁

不与行级锁冲突的表级锁:意向锁

主要用于在已经加了行级锁后,会在上一层记录中自动加一个意向锁,用于判断当前数据页或者数据表是否被加过锁,意向共享锁和意向排他锁之间是任意组合都是相互兼容的,但是与普通的共享锁/排他锁之间,只有意向共享锁与普通共享锁兼容,其余都互斥

具体使用意向共享锁,还是意向排他锁,要根据行级锁(与行级锁一致)

  • 意向锁之间不互斥

  • 意向锁与行级锁不互斥,与表级锁互斥

  • 意向锁在保证高并发的前提下,实现了行锁与表锁共存,并且满足事务隔离性的要求

自增锁

建表字段中如果包含"AUTO_INCREMENT"关键字,每当插入操作时,就要对自增锁进行竞争,以保证主键唯一单调递增

首先,插入数据的模式有3种:

  1. 简单插入(Simple inserts)

    事先明确插入的数量
  2. 批量插入(Bulk inserts)

    基于现有的表进行插入(事先并明确插入的数量)
  3. 混合模式插入(Mixed-mode inserts)

    插入的数据有的有主键,有的没主键

主要有3种模式

  1. "传统"锁定模式(并发性较差)

    - innodb_autoinc_lock_mode=0
  2. "连续"锁定模式(8.0前默认)

    innodb_autoinc_lock_mode=1
  3. "交错"锁定模式(8.0后默认)

    innodb_autoinc_lock_mode=2

元数据锁(MDL)

可以通过下面👇代码查看当前执行的process信息

show processlist;

如果想查看在等待哪把锁,可以重点关注下State字段

页锁

  • 开销、粒度、并发度 介于表锁和行锁之间

  • 也会出现死锁

  • 每个层级的锁数量是有限制的(锁会占用内存空间),锁空间的大小是有限制的,某个层达到阈值后就会进行锁升级(用更大力度的锁代替多个较小粒度的锁)

    • 比如一张表中出现大量DELETE操作时,会锁表

行锁(InnoDB)

行锁又称记录锁,锁的是某一行数据

MySQL的服务层并没有实现行锁机制,行锁是在存储引擎层实现的

  • 优点

    • 锁粒度小,发生锁冲突概率低,可实现的并发度高

  • 缺点

    • 锁的开销较大,加锁较慢,会出现死锁

记录锁

可参照读锁和写锁理解,只不过粒度是行,只有操作相同的行才有影响

间隙锁(解决幻读问题)

在MySQL的可重复读的隔离级别下,可以解决幻读问题

因为在一开始进行读取操作的时候,我们并不知道会产生幻影数据的主键是多少(还不存在),因而无法使用记录锁事先锁定那一行,间隙锁会阻塞当前记录和其前一条记录之间的插入操作,举个例子:

当前加锁的记录id为8,前一条记录id为5,加了间隙锁会不允许别的事务插入任何id在(5,8)之间的数据,直到拥有这个间隙锁的事务提交后

临键锁

一句话理解~就是记录锁+间隙锁(不愧是我(*≧ω≦)/)

插入意向锁

获取锁失败的事务,开始等待的事务的其锁的类型就是插入意向锁,插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁

乐观锁 vs 悲观锁

悲观锁

悲观锁是通过数据库自身的锁机制来实现,从而保证致据操作的排它性。

悲观锁总是假设最坏的情况,认为每次去查数据的时候都会被别人修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)比收如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,当其他线程想要访问数据时,都需要阻塞挂起

如果在SELECT语句上加悲观锁,在这条语句执行过程中所有扫描的行都会被锁上,因此在MySQL中使用悲观锁需要确定必须使用了索引,不然会全表扫描,把整张表都锁上

乐观锁

乐观锁以为对同一数据的并发操作不会总发生,不用每次都对数据上锁,但是在更新的时候会判断一下在此期间别人有设有去更新这个数据,也就是不采用数据库自身的锁机制,而是通过程序来实现的。在程序上,我们可以采用版本号机制或者CAS机制实现,乐观锁适用于多读的应用类型,这样可以提高吞吐量

ps:在Java中java.util.concurrent.atomic包下的原子变量类就是使用了乐观锁的CAS实现的

1. 版本号机制

在表中设计一个饭木字段version,第一次读的时候,会获取version字段的取值。然后对数据进行更新或删除操作时,会执行UPDATE..·SET version=version+1 NHERE version=version。此时如果已经有事务对这条数据进行了更改,修改就不会成功。

这种方式类以我们展悉的SN、CVS版本管理系统,当我们修改了代玛进行提交时,首先会检查当前版木号与服务器上的版本号昏一致,如果一致就可以直接提交,如果不一致就需要更新服务器上的最新代码,然后再进行提交。

2. 时间戳机制

时间戰和版本号机制一样,也是在更新提交的时候,将当前数据的时间戳和更新之前取得的时间戳进行较,如果两者一致则更新功,否则是版本冲突,基本是通过给数据行增加一个戳(版本号或者时间戳),从而证明当前拿到的数据是否最新

总结

  • 乐观锁适用于读多的场景;悲观锁适用于写多的场景

  • 乐观锁基于程序实现,不会产生死锁;悲观锁基于MySQL的锁机制实现

显式锁 vs 隐式锁

隐式锁

一个事务在执行INSERT操作时,如果即将插入的侧除已经被其他争务加了间隙锁,那么本次INSERT操作会阻塞,并且当前事务会在该间隙上加一个插入意向锁,否则一股情况下INSERT操作是不会显式加锁的,执行逻辑大致如下:

  1. InnoDB的每条记录中都有一个隐藏的trx_id字段,存于聚簇索引的B+树中

  2. 在操作一条记录前,首先根据记录中的trx_id检查该事务是否是活动的事务(未提交、回滚),如果是活动的事务,就会现将隐式锁转换为显式锁

  3. 检查是否有锁冲突,如果有,创建锁,并设置为waiting的状态,没有就执行步骤5

  4. 等待加锁成功,被唤醒,或者超时

  5. 写数据,并将自己的trx_id写入trx_id字段

显式锁

通过特定语句进行添加的锁,一般称为显式锁("FOR UPDATE"等)

搞定撒个花(。・ω・。)ノ🎉

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

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

相关文章

ASEMI代理ADI亚德诺AD8065ARTZ-REEL7车规级芯片

编辑-Z AD8065ARTZ-REEL7芯片参数: 型号:AD8065ARTZ-REEL7 −3dB带宽:145MHz 0.1 dB平坦度的带宽:7MHz 输入超速恢复时间:175ns 输出恢复时间:170ns 斜率:180V/μs 三阶拦截&#xff1a…

教育大数据总体解决方案(2)

系统管理对当前大数据基础平台的所有服务及组件版本、账户以及自动启动进行统一管理。 服务版本查看当前大数据基础平台的所有服务信息及版本状态。 服务用户和组查看当前大数据基础平台的所有用户信息。 服务自动启动对当前大数据基础平台的所有服务组件进行自启动设置。 后台…

【信息安全】EDR、HIDS、NDR、MDR、XDR 区别与联系

【前言】 随着安全态势的发展,为应对不同的安全防御场景需求,安全产品层出不穷,各大安全厂商也争先推出自家的安全产品/平台,这就导致产品种类繁多,信息量大而杂,本篇博文重点讲解EDR、HIDS、NDR、MDR和XDR…

LinuxGUI自动化测试框架搭建(二)- 详细设计框架设计

(二)-详细设计&框架设计1 需求分析2 技术栈3 框架设计3.1 框架说明3.2 框架执行流程4 预期结果4.1 测试过程log日志4.2 测试报告html格式4.3 测试报告邮件格式1 需求分析 对 实现需求 进行详细分析,主要有下: 功能说明使用U…

经典文献阅读之--FastFlowNet(轻量光流估计)

0. 简介 密集的光流估计在许多机器人视觉任务中起着关键作用。随着深度学习的到来,已经比传统方法以令人满意的精度预测了它。然而,当前的网络经常占用大量参数并且需要沉重的计算成本。这些缺点阻碍了在功率或内存受限的移动设备上的应用。为了应对这些…

Apache 网页优化与防盗链

目录 一、Apache网页优化概述 二、gzip介绍 Apache的压缩模块 配置网页压缩功能 1.检查是否安装 mod_deflate 模块 2.编译安装 Apache 添加 mod_deflate 模块 3.配置 mod_deflate 模块启用 4.检查安装情况,启动服务 5.测试 mod_deflate 压缩是否生效 三. 网页缓…

ChatGPT 不算新技术革命,带不来什么新机会

有人说:一个人从1岁活到80岁很平凡,但如果从80岁倒着活,那么一半以上的人都可能不凡。 生活没有捷径,我们踩过的坑都成为了生活的经验,这些经验越早知道,你要走的弯路就会越少。

DM8:达梦数据库REDO日志损坏修复

DM8:达梦数据库REDO日志损坏修复环境介绍1 启动数据库报错 CODE-7232 查看原实例(5237)初始化参数3 初始化相同参数的新数据库启停新库4 使用 dmmdf 工具 查看帮助5 查看原库(5237)的 db_magic6 修改新库(5236) REDO 日志的 db_magic 值6.1 新库(5236) DAMENG01.log6.2 新库(52…

浙江美格机械股份有限公司董事长——刘国方

现实工作与理想中的方向大相径庭 怎样从工作经历中汲取养分,磨练自身? 学历不高,自身敲门砖不硬 该如何努力才能弥补自身的学历短板? 想为传统企业焕发新生机 该如何在传统行业中搭建新平台? 观看本期节目和我一起寻找答案&#…

burp插件jsEncrypter使用,爆破密码被自定义算法加密

暴力破解的时候,密码一般都是进行了加密 普通的base64、md5等编码,burp里面都有,如果是自定义的密码算法,我们该如何使用burp进行爆破呢? webapp,关于密码学的一个简单靶场 抓包,被加密了。假…

【WCH】基于Keil环境CH32F203 GPIO点灯实验

【WCH】基于Keil环境CH32F203 GPIO点灯实验📌相关篇《关于CH32F203程序下载方式说明》⚡注意编译器版本不要使用AC6版本。 ✨如果是首次入门使用,请先看上面的相关篇内容,了解其下载相关事宜后,再进来学习。 📑GPIO模式…

15个前端数据大屏展示图+源代码免费下载

https://download.csdn.net/download/wanghongpu9305/87658655?spm1001.2014.3001.5503 https://download.csdn.net/download/wanghongpu9305/87658730?spm1001.2014.3001.5503 https://download.csdn.net/download/wanghongpu9305/87684844?spm1001.2014.3001.5503 http…

windows Linux :python 脚本 下载日本葵花8号卫星数据L1级产品,自定义分辨率、时间

前言介绍 近期需要用到日本葵花8号卫星数据,用于相关研究,而通过官方提供的下载方法,难以针对性的下载所需要的数据类型,因此这里编写了一个针对葵花8号卫星L1级数据产品的脚本下载,主要实现两个功能:1、自…

为docker安装图形界面和配置远程桌面连接

由于远程桌面访问必须要打开端口3389,所以在启动docker中ubuntu系统的时候要首先将linux系统的3389端口映射出来 docker run -tid -p 3389:3389 --name ceshi --privilegedtrue ceshi /bin/bash 接下来进入到ubuntu中 docker exec -it ceshi /bin/bash 首先安装X…

查找 | 顺序查找、分块查找、二分查找、哈希查找 | 牛客刷题笔记

查找 就平均查找速度而言,下列几种查找速度从慢至快的关系是:顺序、分块、折半、哈希。 顺序查找的时间复杂度为o(n) 分块查找的时间复杂度为o(log2n)到o(n)之间 二分查找的时间复杂度为o(log2n) 哈希查找的时间复杂度为o(1) 用概率查找改进查找效率&am…

本周热门chatGPT之AutoGPT-AgentGPT,可以实现完全自主实现任务,附部署使用教程

AutoGPT 是一个实验性的开源应用程序,它由GPT-4驱动,但有别于ChatGPT的是,​ 这与ChatGPT的底层语言模型一致。 ​AutoGPT 的定位是将LLM的"思想"串联起来,自主地实现你设定的任何目标。 简单的说,你只用提出…

界面组件Telerik ASP.NET MVC使用指南 - 如何自定义网格过滤

Telerik UI for ASP. NET MVC拥有使用JavaScript和HTML5构建网站和移动应用所需的70UI部件,来满足开发者的各种需求,提供无语伦比的开发性能和用户体验。它主要是针对专业级的 ASP.NET开发,通过该产品的强大功能,开发者可以开发出…

消息队列面试题-RocketMQ

1.为什么要使用消息队列? 2.消息队列有什么优点和缺点? 3.如何解决重复消费? 幂等性: 概念:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说&#xf…

前端项目-09-购物车-游客列表-变更选中状态-删除

目录 1-加入购物车成功 2-购物车成功页面跳转 3-游客身份获取购物车列表 4-动态渲染购物车列表 5-处理商品数量 6-删除购物车商品 6.1-删除购物车单个商品 6.2-删除全部选中商品 7-修改产品状态 7.1-修改单个产品的状态​编辑 7.2-修改多个产品的状态 1-加入购物车成功…

【2023】分享一份网友的Linux运维面试题

原只有问题,没有答案。答案是我整理的,如发现有什么问题可以在评论区留言告诉我!目录1.grep如何查找一个字符串如何忽略大小写2.kill -9和kill -15有什么区别3.简单描述一下TCP的建立和断开4.Linux服务在什么情况下CPU使用率会超过100%5.服务…