锁机制(Locking):解决数据库“死锁”与“阻塞”的终极指南

📌 今日关键词:​​ 锁机制、死锁、锁等待、并发控制、行锁、表锁

大家好呀!我是​数据库小学妹​👋

我们之前学过事务,知道事务可以把多个操作打包,保证要么全成功要么全失败。但是,你有没有想过:

当两个人同时修改同一条数据------比如双十一抢购最后一件商品,A用户下单库存减1,B用户也同时下单------数据库怎么保证库存不会变成负数?怎么避免两个人的操作互相覆盖?

这就是锁的职责。锁就像数据库里的交通警察,控制着谁可以访问数据、谁需要等待,保证并发操作下数据依然正确。

今天,我们要学习数据库进阶路上的​"交通规则"------锁机制(Locking)​🚦,让你理解数据库背后的"并发控制"原理,写代码时少踩死锁的坑!

一、什么是锁机制?------ 数据库的"红绿灯"

想象一下,如果数据库是一个巨大的图书馆,成千上万的读者(并发请求)同时想借书、还书。如果没有管理员,大家一拥而上,书就会被撕烂或者拿错。

锁机制就是数据库的管理员,它通过给数据加上"锁",来保证在并发环境下,数据的一致性和完整性。

📌核心价值:

  • 防止脏读: 读到了别人还没提交的脏数据。
  • 防止不可重复读: 同一个事务里,两次读取的数据不一致。
  • 防止幻读: 刚读完数据,别人就插了一条新数据进来。

🔍如何查看?

在MySQL中,我们可以通过以下命令查看锁的状态:

SQL 复制代码
-- 查看当前的进程(谁在阻塞谁)
SHOW PROCESSLIST;

-- 查看最近一次死锁的信息(侦探现场)
SHOW ENGINE INNODB STATUS;

二、锁的分类(新手先掌握这些)

✅按粒度分:表锁 vs 行锁

类型 特点 适用引擎 并发性能
表锁 锁定整张表,其他事务不能读写 MyISAM、MEMORY 差
行锁 只锁定某一行,其他行可并发操作 InnoDB(默认) 好

​InnoDB默认使用行锁​,这也是为什么生产环境推荐InnoDB的原因。

✅按模式分:共享锁(S锁)vs 排他锁(X锁)

锁类型 别名 作用 兼容性
共享锁 读锁 允许其他事务同时读,但不能写 多个共享锁可共存
排他锁 写锁 禁止其他事务读和写 不与其他任何锁共存

自动加锁规则:

  • SELECT ... 不加锁(快照读)
  • SELECT ... FOR UPDATE 加行级排他锁
  • SELECT ... LOCK IN SHARE MODE 加行级共享锁
  • UPDATE、DELETE、INSERT 自动加行级排他锁

三、行锁的三种具体形式(InnoDB)

锁类型 锁定范围 触发条件
记录锁 锁定单条记录 WHERE id = 1 且id是唯一索引/主键
间隙锁 锁定一个范围(不包含记录) WHERE id BETWEEN 1 AND 10 且id不是唯一索引
临键锁 锁定一个范围+记录 默认的间隙锁变种(防止幻读)

间隙锁是InnoDB在可重复读隔离级别下防止幻读的关键。它锁定的"不存在"的行,防止其他事务插入到该范围。

四、什么是死锁?怎么避免?

​死锁​:两个或多个事务互相等待对方释放锁,导致谁也进行不下去。

⏳经典死锁 ​场景:

sql 复制代码
-- 事务A
BEGIN;
UPDATE account SET balance = balance - 100 WHERE id = 1;  -- 锁住id=1
UPDATE account SET balance = balance + 100 WHERE id = 2;  -- 等待id=2的锁

-- 事务B
BEGIN;UPDATE account SET balance = balance + 100 WHERE id = 2;  -- 锁住id=2
UPDATE account SET balance = balance - 100 WHERE id = 1;  -- 等待id=1的锁

A等B释放id=2,B等A释放id=1 → 死锁!

​MySQL的处理 ​:检测到死锁后,会选择​回滚其中一个事务 ​(通常是执行代价较小的那个),并返回错误 Deadlock found when trying to get lock。

📚避免死锁的技巧

  1. 按固定顺序访问表/行:比如总是先更新id=1,再更新id=2
  2. 缩短事务:尽快提交,减少锁持有时间
  3. 使用低隔离级别(如读已提交)可减少间隙锁,但可能产生幻读
  4. 合理设计索引 :让UPDATE/DELETE能精确命中行锁,避免升级为表锁
  5. 重试机制:应用程序捕获死锁错误后,自动重试事务

五、实战:查看当前锁和死锁信息

🎯查看当前正在等待的锁

sql 复制代码
-- 查看当前事务和锁(MySQL 8.0)
SELECT * FROM performance_schema.data_locks;

-- 查看正在等待的锁
SELECT * FROM performance_schema.data_lock_waits;

🎯查看最近一次死锁信息

sql 复制代码
SHOW ENGINE INNODB STATUS;

在输出中搜索 LATEST DETECTED DEADLOCK 段落,可以看到造成死锁的SQL。

💡 生产环境定期监控死锁,可以帮助你发现代码中的并发问题。

六、新手必看:3种最常见的"锁升级"陷阱

在使用锁机制时,如果你发现查询变慢了,通常是以下原因导致的"锁升级":

🚩没走索引的更新:

  1. 错误: UPDATE accounts SET balance = 100 WHERE name = 'John'; (name字段没索引)
  2. 后果: InnoDB找不到具体的行,只能锁住整张表!
  3. 修正: 给 name 加索引,或者确保 WHERE 条件走索引。

🚩间隙锁(Gap Lock)的副作用:

  1. 场景: 可重复读(RR)隔离级别下,为了防止幻读,InnoDB会锁住"索引之间的空隙"。
  2. 后果: 你可能锁住了一段不存在的数据范围,导致别人插不进去。
  3. 修正: 如果并发插入很高,可以考虑将隔离级别降为"读已提交(RC)"。

🚩长事务:

  1. 错误: 一个事务开启后,半天不提交(比如在代码里处理复杂的业务逻辑)。
  2. 后果: 锁一直不释放,后面的请求全堵着。
  3. 修正: "开启事务 -> 执行SQL -> 提交事务",这三个动作要像闪电一样快!

七、今日学习心得

  1. 锁是并发访问的守门员,保证数据一致性和完整性
  2. InnoDB行锁是并发友好的,但要注意死锁和间隙锁
  3. 死锁不可怕,关键是要捕获并重试,设计好访问顺序

👋 我是数据库小学妹 一个用设计师思维学数据库的转行人。我们一起,把复杂的技术变得简单有趣!💕

本文示例基于 ​ ​MySQL ​​ 8.0,InnoDB 引擎。不同隔离级别下锁的行为有差异,建议查阅官方文档。

相关推荐
Decay__2 小时前
05_MySQL 常用 SQL 大全 —— 诊断、管理、优化辅助 SQL 与三大系统库速查
运维·数据库·sql·mysql·架构
raindayinrain5 小时前
c++高性能--原子操作,无锁编程,操作顺序
开发语言·c++·性能优化
小徐不徐说7 小时前
C++ 接口与插件设计:从工厂注册到 Qt 动态插件
数据库·c++·qt·系统架构·程序设计
论文复现现场7 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
Decay__8 小时前
04_MySQL DBA 日常运维手册 —— 会话管理、锁与阻塞、事务监控、慢查询与日志巡检
运维·数据库·sql·mysql·架构
海绵宝宝的月光宝盒8 小时前
14-AutoCAD-阵列命令应用与案例实战(AR)
经验分享·笔记·自动化·课程设计·制造·学习方法·cad
心梦缘-雪雁9 小时前
SQL Server 查询数据存在哪个表里
数据库
lusklusklusk10 小时前
Python 巡检脚本:进程运行状态、数据库写入时效检查及邮件通知与告警
数据库·python·oracle
GeW11 小时前
产业新优势基础软件的人才底盘,从Linux与数据库开始
linux·数据库
SelectDB11 小时前
Doris Arrow Flight SQL 高速读取协议实战:与 Snowflake 客户端取数链路对照
大数据·数据库·数据分析