MySQL Innodb 中的锁

本文深入探讨MySQLInnoDB中的锁机制,包括行级锁、意向锁等,并详细阐述了一致性非锁定读与锁定读的区别,以及这些机制如何帮助避免并发冲突,特别是在涉及外键操作时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MySQL Innodb 中的锁

锁是用来解决并发冲突的必要手段,MySQL 中的并发主要是指多个线程同时对同一个数据库进行操作,其中不同线程可能代表不同的事务,本质上也就是对共享资源的不同事务的同时访问。

Innodb 支持行级锁和意向锁。行级锁就是对行记录进行加锁,行级锁也分为两种类型,一种是共享锁( S 锁),一种是排他锁( X 锁)。意向锁为表级别的锁,也就是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁,一般来说,意向锁是从上往下(数据库->表->页->记录)进行加锁,其中任何一个部分导致等待,那么该操作都会需要等待粗粒度的锁的完成。

读的锁定操作

一致性非锁定读

MySQL 的默认事务隔离级别是 Repeatable Read,在事务隔离为 Repeatable Read 和 Read Committed 级别下,默认的读取方式即为一致性非锁定读。一致性非锁定读即为在访问行记录时,不需要等待该行上的 X 锁释放。Innodb 的一致性非锁定读是以多版本控制实现的,在多版本控制中,会保存修改之前数据的快照,作为恢复使用。

需要注意的是:在 Repeatable Read 和 Read Committed 的两种级别下的一致性非锁定读也是不同的,Repeatable Read 隔离级别下,总是读取事务开始时的行数据,而 Read Committed 模式下总是读取该行版本上的最新快照。

一致性锁定读

默认情况下,在 Repeatable Read 和 Read Committed 隔离界别下,SELECT 语句对一行的读取是使用一致性非锁定读的,用户可以通过指定 SELECT 语句使用一致性锁定读,例如:

  1. SELECT ... FOR UPDATE
  2. SELECT ... LOCK IN SHARE MODE

这两种方法可以分别给行记录加上一个 X 锁和 S 锁。

外键和锁

对于一个外键的更新和插入,首先需要查询父表中的记录,即主动对父表加一个 S 锁,这里必须使用一致性锁定读。如果不这么做,当另外事务 A 对父表的行记录进行了删除操作,而事务 B 打算在子表中添加一行与父表具有外键关系的行,事务 B 会读到存在该外键,从而添加(从快照中读取),但是实际上当事务 A 提交后,父表中将不存在该主键,从而导致父子表中完整性被破坏。

行锁的三种方法

Record Lock:单个行记录上的锁。

Gap Lock:间隙锁,不包含记录本身。

Next-Key Lock:锁定一个范围同时也锁定记录本身。

在查询的列是唯一索引时,Next-Key Lock 会降级为 Record Lock,以提高并发效率。

在 Repeatable Read 和 Read Committed 情况下使用行锁的方法不同,在 Repeatable Read 下,使用 Next-Key Lock,而在 Read Committed 情况下,使用 Record Lock。

死锁的发生

死锁发生的概率:与 n(事务的数量)和 r(每个事务操作的数量)以及 R(事务操作的数据集合)有关,前两个是正相关,后两个是负相关。

AB-BA 死锁

在这种情况下,事务 A 和事务 B 互相等待对方的加锁资源,从而导致死锁。InnoDB 存储引擎会回滚 undo log 记录小的事务。

死锁 2

如果当前事务持有了待插入记录的下一个记录的 X 锁,但是在等待队列中存在一个 S 锁的请求,则可能会发生死锁,这个通过举例可以很容易理解。

假设事务 A 首先获取了 a = 4 的行记录的 X 锁,此时事务 B 请求 a <= 4 的 S 锁(阻塞,因为 X 和 S 锁不兼容),此时事务 A 在插入了 a = 3 的行记录。此时会发生死锁,InnoDB 存储引擎会回滚 undo log 记录大的事务。

转载于:https://www.cnblogs.com/bdsir/p/8868510.html

<think>嗯,用户想了解MySQL InnoDB机制,这是个数据库核心问题。从引用内容看,用户应该已经查阅过相关资料,但需要系统性的梳理。 用户可能正在开发高并发应用,遇到了性能瓶颈或死问题。引用[3]提到排他的特性,引用[5]强调索引对行的重要性,说明用户关注的是的实际应用场景和优化方案。需要特别注意InnoDB与Oracle的差异(引用[1]),这点常被忽视但很关键。 机制需要分层讲解:先区分类型(表/行),再细分行里的共享/排他/意向,最后是算法(记录/间隙/临键)。引用[4]的监控指标很重要,但用户没明确说是否需要监控部分,可以放在进阶内容里。 用户引用的资料质量不错,特别是提到《高性能MySQL》和《MySQL技术内幕》,说明需要专业级内容。但要注意避免堆砌术语,比如解释“间隙”时必须配合具体SQL示例(如范围查询)。死分析部分要给出排查路径,因为引用[5]提到优化建议,用户可能正被死困扰。 最后考虑设计相关问题:兼容性(开发常踩坑)、死处理(运维刚需)、隔离级别影响(架构师关注点),覆盖不同层次需求。监控指标那题专门呼应引用[4],保持知识闭环。</think>以下是对MySQL InnoDB机制的系统性解析,结合核心特性和实践建议: --- ### **一、InnoDB的类型** 1. **行级(Row-Level Locks)** - **共享(S Lock)**:允许事务读取一行数据,其他事务可加共享但不可加排他。 ```sql SELECT ... LOCK IN SHARE MODE; -- 显式加共享 ``` - **排他(X Lock)**:允许事务更新或删除数据,其他事务无法加任何。 ```sql SELECT ... FOR UPDATE; -- 显式加排他 ``` 2. **表级(Table-Level Locks)** - 当**无法使用索引检索数据**时,InnoDB会退化为表(引用[^1])。 - 例如:`UPDATE table SET col=1 WHERE unindexed_col=2;` 若`unindexed_col`无索引,则定整表。 3. **意向(Intention Locks)** - **意向共享(IS)**:事务准备给数据行加共享前,先申请IS。 - **意向排他(IX)**:事务准备给数据行加排他前,先申请IX。 - **作用**:避免表与行的冲突(如:事务A持有时,事务B想加表需先检查意向)。 --- ### **二、的算法(定范围)** 1. **记录(Record Lock)** - 定索引中的**单条记录**(即使表无索引,InnoDB也会创建隐藏聚簇索引)。 2. **间隙(Gap Lock)** - 定索引记录之间的**间隙**(开区间),防止其他事务插入。 - 示例:`SELECT * FROM table WHERE id BETWEEN 10 AND 20 FOR UPDATE;` 会定(10,20)区间。 3. **临键(Next-Key Lock)** - **记录 + 间隙**的组合(左开右闭区间),InnoDB默认使用此。 - 解决幻读问题(如`SELECT ... WHERE id > 10`定(10, +∞])。 --- ### **三、的兼容性与冲突** | 请求类型 | X(排他) | S(共享) | IX(意向排他) | IS(意向共享) | |------------|-----------|-----------|----------------|----------------| | **X(排他)** | ❌ | ❌ | ❌ | ❌ | | **S(共享)** | ❌ | ✓ | ❌ | ✓ | | **IX(意向排他)**| ❌ | ❌ | ✓ | ✓ | | **IS(意向共享)**| ❌ | ✓ | ✓ | ✓ | > ✓:兼容; ❌:冲突 --- ### **四、监控与性能指标** 通过`SHOW ENGINE INNODB STATUS`或系统表查看状态(引用[^4]): ```sql SHOW STATUS LIKE 'innodb_row_lock%'; ``` 关键指标: - `Innodb_row_lock_waits`:等待总次数 - `Innodb_row_lock_time_avg`:平均等待时长(毫秒) - `Innodb_row_lock_current_waits`:当前等待的事务数 --- ### **五、优化建议** 1. **强制使用行** - 确保WHERE条件使用**索引列**(否则退化为表,引用[^1][^5])。 2. **减少范围** - 避免大事务,尽量缩小事务操作的数据量。 - 精确使用索引(如避免`LIKE '%value'`导致全表扫描)。 3. **控制隔离级别** - 在`READ COMMITTED`级别下,间隙范围缩小,减少冲突。 4. **死处理** - InnoDB自动检测死并回滚代价较小的事务(需业务层重试机制)。 --- ### **六、实践案例** **场景**:高并发下单扣库存 ```sql -- 正确做法(使用索引+行) BEGIN; SELECT quantity FROM products WHERE id=100 FOR UPDATE; -- id为主键索引 UPDATE products SET quantity=quantity-1 WHERE id=100; COMMIT; ``` **错误做法**: ```sql UPDATE products SET quantity=quantity-1 WHERE name='ProductA'; -- name无索引 → 触发表! ``` > 关键原则:**通过索引检索数据是使用行的前提**(引用[^1][^3])。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值