公司动态
InnoDB 行级锁与意向锁
InnoDB 行级锁与意向锁一、锁整体概念1. 概念Concept定义数据库管理系统提供的并发控制机制用于协调多个事务对共享数据资源的访问顺序。分类按粒度表级锁、行级锁、页级锁-按功能共享锁S、排他锁X、意向锁IS/IX按策略悲观锁数据库锁、乐观锁版本号/CAS关系InnoDB 以行级锁为核心结合MVCC多版本并发控制实现非锁定读快照读兼顾并发性能与一致性。2. 作用 / 目的Purpose保证事务 ACID 中的隔离性Isolation与一致性Consistency。防止并发访问导致的脏读、不可重复读、幻读、丢失更新。不用的后果多事务同时读写同一数据时结果相互覆盖或依赖混乱数据失去可信度。3. 原理 / 机制Mechanism锁管理器Lock ManagerInnoDB 在内存中维护锁表和锁等待队列通过锁兼容性矩阵判断请求是否冲突。自动加锁/释放事务执行 DML 时自动申请锁事务提交COMMIT或回滚ROLLBACK时自动释放。两阶段锁协议2PL加锁和解锁分为两个阶段保证事务隔离。4. 使用方式Usage隐式加锁UPDATE / DELETE / INSERT等 DML 语句自动加相应锁。显式加锁SELECT...FORUPDATE;-- 显式加 X 锁SELECT...LOCKINSHAREMODE;-- 显式加 S 锁LOCKTABLEStWRITE;-- 显式表锁InnoDB 不推荐5. 注意事项 / 坑点Caveats锁粒度过大如退化为表锁会严重降低并发性能。死锁多个事务循环等待对方持有的锁InnoDB 会自动检测并回滚代价最小的事务。锁等待超时由参数innodb_lock_wait_timeout控制默认 50 秒。6. 对比 / 演进Comparison维度MyISAMInnoDB锁粒度仅表锁行锁 表锁并发读读锁会阻塞写MVCC 快照读不阻塞适用场景读多写少、非事务高并发、事务强一致二、共享锁S 锁 / Shared Lock1. 概念Concept定义行级读锁。事务对数据行加 S 锁后允许其他事务继续加 S 锁读取但禁止加 X 锁修改。分类行级锁、悲观锁。关系与 X 锁互斥与其他 S 锁兼容与意向共享锁IS兼容。2. 作用 / 目的Purpose保证读一致性读取期间阻止其他事务修改防止脏读。提高读并发多事务可同时持有同一行的 S 锁实现并发读。使用场景读取数据后需基于该值做业务判断如读取余额后决定是否转账。父子表一致性读取先对父表加 S 锁防止父记录被删。报表统计需确保查询期间数据不被修改。3. 原理 / 机制Mechanism事务对某行加 S 锁时InnoDB 执行两步对表加IS 锁意向共享锁对目标行加S 锁锁兼容性S-S 兼容S-X 互斥。底层通过lock_t结构体记录锁信息并维护锁等待队列。4. 使用方式Usage-- 显式加共享锁BEGIN;SELECT*FROMusersWHEREid1LOCKINSHAREMODE;-- 其他事务可继续 LOCK IN SHARE MODE但不能 FOR UPDATE 或 UPDATECOMMIT;-- 隐式加锁SERIALIZABLE 隔离级别SETSESSIONTRANSACTIONISOLATIONLEVELSERIALIZABLE;BEGIN;SELECT*FROMusersWHEREid1;-- 普通 SELECT 也加 S 锁COMMIT;5. 注意事项 / 坑点Caveats死锁风险事务 A 持 S 锁等 B 释放 X 锁事务 B 持 S 锁等 A 释放 X 锁形成循环等待。阻塞写操作长时间持有 S 锁会导致写事务持续等待影响系统吞吐量。与 MVCC 快照读的区别LOCK IN SHARE MODE是当前读Current Read会加真实锁普通SELECT在 RC/RR 下是快照读不加锁。6. 对比 / 演进Comparisonvs X 锁S 锁共享、只读X 锁独占、可写。vs 意向锁S 锁是真实行锁IS 锁是表级辅助标记。版本演进MySQL 8.0 支持SELECT ... FOR SHARELOCK IN SHARE MODE的新语法别名。三、排他锁X 锁 / Exclusive Lock1. 概念Concept定义行级写锁。事务对数据行加 X 锁后禁止其他事务加任何锁S 或 X访问该行。分类行级锁、悲观锁。关系与 S 锁、X 锁均互斥与意向排他锁IX在表级兼容。2. 作用 / 目的Purpose保证写操作独占性确保修改过程不被其他事务干扰防止丢失更新和脏写。保证原子性事务内的修改要么全部成功要么通过回滚撤销。使用场景库存扣减、账户转账、订单状态变更。悲观锁并发控制先锁定资源再执行业务逻辑。避免读取-修改-写入过程中的竞争条件。3. 原理 / 机制Mechanism事务对某行加 X 锁时InnoDB 执行两步对表加IX 锁意向排他锁对目标行加X 锁记录锁 Record Lock锁兼容性X 与任何锁S、X都互斥。对于范围查询可能升级为间隙锁Gap Lock或临键锁Next-Key Lock。4. 使用方式Usage-- 显式加排他锁BEGIN;SELECT*FROMusersWHEREid1FORUPDATE;UPDATEusersSETbalancebalance-100WHEREid1;COMMIT;-- 隐式加锁DML 自动BEGIN;UPDATEusersSETage20WHEREid1;-- 自动加 X 锁DELETEFROMusersWHEREid2;-- 自动加 X 锁INSERTINTOusers(name)VALUES(Tom);-- 对新插入行自动加 X 锁COMMIT;5. 注意事项 / 坑点Caveats死锁高发区多个事务以不同顺序对多行加 X 锁极易形成循环等待。锁未命中索引若WHERE条件无索引可能退化为表锁或锁定大量行。幻读问题仅靠 X 锁无法完全解决幻读需配合间隙锁 / 临键锁RR 隔离级别。超时控制锁等待超过innodb_lock_wait_timeout会报错Lock wait timeout exceeded。6. 对比 / 演进Comparisonvs S 锁X 锁独占资源S 锁共享资源。vs 乐观锁X 锁是先锁后改的悲观策略乐观锁是先改后校验如版本号无真实数据库锁。版本演进MySQL 8.0 支持SELECT ... FOR UPDATE NOWAIT立即报错不等待和SKIP LOCKED跳过已锁定行。四、意向锁IS / IX 锁 / Intention Lock1. 概念Concept定义表级锁用于声明事务即将对表中的某些行加行级锁S 或 X。IS意向共享锁事务即将对某些行加 S 锁。IX意向排他锁事务即将对某些行加 X 锁。分类表级锁、辅助锁。关系是行锁的前置信号锁协调表级锁与行级锁的冲突判断。2. 作用 / 目的Purpose核心作用解决加表锁时是否需要逐行扫描检查行锁的效率问题。无意向锁加表锁前需遍历全表检查每一行是否被锁定 →O(n) 复杂度。有意向锁只需检查表级是否有 IS/IX →O(1) 复杂度。使用场景ALTER TABLE需表级锁与正在进行的 DML持有 IX 锁之间的协调。LOCK TABLES显式表锁与行锁共存时的快速判断。外键约束检查时父表与子表之间的锁协调。3. 原理 / 机制Mechanism自动维护事务对行加 S 锁前先对表加 IS对行加 X 锁前先对表加 IX。兼容性IS 与 IX互相兼容多个事务可同时持有 IX。但 IS/IX 与表级 S/X 锁存在互斥IX 与 表 S 锁互斥IX 与 表 X 锁互斥IS 与 表 X 锁互斥表锁请求时只需检查表级意向锁状态无需遍历行记录。4. 使用方式Usage完全自动用户不可手动干预。-- 以下操作自动在表上加 IS 锁在行上加 S 锁SELECT*FROMusersWHEREid1LOCKINSHAREMODE;-- 以下操作自动在表上加 IX 锁在行上加 X 锁SELECT*FROMusersWHEREid1FORUPDATE;UPDATEusersSETage20WHEREid1;INSERTINTOusers(name)VALUES(Tom);5. 注意事项 / 坑点Caveats不可见但存在用户无法直接操作但可通过SHOW ENGINE INNODB STATUS查看锁状态。不是真正的阻塞锁意向锁本身不会阻塞其他事务对行的访问只阻塞表级锁请求。常见误区误以为意向锁会阻塞行级操作。实际上IX 与 IX 兼容多个事务可同时持有 IX 并操作不同行。6. 对比 / 演进Comparisonvs 行锁意向锁是表级信号行锁是真实数据保护两者协作意向锁不替代行锁。vs 表锁表锁直接阻塞整表所有访问意向锁仅影响后续表锁请求不影响行级 DML。版本演进从 InnoDB 诞生之初即存在机制稳定无重大版本差异。五、锁兼容性总表请求锁 \ 已有锁ISIXS表X表IS✓✓✓✗IX✓✓✗✗S表✓✗✓✗X表✗✗✗✗✓ 兼容可同时持有✗ 互斥需等待六、问题什么是锁InnoDB有哪些行级锁类型共享锁、排他锁意向锁的作用是什么第一什么是锁。 锁是数据库的并发控制工具。多事务同时读写同一份数据容易出现脏读、数据覆盖。锁就是用来规定访问顺序保障隔离性和一致性。InnoDB 搭配 MVCC 把读分成两种普通 SELECT 是快照读不加锁FOR UPDATE、LOCK IN SHARE MODE 是当前读会加锁。InnoDB 主打行锁粒度小并发高。第二行级锁。 行锁分两种基础锁配合 RR 隔离还有三种细分。共享锁 S 锁也叫读锁。多个事务可以同时加 S 锁读同一行彼此兼容但只要某一行上有 S 锁其他事务就无法对同一行加 X 锁去修改。适用场景读完数据要依据结果做判断必须保证读的过程中数据不会变。加锁方式SELECT … LOCK IN SHARE MODEMySQL 8.0 也可以用 FOR SHARE。排他锁 X 锁也叫写锁。增删改自动加 X 锁也能用 FOR UPDATE 手动加。一旦某行被 X 锁锁住其他事务不能修改也不能加锁读但普通 SELECT 走 MVCC 快照读不加锁也能读到数据。适合转账、库存扣减这类必须独占修改的场景。补充一句RR 隔离下行锁细分为记录锁、间隙锁、临键锁。记录锁锁单行间隙锁锁住索引空隙防止插入新数据临键锁是前两者的组合RR 范围查询默认用它从根源避免幻读。RC 下只有记录锁没有间隙锁所以幻读没法靠锁来解决。第三意向锁。 意向锁是表级锁分 IS 和 IX不能手动加全程自动维护。事务要给行加 S 锁必先给表加 IS要给行加 X 锁必先给表加 IX。核心目的只有一个优化性能。没有意向锁的话执行 ALTER 这类要加表锁的操作时数据库得逐行遍历全表检查有没有行锁效率极低。有意向锁只需要瞄一眼表上有没有 IS/IX立刻就能判断能不能加表锁。这里有个关键误区意向锁不会阻塞普通行操作。IS 和 IX、IX 和 IX 全都互相兼容多个事务改不同行互不影响意向锁只拦后续的表锁请求。最后记住这个分工行锁管数据意向锁管效率。 行锁保护真实读写意向锁是表上的预告牌让表锁判断少走弯路。二者配合InnoDB 才能在保证一致性的同时支撑高并发。