分布式微服务系统架构第150集:JavaPlus技术文档平台日更

加群联系作者vx:xiaoda0423

仓库地址:https://webvueblog.github.io/JavaPlusDoc/

https://1024bat.cn/

https://github.com/webVueBlog/fastapi_plus

https://webvueblog.github.io/JavaPlusDoc/

MySQL锁机制详解

MySQL 中的锁是数据库并发控制的基本机制,用于管理对共享资源的并发访问,确保数据的一致性和完整性。本文将详细介绍 MySQL 中的各种锁类型、工作原理、使用场景以及最佳实践。

锁的基本概念

锁是一种并发控制机制,用于协调多个事务对共享资源的访问。在数据库系统中,锁的主要目的是保证数据的一致性和完整性,防止并发操作导致的数据异常。

锁的基本特性包括:

  • 互斥性:某些锁(如排他锁)一旦被获取,其他事务就无法再获取相同的锁

  • 共享性:某些锁(如共享锁)允许多个事务同时持有

  • 粒度:锁可以作用于不同级别的数据库对象(如行、页、表)

  • 持续时间:锁的持有时间可以是短暂的(如语句级),也可以是长期的(如事务级)

按操作类型分类的锁

读锁(共享锁/S锁)

读锁,也称为共享锁(Shared Lock)或 S 锁,是一种允许多个事务同时读取同一资源,但阻止其他事务获取写锁的锁类型。

特点

  • 允许多个事务同时获取同一资源的读锁

  • 持有读锁的事务只能读取资源,不能修改

  • 如果一个资源已经被加了读锁,其他事务可以继续加读锁,但不能加写锁

使用场景

  • 查询操作,如 SELECT 语句

  • 需要保证读取数据一致性的场景

语法

go 复制代码
-- 在 SELECT 语句中显式添加共享锁
SELECT * FROM table_name WHERE condition LOCK IN SHARE MODE;

-- MySQL 8.0 新语法
SELECT * FROM table_name WHERE condition FOR SHARE;

写锁(排它锁/独占锁/X锁)

写锁,也称为排它锁(Exclusive Lock)、独占锁或 X 锁,是一种在事务修改数据时使用的锁,它排斥任何其他锁。

特点

  • 一个资源只能被一个事务加写锁

  • 持有写锁的事务可以读取和修改资源

  • 如果一个资源已经被加了写锁,其他事务不能加读锁或写锁

  • 如果一个资源已经被加了读锁,事务不能加写锁

使用场景

  • 数据修改操作,如 INSERT、UPDATE、DELETE 语句

  • 需要保证数据修改原子性的场景

语法

go 复制代码
-- 在 SELECT 语句中显式添加排它锁
SELECT * FROM table_name WHERE condition FOR UPDATE;

-- 修改语句会自动加排它锁
UPDATE table_name SET column = value WHERE condition;
DELETE FROM table_name WHERE condition;

按锁粒度分类

表锁(Table Lock)

表锁是 MySQL 中粒度最大的锁,它锁定整个表。

特点

  • 开销小,加锁快,不会出现死锁

  • 锁定粒度大,发生锁冲突的概率高,并发度低

  • 对整表进行写操作会阻塞其他事务对该表的所有读写操作

使用场景

  • 对整表数据进行修改的场景

  • 表数据量较小,并发访问不多的场景

  • MyISAM 存储引擎(只支持表级锁)

语法

go 复制代码
-- 手动加表级读锁
LOCK TABLES table_name READ;

-- 手动加表级写锁
LOCK TABLES table_name WRITE;

-- 释放所有表锁
UNLOCK TABLES;

页锁(Page Lock)

页锁是介于表锁和行锁之间的一种锁,它锁定数据库中的一个页(通常是 16KB)。

特点

  • 锁定粒度介于表锁和行锁之间

  • 加锁开销和加锁时间介于表锁和行锁之间

  • 会出现死锁

  • 锁定粒度较大,有可能在锁定一行数据时,实际锁定了多行数据(因为它们在同一页中)

使用场景

  • BDB 存储引擎(MySQL 5.1 后不再支持)

行锁(Row Lock)

行锁是 MySQL 中粒度最小的锁,它只锁定表中的某一行。

特点

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

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

  • 只有在访问行级数据时才会加锁

使用场景

  • 高并发系统

  • InnoDB 存储引擎(支持行级锁)

注意:InnoDB 的行锁是通过索引实现的,如果查询条件没有使用索引,InnoDB 会使用表锁。

特殊类型的锁

意向锁(Intention Lock)

意向锁是 InnoDB 存储引擎中的一种表级锁,用于指示事务稍后要对表中的行加什么类型的锁(共享锁或排它锁)。

类型

  • 意向共享锁(IS锁):表示事务打算给表中的某些行加共享锁

  • 意向排它锁(IX锁):表示事务打算给表中的某些行加排它锁

特点

  • 意向锁是表级锁,不会与行级的共享/排它锁冲突

  • 意向锁之间不会互斥(IS与IS、IS与IX、IX与IX都可以共存)

  • 意向锁可以与表级共享锁/排它锁互斥(IX与S、IX与X、IS与X互斥,但IS与S兼容)

作用

  • 提高加锁效率,避免在给行加锁前,需要检查表中每一行是否已经被锁定

  • 实现多粒度锁定,允许行锁和表锁共存

间隙锁(Gap Lock)

间隙锁是 InnoDB 在 REPEATABLE READ 隔离级别下,为了防止幻读而引入的一种锁机制,它锁定索引记录之间的间隙。

特点

  • 锁定索引记录之间的间隙,防止其他事务在间隙中插入数据

  • 只在 REPEATABLE READ 隔离级别下生效

  • 可能导致死锁和阻塞

使用场景

  • 防止幻读

  • 保证事务隔离性

临键锁(Next-Key Lock)

临键锁是 InnoDB 的默认行锁算法,它是记录锁(行锁)和间隙锁的组合,锁定一个索引记录及其之前的间隙。

特点

  • 锁定索引记录本身和索引记录之前的间隙

  • 是 InnoDB 在 REPEATABLE READ 隔离级别下使用的默认锁

  • 可以防止幻读

插入意向锁(Insert Intention Lock)

插入意向锁是一种特殊的间隙锁,表示插入的意图,当多个事务插入同一个索引间隙的不同位置时,不需要等待其他事务完成。

特点

  • 是一种特殊的间隙锁

  • 多个事务可以同时获取同一个间隙的插入意向锁,只要它们插入的位置不冲突

  • 如果间隙已经被加了间隙锁,插入意向锁会被阻塞

使用场景

  • 提高并发插入性能

自增锁(Auto-increment Lock)

自增锁是一种特殊的表级锁,用于处理 AUTO_INCREMENT 列的值生成。

特点

  • 在插入操作中,InnoDB 会获取自增锁,确保生成的自增值是连续的

  • 自增锁是一种轻量级锁,在插入语句结束后立即释放

配置参数

  • innodb_autoinc_lock_mode:控制自增锁的行为

    • 0:传统模式,语句级锁定

    • 1:连续模式,批量插入使用表锁,单行插入使用轻量级锁

    • 2:交错模式,所有插入都使用轻量级锁,但自增值可能不连续

元数据锁(MDL锁)

元数据锁(Metadata Lock)用于保护数据库对象的元数据,防止在使用对象时被其他会话修改对象结构。

特点

  • 当对表执行 CRUD 操作时,会自动加 MDL 读锁

  • 当对表结构进行变更时,会自动加 MDL 写锁

  • MDL 锁在事务提交后才会释放

使用场景

  • 防止在查询过程中表结构被修改

  • 防止在修改表结构时表被查询或修改

记录锁(Record Lock)

记录锁是最简单的行锁,它锁定索引记录本身。

特点

  • 锁定单个索引记录

  • 防止其他事务修改或删除该记录

使用场景

  • 更新或删除单条记录

按实现机制分类

悲观锁

悲观锁是一种假设会发生并发冲突的锁机制,它在操作数据前先获取锁,确保在操作过程中数据不会被其他事务修改。

特点

  • 先获取锁,再操作数据

  • 适用于写多读少的场景

  • 实现简单,但并发性能较低

实现方式

  • 使用 SELECT ... FOR UPDATE 语句

  • 使用 LOCK IN SHARE MODE 语句

示例

go 复制代码
-- 使用悲观锁更新数据
BEGIN;
-- 先锁定要更新的行
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 执行更新操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

乐观锁

乐观锁是一种假设不会发生并发冲突的锁机制,它在操作数据时不加锁,而是在提交更新时检查数据是否被其他事务修改过。

特点

  • 不加锁,只在提交时检查冲突

  • 适用于读多写少的场景

  • 并发性能高,但实现复杂

实现方式

  • 使用版本号

  • 使用时间戳

  • 使用条件更新

示例

go 复制代码
-- 使用版本号实现乐观锁
-- 1. 查询当前数据和版本号
SELECT balance, versionFROM accounts WHEREid = 1;

-- 2. 在应用层计算新的余额

-- 3. 更新数据,同时检查版本号是否变化
UPDATE accounts SET balance = new_balance, version = version + 1
WHEREid = 1ANDversion = old_version;

-- 4. 检查影响的行数,如果为0表示更新失败(版本已变化)

其他特殊锁类型

全局锁(Global Lock)

全局锁是 MySQL 中粒度最大的锁,它对整个数据库实例加锁,使整个数据库处于只读状态。

使用场景

  • 全库备份

语法

go 复制代码
-- 加全局锁
FLUSH TABLES WITH READ LOCK;

-- 释放全局锁
UNLOCK TABLES;

读锁(RL锁)

读锁(Read Lock)是表锁的一种,允许其他事务读取表,但不允许写入。

写锁(WL锁)

写锁(Write Lock)是表锁的一种,不允许其他事务读取或写入表。

锁的监控和诊断

查看锁信息

go 复制代码
-- 查看当前锁等待情况
SHOWENGINEINNODBSTATUS\G

-- 查看锁等待详情(MySQL 5.7+)
SELECT * FROM performance_schema.data_locks;
SELECT * FROM performance_schema.data_lock_waits;

-- 查看锁等待详情(MySQL 5.7之前)
SELECT * FROM information_schema.innodb_locks;
SELECT * FROM information_schema.innodb_lock_waits;

-- 查看当前正在执行的事务
SELECT * FROM information_schema.innodb_trx;

死锁检测和处理

InnoDB 存储引擎有内置的死锁检测机制,当检测到死锁时,会自动回滚一个事务来解除死锁。

死锁日志查看

go 复制代码
SHOW ENGINE INNODB STATUS\G

死锁预防措施

  1. 按照固定的顺序访问表和行

  2. 尽量使用主键或唯一索引进行更新操作

  3. 避免长事务

  4. 使用合理的隔离级别

  5. 及时提交或回滚事务

锁的最佳实践

  1. 选择合适的锁粒度:根据业务需求选择合适的锁粒度,一般情况下,行锁的并发性能优于表锁

  2. 使用合适的隔离级别:不同的隔离级别使用不同的锁机制,根据业务需求选择合适的隔离级别

  3. 避免长事务:长事务会长时间持有锁,降低系统并发性能

  4. 合理设计索引:InnoDB 的行锁是通过索引实现的,合理设计索引可以提高锁的效率

  5. 避免锁升级:尽量避免锁升级(如从行锁升级到表锁),可以通过使用索引、拆分大事务等方式实现

  6. 使用乐观锁代替悲观锁:在读多写少的场景下,使用乐观锁可以提高并发性能

  7. 定期监控锁状态:定期监控数据库的锁状态,及时发现和解决锁问题

总结

MySQL 的锁机制是保证数据一致性和完整性的重要手段,了解不同类型的锁及其使用场景,可以帮助我们设计出高性能、高可靠性的数据库应用。在实际应用中,需要根据业务需求和系统特点,选择合适的锁类型和锁策略,平衡数据一致性和系统性能。

锁是数据库并发控制的基础,但过度使用锁会导致系统性能下降,因此需要在保证数据一致性的前提下,尽量减少锁的使用,提高系统的并发性能。

相关推荐
sg_knight30 分钟前
Spring Cloud LoadBalancer深度解析:官方负载均衡方案迁移指南与避坑实践
java·spring boot·spring·spring cloud·微服务·负载均衡
Edingbrugh.南空2 小时前
Kafka 3.0零拷贝技术全链路源码深度剖析:从发送端到日志存储的极致优化
分布式·kafka
楼台的春风4 小时前
【Linux驱动开发 ---- 2.1_深入理解 Linux 内核架构】
linux·c++·人工智能·驱动开发·嵌入式硬件·ubuntu·架构
Edingbrugh.南空5 小时前
Kafka分区机制深度解析:架构原理、负载均衡与性能优化
架构·kafka·负载均衡
技术管理修行6 小时前
【二】主流架构模式深度对比:单体、前后端分离与微服务
微服务·云原生·架构·服务发现·前后端分离·单体架构
喝拿铁写前端6 小时前
前端批量校验还能这么写?函数式校验器组合太香了!
前端·javascript·架构
miaoikxm6 小时前
本地windows搭建kafka
windows·分布式·kafka
安科瑞王可6 小时前
“430”与“531”政策节点后分布式光伏并网技术挑战及智慧调度策略
分布式·虚拟电厂·光伏·智慧能源·自发自用
Edingbrugh.南空7 小时前
Kafka数据写入流程源码深度剖析(Broker篇)
分布式·kafka