核心内容摘要
极速解锁感官密电:关于《小伸入》的高阶观影指南与审美解析
予枫个人主页 个人专栏: 《Java 从入门到起飞》《读研码农的干货日常》 Debug 这个世界Return 更好的自己引言线上系统突然报出死锁异常业务数据更新卡住排查半天却连锁的类型都分不清行锁、表锁、间隙锁到底有啥区别S锁和X锁的竞争又是如何引发死锁的作为后端开发者数据库锁机制是绕不开的核心知识点更是保障系统数据一致性和并发性能的关键。
本文将从基础锁类型到死锁排查层层拆解MySQL锁机制带你吃透每个核心要点轻松应对线上锁相关问题文章目录引言
核心锁类型基础S锁与X锁
1 共享锁S锁读锁不互斥
2 排他锁X锁写锁全互斥
粒度区分表锁与行锁
1 表锁粗粒度锁高效低并发核心特性适用场景
2 行锁细粒度锁低效高并发核心特性关键注意点
间隙锁与Next-Key Lock解决幻读的核心
1 间隙锁Gap Lock锁定区间阻止插入
2 Next-Key Lock行锁间隙锁的组合
死锁Deadlock成因、场景与排查
1 死锁产生的3大核心原因
2 典型死锁场景示例事务A事务B
3 死锁排查的4个核心步骤步骤1开启死锁日志永久生效需修改配置文件步骤2实时查看锁状态步骤3分析死锁日志核心信息步骤4优化并规避死锁
全文
总结
核心锁类型基础S锁与X锁数据库锁的核心目的是解决并发场景下的数据一致性问题而Shared Locks共享锁简称S锁和Exclusive Locks排他锁简称X锁是所有锁机制的基础几乎所有数据库都实现了这两种核心锁类型。
1 共享锁S锁读锁不互斥共享锁的核心特质多个事务可以同时持有同一资源的S锁互不干扰但会阻塞X锁的获取。
适用场景只读操作比如SELECT * FROM table WHERE id1 LOCK IN SHARE MODE;MySQL中显式加S锁核心规则事务A获取资源R的S锁后其他事务可正常获取R的S锁读-读兼容事务A获取资源R的S锁后其他事务请求R的X锁会被阻塞直至A释放S锁读-写互斥
2 排他锁X锁写锁全互斥排他锁的核心特质同一资源同一时间只能被一个事务持有X锁既阻塞其他X锁也阻塞S锁。
适用场景写操作插入、更新、删除MySQL中默认对写操作加X锁比如UPDATE table SET nametest WHERE id1;核心规则事务A获取资源R的X锁后其他事务请求R的S锁或X锁都会被阻塞写-读、写-写均互斥事务A释放X锁后阻塞的事务才会按优先级依次获取锁资源 小提示这里建议大家点赞收藏后续排查锁问题时先明确是读锁还是写锁的竞争能快速缩小排查范围
粒度区分表锁与行锁除了S锁和X锁的类型区分按锁定资源的粒度MySQL锁可分为表锁Table Lock和行锁Record Lock两者的锁定范围和适用场景差异极大。
1 表锁粗粒度锁高效低并发表锁是锁定整个数据表的锁机制是MySQL中最基础、开销最小的锁类型MyISAM存储引擎默认支持表锁InnoDB也支持但不常用。
核心特性锁定范围整个数据表无论操作涉及多少行数据加锁方式-- 显式加表级S锁LOCKTABLEStable_nameREAD;-- 显式加表级X锁LOCKTABLEStable_nameWRITE;-- 释放表锁UNLOCKTABLES;优缺点✅ 优点加锁/解锁速度快开销小不会产生死锁❌ 缺点锁定粒度大并发性能差比如事务A更新表中1行数据事务B更新同一表中另一行数据会被阻塞适用场景读多写少的场景如日志查询表批量操作场景如批量导入数据加表锁避免频繁行锁竞争
2 行锁细粒度锁低效高并发行锁Record Lock是锁定数据表中某一行或多行数据的锁机制仅InnoDB存储引擎支持也是MySQL高并发场景下的核心锁类型。
核心特性锁定范围仅涉及的行数据不影响其他行加锁方式无需显式加锁默认通过索引实现重点无索引会升级为表锁-- 写操作默认加行级X锁DELETEFROMtable_nameWHEREid1;-- 读操作显式加行级S锁SELECT*FROMtable_nameWHEREid1FORUPDATE;优缺点✅ 优点锁定粒度小并发性能好多个事务可同时操作同一表中不同行数据❌ 缺点加锁/解锁速度慢开销大可能产生死锁关键注意点划重点行锁的实现依赖索引如果查询条件没有使用索引比如WHERE nametest且name无索引InnoDB会无法定位到具体行此时会将行锁升级为表锁导致并发性能骤降 互动提问你有没有遇到过因无索引导致行锁升级为表锁的问题欢迎在评论区留言分享
间隙锁与Next-Key Lock解决幻读的核心行锁只能锁定已存在的行数据无法解决幻读问题事务A读取范围内数据事务B插入该范围数据A再次读取出现新数据。
为此InnoDB引入了间隙锁Gap Lock和Next-Key Lock两者共同构成了解决幻读的方案。
1 间隙锁Gap Lock锁定区间阻止插入定义锁定数据行之间的间隙包括行前和行后不锁定行本身核心目的是阻止其他事务在间隙中插入数据适用场景仅在InnoDB的Repeatable Read可重复读隔离级别下生效MySQL默认隔离级别示例假设表中有id为
1、
5的行数据事务A执行SELECT * FROM table WHERE id BETWEEN 1 AND 5 FOR UPDATE;此时会锁定以下间隙(负无穷,
(1,
(3,
(5, 正无穷)事务B尝试插入id2或4的数据时会被间隙锁阻塞
2 Next-Key Lock行锁间隙锁的组合定义Next-Key Lock 行锁Record Lock 间隙锁Gap Lock既锁定当前行也锁定当前行与下一行之间的间隙核心规则锁定的范围是“左开右闭”区间比如id3的行Next-Key Lock锁定的范围是(1, 3]作用彻底解决幻读问题是InnoDB Repeatable Read隔离级别下的默认锁机制示例表中id
1、
5事务A执行SELECT * FROM table WHERE id3 FOR UPDATE;此时Next-Key Lock锁定的范围是(1, 3]事务B更新id3的数据被行锁阻塞插入id2的数据被间隙锁阻塞
死锁Deadlock成因、场景与排查死锁是并发系统中最棘手的问题之一当两个或多个事务互相持有对方需要的锁资源且都不主动释放时就会陷入无限等待的死锁状态。
1 死锁产生的3大核心原因互斥条件资源锁只能被一个事务持有X锁的核心特性持有并等待事务A持有锁1同时等待事务B持有的锁2事务B持有锁2同时等待事务A持有的锁1不可剥夺事务持有锁时其他事务无法强制剥夺该锁只能等待其主动释放循环等待多个事务形成循环等待锁资源的链条比如A等BB等CC等A
2 典型死锁场景示例假设有user表id为主键两个事务同时执行以下操作事务ABEGIN;-- 持有id1的行级X锁UPDATEuserSETnameAWHEREid1;-- 等待id2的行级X锁UPDATEuserSETnameAWHEREid2;COMMIT;事务BBEGIN;-- 持有id2的行级X锁UPDATEuserSETnameBWHEREid2;-- 等待id1的行级X锁UPDATEuserSETnameBWHEREid1;COMMIT;此时两个事务互相等待对方的锁资源形成死锁MySQL会自动检测到死锁并回滚其中一个事务代价较小的那个。
3 死锁排查的4个核心步骤步骤1开启死锁日志永久生效需修改配置文件-- 临时开启死锁日志重启MySQL失效SETGLOBALinnodb_print_all_deadlocks1;-- 查看死锁日志位置SHOWVARIABLESLIKEdatadir;死锁日志会记录在MySQL的错误日志中通常是hostname.err文件。
步骤2实时查看锁状态-- 查看当前事务持有和等待的锁信息SELECT*FROMinformation_schema.innodb_locks;-- 查看当前事务等待锁的情况SELECT*FROMinformation_schema.innodb_lock_waits;-- 查看当前运行的事务SELECT*FROMinformation_schema.innodb_trx;步骤3分析死锁日志核心信息死锁日志会包含以下关键内容帮助定位问题死锁事务的IDTRX ID每个事务持有和等待的锁类型行锁/表锁、S锁/X锁每个事务执行的SQL语句死锁检测结果和回滚的事务步骤4优化并规避死锁根据排查结果针对性优化核心规避方案统一事务操作顺序所有事务对同一组资源的操作顺序保持一致比如都按id升序操作缩短事务执行时间减少事务中不必要的操作避免长时间持有锁避免批量加锁尽量拆分批量操作避免一次性锁定大量行数据合理设置隔离级别非必要不使用Repeatable Read可降低间隙锁带来的死锁概率加锁前预判对可能产生死锁的场景可通过显式加锁或事务超时设置规避✅ 小技巧这里建议大家收藏本文死锁排查步骤直接套用高效解决线上问题
全文
总结本文从核心锁类型S锁/X锁出发详解了表锁与行锁的粒度差异、适用场景深入剖析了间隙锁与Next-Key Lock解决幻读的底层逻辑最后重点讲解了死锁的成因、典型场景及排查优化方案。
掌握数据库锁机制不仅能快速定位线上并发问题更能在系统设计阶段规避潜在风险保障数据一致性和系统高可用性。
建议大家结合实际业务场景多练多