MySQL锁概述
数据库锁是一种机制,用于管理并发访问数据库的方式。当多个用户或事务同时访问数据库时,可能会导致数据不一致或冲突的问题。数据库锁的作用是确保数据的一致性和完整性,同时允许多个用户并发地访问数据库。
需要注意的是,加锁是消耗资源的,锁的各种操作,包括获取锁、校验锁是否已解除、释放锁等,都会增加系统的开销。
这里以MySQL为例,介绍下数据库锁,关于数据库锁的理论知识可以参考数据库锁概述一文。
锁的分类
按照不同的划分标准,可以将锁分为多种类型。如悲观锁和乐观锁、共享锁和排他锁、意向锁、表锁和行锁和页锁等。
悲观锁和乐观锁
根据加锁的时机,可以将数据库锁可以分为两类:悲观锁和乐观锁。
所谓悲观锁,就是对数据的修改抱有悲观态度。当要对一个数据库中的一条数据进行修改时,为了避免同时被其他人修改,直接对该数据进行加锁以防止并发。这种借助数据库锁机制在修改数据之前先锁定,再修改的方式被称之为悲观并发控制(又名"悲观锁",Pessimistic Concurrency Control,简称"PCC")。
悲观锁采用的是"先取锁再访问"的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,还会降低并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。
所谓乐观锁,是相对悲观锁而言的,对数据的修改持有乐观的态度。当要对一个数据库中的一条数据进行修改时,假设数据一般不会造成冲突,只在数据进行提交的时候,对数据进行冲突检测,如果没有冲突,则直接修改,如果存在冲突则提供补偿策略,如执行回滚操作或执行重试操作。这次推迟并发冲突校验的方式被称为乐观并发控制(又名"乐观锁",Optimistic Concurrency Control,简称"OCC")。
乐观锁采用的是"推迟冲突校验"的积极策略,在数据争用不大、冲突较少的场景下中,可以获得更高的吞吐量,且不会引入锁,更不会带来死锁的问题。但是,对于数据争用较大,冲突较多的场景,乐观锁则会因补偿策略,带来回滚压力或CPU消耗。
共享锁和排他锁
根据是否独占数据还是共享数据,可以将数据库的锁分为共享锁和排他锁。
共享锁(shared lock,记为S),也称读锁,如果事务 T T T获得了数据项Q上的共享型锁,则 T T T对数据项Q可读但不可写,其他事务也可对数据项Q加共享锁,但不能加排他锁。
排他锁(exclusive lock,记为X),也称写锁,如果事务 T T T获得了数据项Q上的排他型锁,则 T T T对数据项Q即可写又可读,其他事务对数据项Q不能加任何锁。
共享锁和排他锁的兼容关系如下:
锁的类型 | 共享锁(S) | 排他锁(X) |
---|---|---|
共享锁(S) | 兼容 | 冲突 |
排他锁(X) | 冲突 | 冲突 |
从上面的表格可知,共享锁和共享锁是兼容的,也就是说,如果事务 T T T对数据加了共享锁,那么其他事务是可以继续加共享锁的。但是,如果事务 T T T对数据加了排他锁,那么其他事务是不可加任何锁的。
从数据隔离性的角度来说,共享锁主要是为了支持并发的读取数据而出现的,读取数据时,不允许其他事务对当前数据进行修改操作,从而有效避免"不可重复读"的问题。排他锁主要是为了解决在修改数据时,不允许其他事务对当前数据进行修改和读取操作,从而可以有效避免"脏读"的问题。
意向锁
意向锁是一种表锁,用来表示一个事务接下来要获取的行锁类型(共享锁还是排他锁)。MySQL官方文档给出的描述如下:
Intention locks are table-level locks that indicate which type of lock (shared or exclusive)
a transaction requires later for a row in a table.
当一个事务想要获取某行的锁时,它必须先获取该行所在表的意向锁。根据意图获取的行锁类型,可以将意向锁细分为意向共享锁(Intention Shared lock,IS)和意向排他锁(Intention eXclusive lock,IX)。意向共享锁表示该事务要获取的行锁类型是共享锁,而意向排他锁则表示该事务要获取的行锁类型是排他锁。
意向锁主要用于表锁和行锁共存的场景。举例来说,如果一个事务对表中的某一行加行锁,那么当另一个事务想对表加锁时,如果没有意向锁,则需要遍历该表的所有行,确保不存在锁冲突。如果有意向锁,如果可以兼容,则另一个事务进一步加锁,否则,直接返回,提示锁冲突。意向锁的兼容关系如下:
锁的类型 | 共享锁(S) | 排他锁(X) | 意向共享锁(IS) | 意向排他锁(IX) |
---|---|---|---|---|
共享锁(S) | 兼容 | 冲突 | 兼容 | 冲突 |
排他锁(X) | 冲突 | 冲突 | 冲突 | 冲突 |
意向共享(IS) | 兼容 | 冲突 | 兼容 | 兼容 |
意向排他锁(IX) | 冲突 | 冲突 | 兼容 | 兼容 |
从上面的表格可知,意向锁之间是兼容的。也就是说,对同一个表,是可以随便加意向锁的。但是,对于意向共享锁来说,和共享锁是兼容的,和排他锁是冲突的。这主要是因为意向共享锁目的是给特定的行添加共享锁,而共享锁与排他锁是冲突的。同样的,对于意向排他锁来说,对共享锁或排他锁都是冲突的,这主要是因为意向排他锁目的是给特定的行添加排他锁,而排他锁与共享锁或排他锁是冲突的。
需要说明的是,对InnoDB来说,意向锁是由InnoDB执行引擎自己维护的,用户无法手动操作意向锁。当向数据行加共享锁/排他锁之前,InooDB 会自动先获取该数据行所在数据表的对应意向锁。
此外,除了存在意向共享锁和意向排他锁,还有共享排他型意向锁(shared and intention-exclusive, SIX),见于多粒度封锁协议中。有兴趣的同学可以自行学习。
表锁和行锁和页锁
根据加锁的粒度,可以将数据库的锁细分为表锁、行锁、页锁。
表锁(Table Lock)是一种粗粒度的锁,它锁定整个表,阻止其他事务访问表中的任何行。表锁适用于需要对整个表进行操作的情况,表锁开销小,且加锁快,但因锁的粒度较大,发生锁冲突的概率较高,数据库的并发性能较低。
行锁(Row Lock)是一种细粒度的锁,它锁定指定的行。行锁开销大,且加锁慢,但因其加锁粒度较小,发生锁冲突的概率较低,数据库的并发性能较高。
页锁(Page Lock)是介于行锁和表锁之间的一种锁机制,它锁定表的一个页或多个页。页锁通常在特定情况下使用,例如当需要批量操作数据时。页锁的开销和加锁速度也介于行锁和表锁之间,数据库的并发性能一般。注意,页级锁是 MySQL 中比较独特的一种锁定级别,在其他数据库中并不常见。页级锁主要应用于 BDB(BerkeleyDB )存储引擎中,这里不过多介绍,有兴趣的同学还请自行学习。
此外,无论是表锁,还是行锁,都可以根据是否独占数据还是共享数据,进一步细分为共享锁和排他锁。
锁的使用
对不同的存储引擎,锁的支持情况和使用方式是不同的。这里重点介绍下MyISAM和InnoDB两种存储引擎的锁的使用,其他引擎的锁的使用还请自行学习。
表锁的使用
MyISAM 只支持表级锁,不支持行锁。在执行 select,update,delete,insert 等语句都会给表自动加锁(针对读操作加共享锁、针对写操作加排他锁)。
InnoDB默认采用行锁,在未使用索引字段查询时升级为表锁。需要说明的是,即便在条件中使用了索引字段,MySQL会根据自身的执行计划,考虑是否使用索引。如果MySQL认为全表扫描效率更高,即使指定了索引字段,也不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,有必要进一步检查SQL的执行计划,以确认是否真正使用了索引。注意,以下场景下,会优先使用表锁:
(1) 全表更新。事务需要更新大部分或全部数据,且表又比较大。若使用行锁,会导致事务执行效率低,从而可能造成其他事务长时间锁等待和更多的锁冲突。
(2) 多表查询。事务涉及多个表,比较复杂的关联查询,很可能引起死锁,造成大量事务回滚。这种情况若能一次性锁定事务涉及的表,从而可以避免死锁、减少数据库因事务回滚带来的开销。
行锁的使用
InnoDB为实现行锁,提供了多种实现方式。如记录锁(Record Lock)、间隙锁(Gap Lock)、临键锁(Next-Key Lock)等。需要说明的是,行锁仅在使用索引字段时才会生效,否则会升级为表锁。
记录锁(Record Lock)
记录锁是封锁记录,记录锁也叫行锁,示例如下:
SELECT * FROM `test_table` WHERE `id` = 1 FOR UPDATE;
它会在 id=1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1 这一行。
间隙锁(Gap Lock)
间隙锁,锁定一个范围,但不包含记录本身,间隙锁是封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。间隙锁主要目的,也是为了避免出现幻读(Phantom Read),对应的事务的隔离级别降级为可重复读。如果将事务的隔离性级别调整成读已提交或读未提交,间隙锁都会失效。间隙锁对应的事务隔离级别是可重复。示例如下:
SELECT * FROM `test_table` WHERE `id` > 100 FOR UPDATE
更多间隙锁的使用示例可以参考MySQL锁 —— 记录锁、间隙锁、临键锁、自增锁 一文。
以下情况均会产生间隙锁:
(1) 使用普通索引,且需要锁定一个区间。
(2) 使用多列唯一索引,且需要锁定一个区间。
(3) 使用唯一索引,且需要锁定一个区间。
注意,间隙锁封锁的不是多条记录,而是一个区间,以防止出现幻读现象。
临键锁(Next-Key Lock)
临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。同间隙锁一样,临键锁也是为了避免出现幻读(Phantom Read)。同样的,临键锁对应的事务隔离级别是可重复。示例如下:
SELECT * FROM table WHERE age <= 24 FOR UPDATE;
更多临键锁的使用示例可以参考MySQL锁 —— 记录锁、间隙锁、临键锁、自增锁 一文。
临键锁会锁住一段左开右闭区间的数据。需要强调的一点是,InnoDB 中临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。
锁优化
MySQL锁机制是确保数据库数据一致性和完整性的关键技术,同时也是提高数据库性能的挑战之一。了解不同类型的锁以及它们的适用场景是设计高效数据库应用程序的重要一步。
在使用锁时,需要根据应用程序的需求选择合适的锁类型,同时考虑事务隔离级别以及优化策略,以提高并发性能并避免常见的锁问题。
为了优化MySQL锁性能,可以考虑以下几个策略:
(1) 选择合适的隔离级别
(2) 减小事务的规模
(3) 尽量在SQL中使用索引
(4) 避免长时间的锁定
(5) 定期清理无效的锁
参考
数据库系统概念 Abraham Silberschatz, Henry F. Korth, S. Sudarshan 著
https://www.cnblogs.com/qlqwjy/p/7798266.html 乐观锁和悲观锁的区别
https://www.cnblogs.com/kexinxin/p/11620345.html 数据库中的锁
https://cloud.tencent.com/developer/article/2332879 MySQL 锁的完全解析:提高数据库性能的关键技术
https://learnku.com/articles/39212 一张图彻底搞懂 MySQL 的锁机制
https://www.zhihu.com/tardis/zm/art/52312376?source_id=1005 数据库基础(三)Mysql里的锁
https://zhuanlan.zhihu.com/p/185003485 详解 MySql InnoDB 中意向锁的作用
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-intention-locks InnoDB Locking
https://blog.csdn.net/zcl_love_wx/article/details/82015281 一分钟深入Mysql的意向锁——《深究Mysql锁》
https://zhuanlan.zhihu.com/p/52879825 浅谈数据库共享锁与排它锁
https://juejin.cn/post/7088256473574309919 MySQL表锁、行锁和页锁
https://www.mysqlzh.com/doc/218.html BDB (BerkeleyDB)存储引擎
https://blog.csdn.net/kuyuyingzi/article/details/87710259 MySQL 表锁和行锁机制(很详细)
https://blog.csdn.net/qq_16268979/article/details/103441178 MyISAM的表锁和InnoDB的行锁
https://www.jianshu.com/p/478bc84a7721 MySQL记录锁、间隙锁、临键锁(Next-Key Locks)详解
https://blog.csdn.net/zhangchaoyang/article/details/108809249 MySQL锁 —— 记录锁、间隙锁、临键锁、自增锁
相关文章:
MySQL锁概述
数据库锁是一种机制,用于管理并发访问数据库的方式。当多个用户或事务同时访问数据库时,可能会导致数据不一致或冲突的问题。数据库锁的作用是确保数据的一致性和完整性,同时允许多个用户并发地访问数据库。 需要注意的是,加锁是消…...

【Ceph Block Device】块设备挂载使用
文章目录 前言创建pool创建user创建image列出image检索image信息调整image大小增加image大小减少image大小 删除image从pool中删除image从pool中“延迟删除”image从pool中移除“延迟删除的image” 恢复image恢复指定pool中延迟删除的image恢复并重命名image 映射块设备格式化i…...

Arbitrum Stylus 的工作原理
理解 Arbitrum 如何协调 EVM 和 WASM 的共存是至关重要的。这不仅仅是拥有两个独立的引擎;这是一种增强两者优势的协同关系。 Arbitrum 的独特架构允许 EVM 和 WASM 之间进行无缝和同步的操作,这要归功于其统一的状态、跨 VM 调用和兼容的经济模型。 用…...

nextjs构建服务端渲染,同时使用Material UI进行项目配置
一、创建一个next项目 使用create-next-app来启动一个新的Next.js应用,它会自动为你设置好一切 运行命令: npx create-next-applatest 执行结果如下: 启动项目: pnpm dev 执行结果: 启动成功! 二、安装Mater…...
Java 使用 Easyexcel 导出大量数据
读Excel | Easy Excel 1、 我遇到的数据量超级大,使用传统的POI方式来完成导入导出很明显会内存溢出,并且效率会非常低;2、 数据量大直接使用select * from tableName肯定不行,一下子查出来300w条数据肯定会很慢;3、 …...

高效防汛决策:山海鲸可视化系统助力城市防洪
随着全球气候的变化,自然灾害如洪水、台风等频发,防范洪水成为城市管理者和居民们亟待解决的重要问题。 洪水的威胁 洪水是自然界的杀手之一,不仅会造成大量的财产损失,还可能危害人们的生命安全。因此,预测、监测和有…...

易点云CFO向征:CFO不能只讲故事,价值创造才是核心
作者 | 曾响铃 文 | 响铃说 在今年6月初,也是易点云上市6天后,《巴伦周刊》正式启动评价“2023港美上市中国企业CFO精英100”的活动。 时间来到9月,评价揭秘,易点云CFO向征成功入选,被评为“年度最具成长潜力CFO”…...

【计算机网络】poll | epoll
文章目录 1. pollpoll函数参数解析代码解析PollServer代码 poll 特点 2. epoll认识接口epoll_createepoll_ctlepoll_wait 基本原理红黑树就绪队列 1. poll poll函数参数解析 输入 man poll poll的第一个参数是文件描述符 poll的第二个参数为 等待的多个文件描述符(fd)数字层面…...

C++设计模式_07_Bridge 桥模式
文章目录 1. 动机(Motivation)2. 代码演示Bridge 桥模式2.1 基于继承的常规思维处理2.2 基于组合关系的重构优化2.3 采用Bridge 桥模式的实现 3. 模式定义4. 结构(Structure)5. 要点总结 与上篇介绍的Decorator 装饰模式一样&…...
[JAVA版本] Websocket获取B站直播弹幕——基于直播开放平台
教程 B站直播间弹幕Websocket获取 — 哔哩哔哩直播开放平台 基于B站直播开放平台开放且未上架时,只能个人使用。 代码实现 1、相关依赖 fastjson2用于解析JSON字符串,可自行替换成别的框架。 hutool-core用于解压zip数据,可自行替换成别的…...

第一个 Python 程序
三、第一个 Python 程序 好了,说了那么多,现在我们可以来写一下第一个 Python 程序了。 一开始写 Python 程序,个人不太建议用专门的工具来写,不方便熟悉语法,所以这里我先用 Sublime Text 来写,后期可以…...

广告牌安全监测,保障户外广告牌的安全与稳定
随着城市的发展和现代化,广告牌已经成为城市风景的一部分。然而,随之而来的是广告牌安全问题,因为它们暴露在各种天气和环境条件下,一旦掉落,可能对人们的生命和财产造成威胁。广告牌安全监测有效的解决了这一问题&…...

分类预测 | MATLAB实现KOA-CNN-GRU开普勒算法优化卷积门控循环单元数据分类预测
分类预测 | MATLAB实现KOA-CNN-GRU开普勒算法优化卷积门控循环单元数据分类预测 目录 分类预测 | MATLAB实现KOA-CNN-GRU开普勒算法优化卷积门控循环单元数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现KOA-CNN-GRU开普勒算法优化卷积门控循环单…...

进来了解实现官网搜索引擎的三种方法
做网站的目的是对自己的品牌进行推广,让越来越多的人知道自己的产品,但是如果只是做了一个网站放着,然后等着生意找上门来那是不可能的。在当今数字时代,实现官网搜索引擎对于提升用户体验和推动整体性能至关重要。搜索引擎可以帮…...

OpenCV3-Python(7)模板匹配和霍夫检测
模板匹配 膜版匹配不能匹配尺度变换和视角变换的图像 图片中查找和模板相似度最高的图像 计算相似程度最高的位置 res cv.matchTemplate(img , template, method) 该方法返回一个类似灰度图的东西,如果用的相关匹配,那么亮的地方就是可能匹配上的地方 …...

[C++11]花括号{}、initializer_list、auto、decltype
文章目录 1.花括号{ }的扩展2.initializer_list3.auto4.decltype5.容器的增加5.1array[useless]5.2forward_list[useless]5.3unordered_map/unordered_set5.4统一增加 6.知乎文章 1.花括号{ }的扩展 int main() {//C98花括号{ }支持 1.数组 2.结构体struct Point{int _x;int _…...

在Android平板上使用code-server公网远程Ubuntu服务器编程
文章目录 1.ubuntu本地安装code-server2. 安装cpolar内网穿透3. 创建隧道映射本地端口4. 安卓平板测试访问5.固定域名公网地址6.结语 1.ubuntu本地安装code-server 准备一台虚拟机,Ubuntu或者centos都可以,这里以VMwhere ubuntu系统为例 下载code server服务,浏览器…...
宝塔上安装mysql版本比较和区别
在宝塔上安装MySQL时,几个主要版本之间的区别主要在于性能、功能和安全性。 MySQL Community Server(社区版本):这是MySQL的开源免费版本,由社区支持,但不提供官方技术支持。这个版本对一般用户来说可能有…...

uniapp微信小程序自定义封装分段器。
uniapp微信小程序自定义封装分段器。 话不多说先上效果 这里我用的是cil框架 vue3 下面贴代码 组价代码: <template><view class"page"><viewv-for"(item, index) in navList":key"index"click"changeNav(ind…...

文心一言 VS 讯飞星火 VS chatgpt (111)-- 算法导论10.2 2题
二、用go语言,用一个单链表 L实现一个栈。要求操作 PUSH和 POP 的运行时间仍为 O(1)。 文心一言: 在 Go 语言中,我们可以用结构体来实现一个单链表,然后再用栈的相关操作。 以下是一个可能的实现方式: package mai…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...