# 纲要
> 主干纲要、Hint/线索/路标
- **上锁思想**:
- 乐观锁
- 悲观锁
- **粒度分**:
- 全局锁
- **表级锁**
- 意图锁
- 元数据锁
- **行级锁**
- 记录锁
- 间隙锁
- 临键锁
- **按功能分**:(表级、行级锁均分为下列两种)
- 共享锁、独占锁
%%
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
%%
<br><br>
# 数据库上锁思想
就**上锁思想**而言(非具体锁),可分为两类:
- **乐观锁**:**操作的时候不加锁**,而在**更新数据时**进行 "**==版本校验==**" (利用版本号或时间戳),如果冲突了就拒绝修改。
- 适用于 "**读多写少**" 的场景,写操作少,故**假设数据冲突概率低**。
- **悲观锁**:**操作数据之前==先对数据加锁==**(利用数据库提供的 **==具体锁机制==**,例如行锁或者表锁),确保其他事务无法访问。
- 适用于 "**写多读少,并发冲突多**" 的场景。
这两种 "**锁**" 只是**一种抽象思想**,而非指代数据库提供的具体锁机制。
%%
- **乐观锁**:基于乐观假设(并发环境下对共享资源的访问冲突少见),采用 "**冲突检测机制**"(版本号或 CAS 操作),**更新共享数据时不加锁**,仅在 "**提交时**" 进行冲突检测,**若冲突则回滚操作**。
- **悲观锁**:基于悲观假设(并发环境中**对共享资源的访问冲突**频繁常见),因此**每次访问共享资源时都必须==直接加锁互斥==**。
%%
<br>
## 乐观锁
「**乐观锁**」:基于乐观假设(并发环境中**对共享资源的访问冲突**少见),因此:
- 在更新共享资源时不加锁,仅在**提交修改**时进行 **==冲突检测==**。
- 若没有检测到冲突,则修改可成功提交;如果检测到冲突,则会 **==回滚操作==** 并重试。
> [!NOTE] 乐观锁实际上 "没有锁",而是基于 "**冲突检测机制**"(版本号或 CAS 操作)
优点:
- **高并发场景效率高**:适用于**读多写少**的场景。在大部分操作不冲突的情况下,乐观锁可以减少锁的开销和阻塞时间,提升并发性能。
- **避免锁的长期持有**:因为不加锁,多个线程可以并行操作,只有在提交时才检查冲突。
缺点:
- **不适合写多的场景**:如果冲突频繁发生,回滚和重试的代价会很高,导致系统性能下降。
- **实现复杂度**:需要额外的机制(如版本号、CAS 等)来检测和解决冲突。
适用场景:
- 数据库中的**无锁更新**:在数据库中,乐观锁常用于**非阻塞事务**,如在`UPDATE`操作时,使用版本号或时间戳来检测数据是否被修改。
- **分布式系统**:在分布式环境中,多个节点对共享数据的更新往往使用乐观锁,以减少锁竞争带来的性能问题。
> [!example] 乐观锁使用场景
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-A1EF025DF9275E61906F027E32FA709B.png|492]]
### 乐观锁的实现思路——版本号机制
"版本号机制" 是**乐观锁常见的实现方式**:
1. 读取共享资源,同时**读取资源的==版本号或状态标识==**;
2. 执行更新操作(期间**不加锁**),准备提交更改;
3. 提交更改前,**检查版本号或状态是否与预期一致**:
- 若版本号未变化,**允许提交**。
- 若版本号已变化,**==回滚==操作**。
> [!NOTE] 手动实现乐观锁:基于 "版本号" 字段
>
> ```sql
> SELECT id, name, version FROM users WHERE id = 1;
>
> UPDATE users
> SET name = 'new_name', version = version + 1;
> WHERE id = 1 AND version = current_version; -- 仅当记录中的版本号与当前"版本号"变量一致时, 才进行加锁.
> ```
>
>
<br>
## 悲观锁
「**悲观锁**」:基于**悲观假设**(并发环境中**对共享资源的访问冲突**频繁常见),因此**每次访问共享资源时都必须==直接加锁互斥==**。
数据库提供的 **==具体锁机制==**,例如行锁或者表锁均属于"**悲观锁**" 的一种具体方案。
- 适用场景:
- **写操作频繁的场景**:在需要频繁写入数据的场景下,悲观锁可以避免数据不一致和冲突问题。
- **强一致性要求的应用**:如数据库事务中,为了保证数据的准确性,悲观锁会确保资源操作的排他性。
- 优点
- **冲突情况下安全性高**:悲观锁可以保证数据的一致性和安全性,尤其适用于高冲突场景或需要强一致性要求的场景。
- **实现简单**:直接加锁,而**无需复杂的冲突检测和回滚机制**
- 缺点:
- **性能问题**:在高并发场景下,悲观锁可能导致**锁争用严重**,阻塞其他线程的访问,降低系统的并发性能。
- **死锁风险**:多个线程或进程在获取锁时,如果顺序不当,可能会产生**死锁**。
<br><br><br>
# MySQL 锁类型
MySQL 提供**给 DBA 使用的锁**,按 "**粒度**" 分类,包括三种:
- (1)**==全局锁==**:对整个数据库加锁,**只允许读**(`SELECT`),而**阻止所有写操作**(包括对表结构和数据的写)。
- 主要用途:创建**数据库备份**(快照)、主从复制
- (2)**==表级锁==**:对 **"整个表"** 加锁;
- 主要用途:通常用于 "**DDL**" 语句,例如 `ALTER TABLE` 时,锁住整个表。
- (3)**==行级锁==**:仅对 **"==索引==上的特定==单行记录=="** 加锁,允许其他事务并发访问 "**不同行**"。
- 仅由 InnoDB 引擎提供,仅限于 "**事务**" 内使用。
从 "**功能**" 上,无论是 "**行锁**" 和 "**表锁**",都包括两种具体类型:
- **==共享锁== S Lock**(Share Lock):也称 "**读锁**",加锁后其他事务可以再**获取 "共享锁" 而 "==同时读=="**。
- **==排它锁== X Lock**(Exclusive Lock):也称 "**写锁/独占锁**",加锁后其他事务**不能 "读写"**;
> [!note] 注:
> - **表级锁**:在 MyISAM、InnoDB、MEMORY 引擎中均有提供。
> - **==行级锁==**:仅由 **InnoDB 引擎**提供,仅限在**事务内**使用,InnoDB 底层通过具体的 "记录锁"、"间隙锁"、"临键锁" 来实现。
<br>
## 锁的生命周期
| 锁类型 | |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| 表锁 | - `LOCK TABLES` 手动加锁后,直到**显式执行 `UNLOCK TABLES` 才会释放**。 <br>- DDL 语句(`ALTER TABLE, DROP TABLE, TRUNCATE TABLE`)会隐式加表锁,**语句结束后自动释放** |
| - InnoDB 意向锁 <br>- 元数据锁 MDL <br>- 行锁 | - 非事务环境:**单条语句执行结束后**立即释放<br>- 事务内:同**事务生命周期**,加锁后一直持有,直到**事务提交或回滚** |
<br><br>
# 上锁命令
##### 全局锁
```sql
-- 添加全局锁(FTWRL) 注: 会话断开后将自动解锁.
FLUSH TABLES WITH READ LOCK;
-- 记录 binlog 位置
SHOW MASTER STATUS;
-- 备份数据库(使用 mysqldump, 控制台命令)
mysqldump -uroot -p123456 数据库名 > 数据库文件名.sql
-- 解除全局锁
UNLOCK TABLES;
```
```sql
-- 设置全局只读模式
SET GLOBAL READ_ONLY = 1;
-- 取消全局只读模式
SET GLOBAL READ_ONLY = 0;
```
#### 表级锁
```sql
-- 添加表级锁
LOCK TABLES my_table READ; -- 表级共享锁(只读锁, 允许并发读)
LOCK TABLES my_table WRITE; -- 表级排它锁(阻塞其他所有读/写)
UNLOCK TABLES; -- 解锁;
```
#### 行级锁
- `SELECT ... LOCK IN SHARE MODE`:添加**行级共享锁**
- `SELECT ... FOR UPDATE`:添加**行级排它锁**
```sql
-- 手动添加行级锁: 为 `id=1` 一行记录添加行级锁
-- 1) 行级排它锁:
BEGIN;
SELECT * FROM my_table WHERE id = 1 FOR UPDATE;
-- 2) 行级共享锁:
BEGIN;
SELECT * FROM my_table WHERE id = 1 LOCK IN SHARE MODE;
-- UPDATE/DELETE会自动 "隐式添加排它锁X"
BEGIN;
UPDATE my_table SET name = 'new_value' WHERE id = 1; -- InnoDB自动为`id=1`一行记录添加排他锁.
DELETE FROM my_table WHERE id = 1;
```
<br><br><br>
# MySQL 中对 "表锁" 的支持
MySQL 中在对 "**表锁**" 的底层支持上,引入了三种锁机制:
- **==元数据锁==**(Metadata Lock, **==MDL==**):提供对表格元数据的 DLL 保护。
- **元数据读锁**(==MDL-S==)
- **元数据写锁**(==MDL-X==)
- **==意向锁==**(Intention Lock):用于在 "**事务添加行锁**" 时自动声明意图,提升**表锁性能**。
- **共享意向锁** ==IS==(Itention Shared Lock)
- **独占意向锁** ==IX==(Itention Exclusive Lock)
- **==自增锁==**(Auto Increment Lock):插入自增列时,加锁以**保证自增 ID 的唯一性**,防止并发插入导致的冲突。
> [!info] 这三种锁皆由 MySQL 底层 "自动加锁/释放" 。
>
> - **元数据锁**:由 "**==MySQL 服务器层==**" 提供,各引擎均支持;
> - **意向锁**、**自增锁**:仅由 "**==InnoDB 存储引擎==**" 提供。
<br>
## 元数据锁
元数据锁用于 "**数据库对象(表和索引)的元数据**" 提供 DDL 保护。
> [!NOTE] 元数据锁 MDL 是 "**==自动加锁==**",无论是否在事务内。
>
> - 在**事务内**,则 **MDL 锁会一直持有到事务结束**(提交 or 回滚)
> - 在**非事务环境**,则 **MDL 仅在单条 SQL 语句执行期间持有**。
作用:防止 ==**并发 DDL 冲突**==,以及 **==DDL 与 DML 冲突==("表结构修改" 与 "数据修改" 冲突)**;
- 对表进行 **DDL 结构性修改** (`ALTER TABLE` 修改表的元数据 或 `DROP TABLE`)时:
- 自动获取 "==**MDL-X 锁**==",**阻止其他事务对该表进行操作(包含==读和改==)**;
- 对表进行 **DML、DQL 数据操作**(读**表的元数据** or **读增删改表内数据**) 时:
- 自动获取 "**==MDL-S 锁==**",将**阻止其他事务对表进行 "结构性更改"**,防止对表 `ALTER` 或 `DROP`。
```SQL
BEGIN;
SELECT * FROM users; -- 自动加 MDL-S 锁
ALTER TABLE users ADD COLUMN age INT; -- 自动加 MDL-X 锁
```
<br>
## 意向锁
意向锁是 InnoDB 中为 "**==快速判断是否可以获取表锁==**" 而引入的辅助锁,**不阻塞行锁,仅影响 "==表锁==" 的获取**。
> [!NOTE] 作用说明
>
> 当 InnoDB 中需要添加 "表锁" 时,必须先判断表中是否 "已有 **==行锁==**",为**避免低效地逐行遍历**而引入了 "**意向锁**"。
> InnoDB 只需要检查是否存在 "**意向锁**",即可判断能否添加 "**表级锁**"。
意向锁包括两种:
- **共享意向锁** ==IS==(Itention Shared Lock):
- 当事务需要对表中数据行加 "**==行级读锁==**" 时,自动添加 "**==IS 共享意向锁==**"。此后允许获取 "**表级读锁**",但阻止 "**获取表级==写锁==**"
- **独占意向锁** ==IX==(Itention Exclusive Lock):
- 当事务需要对表中数据行加 "==**行级写锁**==" 时,自动添加 "**==IX 独占意向锁==**"。此后阻止获取任何 **"表级读锁 or 写锁"**。
> [!NOTE] **IS 和 IX 锁之间均 "互不冲突"**
>
> - 只要事务中 "声明需获取**行级读锁或写锁**",就会同时自动添加相应的 "**==意向锁==**",故一张表**可以同时存在多个 IS 和 IX 锁**。
>
> ```SQL
> -- 事务 A:对某行加S锁
> BEGIN;
> SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE; -- 自动添加IS锁
>
> -- 事务 B:对另一行加X锁
> BEGIN;
> SELECT * FROM user WHERE id = 2 FOR UPDATE; -- 自动添加IX锁
> ```
>
>
<br><br><br>
# InnoDB 引擎中对 "行锁" 的支持
**InnoDB** 中对 "**行级锁的==实现==**" 以及 "**防止 `REPEATEBLE READ` 级别下的==幻读==**",是通过下列三种具体 "上锁方式" 完成。
| | 作用说明 |
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
| **记录锁**<br>(Record Lock) | 锁定 "**==索引==**" 上的单个 "**==行记录==**",防止其他事务**修改**该条记录。 |
| **间隙锁**<br>(Gap Lock) | 锁定 **"==索引==" 上某个范围(而非具体行)**,防止其他事务**向该范围内插入 or 删除数据**。 <br>- `REPEATABLE READ` 隔离级别下生效,防止 “**幻读**”。 |
| **临键锁**<br>(Next-Key Lock) | 等于记录锁 + 间隙锁——同时锁定 "**==某行==**" 及其 "**==前面间隙==**"。可理解为锁住 "`(,]` **左开右闭区间**" |
> [!NOTE] 行级锁加在 "**==索引项==**" 上
>
> - 走**聚簇索引**时,锁只加在聚簇索引项上。
> - 走**二级索引**时,**二级索引项** 和 **聚簇索引项**都会加锁。
>
> [!danger] 如果 where 条件没有使用 "**==索引列==**" 却进行**加锁**,意味着将会 "**全表扫描**",对所有行加锁!相当于全表加锁!
>
> **带锁 select、update、delete** 均如此。
> 例如 `UPDATE user SET age = 20 WHERE name = "Alice";`, `name` 字段未建立索引,该语句将全表扫描,对所有行加锁![^2]
>
> 避免办法:可设置参数 `sql_safe_updates = 1`,则
>
> - **update** 仅当下列条件之一满足时才执行:
> - 使用 `where`,且 `where` 条件中具有索引列;
> - 使用 `limit`
> - 同时使用 `where` 和 `limit`,此时`where` 中可以无需索引列。
>
> - **delete** 仅当下列条件之一满足时才执行:
> - 同时使用 `where` 和 `limit`,此时`where` 中可以无需索引列。
> [!note] 对 "相同区间" 的多个 ==**间隙锁**== 可以共存(不区分 X 与 S),其目的只在于 "阻止区间被插入 or 删除区间内记录"
> [!info] "**插入间隙锁**"
>
> 该锁是与 "**间隙锁**" 相关的锁,其作用是 "**标记==等待某个间隙释放锁==**"。
>
> 当事务试图**向==被锁定的间隙==插入**时,将生成该锁,多个 "**插入意向锁**" 之间互不冲突。
> 当 "间隙锁" 被释放后,将寻找 "**插入意向锁**" 并唤醒。由于插入意向锁互不冲突,故**可多个事务并发插入**。
<br><br>
# InnoDB 行锁的具体加锁形式
上述三种锁是 InnoDB "**==实现行级锁==**" 的机制,而不是提供给 DBA 使用的锁。
在 **不同隔离级别 & 不同场景** 下添加**行锁**时,InnoDB 实际应用的锁形式不同:
| 隔离级别 | 行锁的实际类型 | 备注 |
| ---- | ----------------------- | ------------ |
| 读未提交 | 无锁 | |
| 读已提交 | **记录锁** | |
| 可重复读 | **临键锁**、**记录锁**、**间隙锁** | 还取决于具体场景,见下文 |
| 串行化 | | |
> [!example] 三种锁的应用示例
>
> 示例一:等值查询
>
> ```SQL
> -- (1)"读已提交"级别下, InnoDB 将对 id=5 这条记录加"记录锁".
> -- (2)"可重复读"级别下:
> -- (2.1) 对于"唯一索引or主键索引", 将只对id=5 这条记录加"记录锁".
> -- (2.2) 对于"非唯一索引", 将对id=5及其左右相邻间隙均加锁.
> -- 例如,`id=5`的相邻值是2与7, 则将加锁(2, 5]+(5, 7). 若id=5记录不存在, 则只加间隙锁(2, 7)
> -- 其余事务B将不能插入`id=3,4,6,7`的数据(间隙锁的效果, 防止幻读), 也不能更新`id=5`(记录锁的效果, 防止脏读和不可重复读)
> BEGIN;
> SELECT * FROM users WHERE id = 5 FOR UPDATE;
> UPDATE users set age = 15 WHERE id = 5; -- UPDATE、DELETE语句隐式加行锁, 实际锁形式同上
> ```
>
> 示例二:范围查询
>
> ```sql
> -- (1)"读已提交"级别下, InnoDB 将对 id > 100 的所有记录加 "记录锁".
> -- (2)"可重复读"级别下:
> -- (2.1) 对于"唯一索引or主键索引": 将只对`id>100`范围上锁.
> -- (2.2) 对于"非唯一索引": 将对`id>100`范围, 以及 "id=100的前方间隙" 加锁,
> BEGIN;
> SELECT * FROM my_table WHERE id > 100 FOR UPDATE;
> UPDATE users set age = 15 WHERE id > 100; -- UPDATE、DELETE 语句隐式加行锁, 实际锁形式同上
> ```
>
>
>
<br>
## 「可重复读」级别查询示例
> 详情参见[^1]
该级别下,**行锁**可能具体表现为三种类型:**记录锁、间隙锁、临键锁**,取决于具体场景,如下所示:
![[Excalidraw/Excalidraw-Solution/MySQL 锁.excalidraw|936]]
<br>
### 等值查询示例
#### (1)查询值存在
> [!example] 唯一索引—等值查询—值存在
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE id = 1 FOR UPDATE;
> ```
>
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-D4B9FF9B97438BBD43C56899B5F2F1D8.png|380]]
>
> [!example] 非唯一索引—等值查询—值存在
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE age = 22 FOR UPDATE;
> ```
>
> 值存在,则对**值**本身加**临键锁**,同时对值 "**右侧相邻间隙**" 加**间隙锁**:
>
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-AA3D38BF5B90A3DA0EE6596AF07A7CF8.png|506]]
>
>
#### (2)查询值不存在
> [!example] 唯一索引—等值查询—值不存在
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE id = 2 FOR UPDATE;
> ```
> 值不存在,故对**值所在 "间隙"** 加**间隙锁**:
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-8D41631E4E4272E6C637E665488CDE4B.png|389]]
>
>
> [!example] 非唯一索引—等值查询—值不存在
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE age = 25 FOR UPDATE;
> ```
>
> 值不存在,故对**值所在 "间隙"** 加**间隙锁**:
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-22729AFE5B15F03A3E44A50CCE186DD3.png|596]]
>
> 上述间隙锁下,若插入:
>
> - `age = 22` 的记录:**当 `id < 10` 是可插入的**,若 `id > 10` 则是不可插入的。
> - `age = 39` 的记录:**当 `id > 20` 是可插入的**,若 `id < 20` 则是不可插入的。
>
<br>
### 范围查询示例
#### (1)唯一索引上的范围查询
> [!example] 「唯一索引—范围查询——`>`
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE id > 15 FOR UPDATE;
> ```
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-E7D9ED910547D6E0D5EB3F666BCF305B.png|425]]
>
>
> [!example] 「唯一索引—范围查询——`>=`
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE id >= 15 FOR UPDATE;
> ```
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-AA851F3E77D89541F0B9C1A25B1DDCAF.png|432]]
>
> [!example] 「唯一索引—范围查询——`<`,值存在
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE id < 5 FOR UPDATE;
> ```
> 注:记录 `id=5` 存在,不取等,故只加 `(1, 5)` 间隙锁
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-6959A4349F2E76CD1905C04BEA38BF32.png|391]]
>
>
> [!example] 「唯一索引—范围查询——`<`,值不存在
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE id < 6 FOR UPDATE;
> ```
>
> 注意:`id=6` 记录不存在,故**间隙 `(5,10)`** 被加间隙锁。
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-4CABB2B4334ABC68FF424378A39D117E.png|386]]
>
#### (2)非唯一索引上的范围查询
会对条件值的 "**==左右相邻间隙==**" 全部加 "**==临键锁==**"。
> [!example] 「非唯一索引」—范围查询
>
> ```sql
> BEGIN;
> SELECT * FROM user WHERE age >= 22 FOR UPDATE;
> ```
>
> 注:`(, 22]` 与 `(22, +inf)` 范围**都会被上锁**。
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-91B9D1B8D43204820653D230669841E3.png|712]]
>
>
<br><br><br>
# 查看加锁情况
通过 `SELECT * FROM performance_schema.data_locks\G;` 可查看事务执行过程中添加的具体锁类型
字段说明:
| LOCK_TYPE | LOCK_MODE | LOCK_DATA |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------- |
| TABLE(表锁) | - `IX` : 排他意向锁 <br>- `IS`:共享意向锁 | NULL |
| RECORD(行锁) | - `X, REC_NOT_GAP`:独占记录锁<br>- `X, GAP`: 独占间隙锁 <br>- `X`: 独占临键锁 <br>- `S, REC_NOT_GAP`:共享记录锁<br>- `S, GAP`: 共享间隙锁 <br>- `S`: 共享临键锁 <br>- `INSERT_INTENTION`:插入意向锁 | 锁的右边界(锁住的索引值) |
> [!info] `LOCK_STATUS` 字段为上锁情况,`WATING` 表示在阻塞等待,`GRANTED` 表示当前持有。
> [!example]
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-7B17875E4DDD3DBC76CDB8E9135C4D70.png|431]]
>
>
> ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-67E2A9823C2C89A0EC57C27D0A9E318F.png|472]]
>
<br><br><br>
# 如何避免死锁
数据库层面,有两个办法:
- 设置事务**等待锁的超时时间**,**超时回滚**——InnoDB 中,通过参数 `innodb_lock_wait_timeout` 设置,默认为 50 秒。
- 超时信息: ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-13068D7ED57A5CF24F654C671631021C.png|710]]
- 开启 "**主动死锁检测**"——检测到死锁后,**主动回滚**死锁环路中的某一事务,破坏 "循环等待" 条件。
- 死锁检测信息: ![[_attachment/02-开发笔记/08-数据库/MySQL 相关/MySQL 锁.assets/IMG-MySQL 锁-70AAE4D954A7197FD87B0F3674A1E0C9.png|703]]
<br><br><br>
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
# ♾️参考资料
- [【MySQL】 锁-行级锁、表级锁、全局锁-CSDN博客](https://blog.csdn.net/wy02_/article/details/144596634?spm=1001.2014.3001.5501)
- [MySQL 是怎么加锁的? | 小林coding](https://xiaolincoding.com/mysql/lock/how_to_lock.html#%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95%E8%8C%83%E5%9B%B4%E6%9F%A5%E8%AF%A2)
- [MySQL 死锁了,怎么办? | 小林coding](https://xiaolincoding.com/mysql/lock/deadlock.html#_1%E3%80%81%E8%AE%B0%E5%BD%95%E4%B9%8B%E9%97%B4%E5%8A%A0%E6%9C%89%E9%97%B4%E9%9A%99%E9%94%81)
# Footnotes
[^1]: [MySQL 是怎么加锁的? | 小林coding](https://xiaolincoding.com/mysql/lock/how_to_lock.html#%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95%E8%8C%83%E5%9B%B4%E6%9F%A5%E8%AF%A2)
[^2]: [Fetching Title#9ouf](https://xiaolincoding.com/mysql/lock/update_index.html#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BC%9A%E5%8F%91%E7%94%9F%E8%BF%99%E7%A7%8D%E7%9A%84%E4%BA%8B%E6%95%85)