做网站费用入什么科目,网站代备案多少钱,推荐就业的培训机构,手机网站建设原则MySQL 的锁机制是实现并发控制的核心#xff0c;不同存储引擎和隔离级别下的锁策略差异巨大。以下按锁粒度 → 锁模式 → 特殊锁 → 死锁处理的顺序系统讲解
一、按粒度分类#xff1a;表锁 vs 行锁
锁的粒度决定了锁的并发度和开销#xff0c;是锁机制的首要维度
1. 表锁不同存储引擎和隔离级别下的锁策略差异巨大。以下按锁粒度 → 锁模式 → 特殊锁 → 死锁处理的顺序系统讲解一、按粒度分类表锁 vs 行锁锁的粒度决定了锁的并发度和开销是锁机制的首要维度1. 表锁Table Lock对整个表加锁开销小、加锁快但并发度最低。MyISAM 存储引擎-- 自动加表锁读锁共享锁SELECT*FROMusersWHEREid1;-- 自动加 READ LOCK-- 自动加表锁写锁排他锁UPDATEusersSETname张三WHEREid1;-- 自动加 WRITE LOCK锁机制读锁READ LOCK允许其他会话读禁止写写锁WRITE LOCK禁止其他会话读和写锁排队读锁和写锁互斥串行执行适用场景读多写少的静态表如配置表不适合高并发写入场景。InnoDB 存储引擎的表锁-- 手动加表级 READ LOCKLOCKTABLESusersREAD;-- 手动加表级 WRITE LOCKLOCKTABLESusersWRITE;-- 查看表锁状态SHOWOPENTABLESWHEREIn_use0;⚠️ 注意InnoDB 默认使用行锁表锁仅用于特殊场景如 DDL 操作手动加表锁会降级并发性能2. 行锁Row Lock对索引记录加锁开销大、加锁慢但并发度最高-- InnoDB 自动加行锁RC/RR 隔离级别UPDATEusersSETname张三WHEREid1;-- 对 id1 的行加排他锁行锁的 3 种实现方式行锁类型锁定范围示例说明Record Lock精确索引记录WHERE id 1锁定主键值为 1 的索引记录Gap Lock索引间隙不含记录WHERE id BETWEEN 10 AND 20RR 级别防止幻读锁定 10-20 的开区间Next-Key Lock记录 间隙WHERE id 10RR 级别Gap Lock Record Lock锁定 (负无穷, 10]⚠️关键行锁必须命中索引否则退化为表锁-- ❌ 行锁失效name 无索引退化为表锁UPDATEusersSETage20WHEREname张三;-- 锁全表-- ✅ 行锁生效name 有索引ALTERTABLEusersADDINDEXidx_name(name);UPDATEusersSETage20WHEREname张三;-- 仅锁匹配行二、按模式分类共享锁、排他锁、意向锁1. 共享锁S LockShared Lock允许其他事务读禁止写-- 显式加共享锁FOR SHARE 是 MySQL 8.0 语法兼容 READ LOCKSELECT*FROMusersWHEREid1FORSHARE;-- MySQL 8.0SELECT*FROMusersWHEREid1LOCKINSHAREMODE;-- MySQL 5.x-- 效果其他事务可读但无法获取排他锁无法 UPDATE/DELETE应用场景读取数据后需保证不被修改如财务对账2. 排他锁X LockExclusive Lock禁止其他事务读和写-- 自动加排他锁UPDATEusersSETname张三WHEREid1;-- 显式加排他锁SELECT*FROMusersWHEREid1FORUPDATE;关键行为UPDATE/DELETE自动加 X 锁SELECT … FOR UPDATE显式加 X 锁用于悲观锁场景如秒杀扣库存锁升级同一事务可对同一行先加 S 锁再加 X 锁3. 意向锁Intention Lock表级锁表明事务稍后要对表中的行加锁是 InnoDB 实现表锁和行锁共存的关键-- 事务 1对 users 表某行加 X 锁自动加意向排他锁 IXBEGIN;UPDATEusersSETname张三WHEREid1;-- 自动在 users 表加 IX 锁-- 事务 2尝试对 users 表加表锁LOCKTABLESusersREAD;-- 被阻塞因为检测到 IX 锁意向锁类型说明兼容性IS意向共享锁事务想对表中的行加 S 锁与表级 READ 锁兼容IX意向排他锁事务想对表中的行加 X 锁与任何表锁都冲突意义避免表锁和行锁冲突导致的死锁提升检查效率三、InnoDB 特殊锁机制1. 间隙锁Gap Lock在RR可重复读隔离级别下防止幻读的关键机制-- 假设 users 表中 id 为 1, 5, 10, 20-- 事务 1RR 级别BEGIN;SELECT*FROMusersWHEREidBETWEEN5AND15FORUPDATE;-- 锁定范围(5, 10) 和 (10, 15) 这两个间隙-- 关键字锁定开区间 (5, 15)不包含 5 和 15-- 事务 2被阻塞INSERTINTOusers(id,name)VALUES(8,李四);-- 被阻塞间隙被锁INSERTINTOusers(id,name)VALUES(3,王五);-- 成功不在锁定间隙目的防止其他事务在范围内插入新记录避免幻读2. 临键锁Next-Key LockGap Lock Record Lock锁定左开右闭区间(prev_record, record]-- 事务 1RR 级别BEGIN;SELECT*FROMusersWHEREid10FORUPDATE;-- 锁定范围(负无穷, 1], (1, 5], (5, 10], (10, 正无穷)-- 关键点包含记录本身及其左侧的间隙-- 事务 2INSERTINTOusers(id,name)VALUES(9,赵六);-- 被阻塞锁定 (5,10] 间隙INSERTINTOusers(id,name)VALUES(11,孙七);-- 被阻塞锁定 (10, 正无穷)InnoDB 默认锁在 RR 级别普通索引加 Next-Key Lock主键索引加 Record Lock。3. 插入意向锁Insert Intention Lock一种特殊的间隙锁表明事务想在某个间隙插入记录多个事务可同时在不同位置插入提高并发-- 事务 1BEGIN;SELECT*FROMusersWHEREid5FORUPDATE;-- 对 id5 加 X 锁-- 事务 2BEGIN;SELECT*FROMusersWHEREid5FORUPDATE;-- 被阻塞等待事务 1 释放-- 事务 3可成功INSERTINTOusers(id,name)VALUES(6,周八);-- 成功只等待 id5 的锁机制插入时先加 Insert Intention Lock检查间隙是否冲突不冲突则等待 Gap Lock 释放后插入4. 自增锁AUTO-INC Lock表级锁用于保证自增列值的唯一性CREATETABLEusers(idBIGINTAUTO_INCREMENTPRIMARYKEY,nameVARCHAR(50));-- 插入时自动加自增锁INSERTINTOusers(name)VALUES(张三),(李四),(王五);-- 锁定自增计数器保证三个 ID 连续且唯一锁模式innodb_autoinc_lock_mode0Traditional每次插入都加表级锁性能最差保证连续1Consecutive批量插入加表级锁简单插入用轻量锁默认2Interleaved无锁性能最好但 ID 可能不连续适合高并发四、不同隔离级别下的锁行为RC读已提交行锁仅对匹配行加 Record Lock无 Gap Lock不锁定间隙允许插入可能产生幻读一致性每次 SELECT 生成新 Read View可能读到其他事务已提交数据-- 事务 1RC 级别BEGIN;SELECT*FROMusersWHEREid10FORUPDATE;-- 仅锁定 id10 的行-- 事务 2INSERTINTOusers(id,name)VALUES(9,吴九);-- ✅ 成功间隙未锁RR可重复读行锁对匹配行和间隙加 Next-Key LockGap Lock锁定范围防止幻读一致性事务启动时生成 Read View保证可重复读-- 事务 1RR 级别BEGIN;SELECT*FROMusersWHEREidBETWEEN5AND15FORUPDATE;-- 锁定 (5,15] 间隙-- 事务 2INSERTINTOusers(id,name)VALUES(8,郑十);-- ❌ 被阻塞Serializable读加锁普通 SELECT 自动加S 锁完全串行所有操作串行执行并发度最低数据一致性最强五、死锁Deadlock与排查死锁产生条件互斥条件资源不能共享请求与保持持有资源同时申请新资源不可剥夺资源只能主动释放循环等待形成等待环路经典死锁案例-- 事务 1BEGIN;UPDATEusersSETnameAWHEREid1;-- 持有 id1 的 X 锁-- 等待 id2 的 X 锁UPDATEusersSETnameBWHEREid2;-- 事务 2同时执行BEGIN;UPDATEusersSETnameCWHEREid2;-- 持有 id2 的 X 锁-- 等待 id1 的 X 锁形成循环等待UPDATEusersSETnameDWHEREid1;-- DeadlockInnoDB 处理自动检测死锁回滚持有锁最少的事务死锁排查命令-- 查看最近一次死锁信息SHOWENGINEINNODBSTATUS\G-- 输出-- -------------------------- LATEST DETECTED DEADLOCK-- -------------------------- *** (1) TRANSACTION:-- UPDATE users SET name B WHERE id 2-- *** (2) TRANSACTION:-- UPDATE users SET name D WHERE id 1-- *** WE ROLL BACK TRANSACTION (2) -- 回滚事务 2-- 查看当前锁等待SELECT*FROMinformation_schema.INNODB_LOCK_WAITS;-- 查看当前事务SELECT*FROMinformation_schema.INNODB_TRX;-- 查看当前锁SELECT*FROMinformation_schema.INNODB_LOCKS;六、锁优化最佳实践1. 索引设计优化-- ❌ 导致表锁UPDATEusersSETname张三WHEREname李四;-- name 无索引-- ✅ 保证行锁ALTERTABLEusersADDINDEXidx_name(name);UPDATEusersSETname张三WHEREname李四;-- name 有索引2. 事务粒度控制-- ❌ 长事务持有锁时间过长BEGIN;-- 处理 10 秒业务逻辑UPDATEusersSETscorescore10WHEREid1;COMMIT;-- ✅ 缩短事务减少锁持有时间-- 业务逻辑提前处理BEGIN;UPDATEusersSETscorescore10WHEREid1;COMMIT;3. 避免热点行-- ❌ 高并发下所有请求锁同一行UPDATEinventorySETstockstock-1WHEREproduct_id1;-- ✅ 拆分到多行减少冲突UPDATEinventorySETstockstock-1WHEREproduct_id1ANDwarehouse_idMOD(NOW(),10);4. 死锁预防固定顺序访问所有事务按相同顺序申请锁如先锁 id1再锁 id2降低隔离级别将 RR 改为 RC减少 Gap Lock批量操作拆分将大事务拆分为小事务减少锁持有时间5.监控锁等待-- 查看锁等待超时时间SHOWVARIABLESLIKEinnodb_lock_wait_timeout;-- 默认 50 秒-- 设置会话级超时时间SETSESSIONinnodb_lock_wait_timeout10;-- 10 秒超时-- 开启死锁日志SETGLOBALinnodb_print_all_deadlocksON;七、总结锁类型粒度模式适用场景性能影响表锁粗S/XMyISAM 读多写少表并发度低行锁细S/XInnoDB 高并发 OLTP并发度高意向锁表IS/IXInnoDB 表锁检查几乎无影响间隙锁行间隙GapRR 级别防幻读可能阻塞插入临键锁行间隙Next-KeyRR 级别默认锁最严格自增锁表AUTO-INC自增列赋值批量插入影响性能核心原则优先行锁InnoDB 行锁并发度远高于表锁命中索引确保 WHERE 条件走索引避免退化为表锁缩短事务减少锁持有时间降低死锁概率监控死锁定期分析死锁日志优化事务顺序