---
title: MySQL共享锁和排他锁的区别
published: 2025-07-18
description: ''
image: ''
tags: [mysql锁]
category: '中间件 > MySQL'
draft: false 
lang: 'zh-cn'
---

# 共享锁和排他锁的区别

MySQL中的**共享锁（Shared Lock，简称S锁）**和**排他锁（Exclusive Lock，简称X锁）**是InnoDB存储引擎用于并发控制的两种锁机制，主要区别在于锁的兼容性和使用场景。以下是两者的详细对比：

### 1. **定义**

- **共享锁（S锁）**：允许多个事务同时对同一数据加共享锁，用于读取数据，防止其他事务修改数据，但允许多个事务同时读取。
- **排他锁（X锁）**：只允许一个事务对数据加锁，用于修改数据，阻止其他事务对同一数据加任何锁（包括共享锁和排他锁）。

### 2. **锁的兼容性**

| 锁类型            | 共享锁（S锁） | 排他锁（X锁） |
| ----------------- | ------------- | ------------- |
| **共享锁（S锁）** | 兼容          | 不兼容        |
| **排他锁（X锁）** | 不兼容        | 不兼容        |

- **共享锁**：多个事务可以同时持有同一数据的S锁，适合并发读取。
- **排他锁**：一旦某事务持有X锁，其他事务无法对同一数据加S锁或X锁，必须等待锁释放。

### 3. **使用场景**

- **共享锁**：

  - 用于只读操作，如`SELECT`查询。

  - 典型场景：多个事务需要读取同一数据，但不修改（如报表查询）。

  - 显式获取方式：`SELECT ... LOCK IN SHARE MODE`。

  - 示例：
    允许多个事务同时读取`id = 1`的行，但阻止其他事务修改该行。

      ```sql
    SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
    
      ```

- **排他锁**：

  - 用于写操作，如`UPDATE`、`DELETE`、`INSERT`。

  - 典型场景：需要修改数据并确保数据一致性（如库存扣减、余额更新）。

  - 显式获取方式：`SELECT ... FOR UPDATE`。

  - 示例：
    锁定`id = 1`的行，阻止其他事务读取或修改，直到当前事务结束。

      ```sql
    SELECT * FROM users WHERE id = 1 FOR UPDATE;
    
      ```

### 4. **获取方式**

- **共享锁**：
  - 自动：某些情况下，InnoDB在`SELECT`查询时可能隐式加S锁（取决于隔离级别）。
  - 显式：`SELECT ... LOCK IN SHARE MODE`。
- **排他锁**：
  - 自动：执行`UPDATE`、`DELETE`等写操作时，InnoDB自动为受影响的行加X锁。
  - 显式：`SELECT ... FOR UPDATE`。

### 5. **锁粒度**

- 两者都支持**行级锁**（InnoDB默认）和**表级锁**（如MyISAM或特定操作）。
- 共享锁和排他锁在范围查询中可能涉及**间隙锁**或**下一键锁**（Next-Key Lock），用于防止幻读，具体取决于事务隔离级别（如`REPEATABLE READ`）。

### 6. **性能影响**

- **共享锁**：允许多个事务并发读取，适合读多写少的场景，阻塞较少。
- **排他锁**：阻止其他事务读写，适合写操作，但可能导致阻塞和死锁，尤其在高并发场景下。

### 7. **典型应用场景对比**

- **共享锁**：多个用户同时查看商品库存、生成报表等。
- **排他锁**：扣减库存、更新账户余额、防止并发修改导致数据不一致。

### 8. **死锁风险**

- **共享锁**：死锁风险较低，因为S锁之间兼容。
- **排他锁**：死锁风险较高，多个事务竞争X锁可能导致互相等待，InnoDB会检测并回滚一个事务。

### 示例对比

假设有`products`表，字段包括`id`和`stock`：

```sql
-- 事务A：读取库存（共享锁）
BEGIN;
SELECT stock FROM products WHERE id = 1 LOCK IN SHARE MODE;
-- 其他事务可以同时读取，但不能修改
COMMIT;

-- 事务B：扣减库存（排他锁）
BEGIN;
SELECT stock FROM products WHERE id = 1 FOR UPDATE;
UPDATE products SET stock = stock - 1 WHERE id = 1;
COMMIT;

```

- 事务A的共享锁允许其他事务读取`id = 1`的行，但阻止修改。
- 事务B的排他锁阻止其他事务读取或修改`id = 1`的行，直到事务B结束。

### 总结

- **共享锁**适合读操作，允许多个事务并发读取，强调高并发读性能。
- **排他锁**适合写操作，确保数据修改的独占性和一致性，但可能降低并发性能。
- 在实际应用中，结合业务需求、索引优化和事务隔离级别（如`READ COMMITTED`或`REPEATABLE READ`）合理选择锁类型，以平衡一致性和性能。

# 排他锁

MySQL的排他锁（Exclusive Lock，简称X锁）是一种用于并发控制的锁机制，确保在同一时间只有一个事务可以修改特定数据，防止数据冲突和不一致。以下是对MySQL排他锁的详细讲解：

### 1. **什么是排他锁？**

排他锁是MySQL中用于写操作的锁类型。当一个事务对某行、表或数据对象加了排他锁后，其他事务无法对同一数据进行读（共享锁）或写（排他锁）操作，直到该锁被释放。这保证了数据修改的原子性和一致性。

- **特点**：
  - 排他锁与任何其他锁（包括共享锁和排他锁）都不兼容。
  - 持有排他锁的事务可以安全地修改数据，而不被其他事务干扰。
  - 常用于`UPDATE`、`DELETE`、`INSERT`等写操作。

### 2. **排他锁的工作机制**

在MySQL的InnoDB存储引擎中（默认支持事务和行级锁），排他锁主要通过以下方式实现：

- **行级锁**：锁住特定的行记录，只有被锁定的行无法被其他事务访问。
- **表级锁**：在某些情况下（如表级操作或MyISAM引擎），锁住整个表。
- **间隙锁（Gap Lock）和下一键锁（Next-Key Lock）**：用于防止幻读，锁定某个范围的索引记录（常见于范围查询）。

当一个事务执行写操作（如`UPDATE`或`DELETE`）时，InnoDB会自动为受影响的行加排他锁。例如：

```sql
UPDATE users SET age = 30 WHERE id = 1;

```

MySQL会对`id = 1`的行加排他锁，直到事务提交（`COMMIT`）或回滚（`ROLLBACK`）才会释放锁。

### 3. **排他锁的获取方式**

- **隐式获取**：通过DML操作（如`UPDATE`、`DELETE`）自动加锁。例如：

  ```sql
  UPDATE table_name SET column = value WHERE condition;
  
  ```

  InnoDB会自动为受影响的行加排他锁。

- **显式获取**：使用`SELECT ... FOR UPDATE`语句显式加排他锁。例如：

  ```sql
  SELECT * FROM users WHERE id = 1 FOR UPDATE;
  
  ```

  这会锁定`id = 1`的行，阻止其他事务读取或修改该行，直到当前事务结束。

### 4. **排他锁的兼容性**

排他锁与其他锁的兼容性如下：

- **排他锁与排他锁**：不兼容，两个事务不能同时对同一数据加X锁。
- **排他锁与共享锁（S锁）**：不兼容，持有X锁的数据无法被其他事务加S锁读取。
- **结果**：排他锁会导致其他事务等待（阻塞），直到锁释放。

### 5. **排他锁的场景**

- **数据修改**：如`UPDATE`、`DELETE`操作，确保数据一致性。
- **防止并发冲突**：在高并发场景下，避免多个事务同时修改同一行导致数据不一致。
- **悲观锁机制**：通过`SELECT ... FOR UPDATE`实现悲观锁，适合需要严格控制并发访问的业务场景（如库存扣减）。

示例（库存扣减）：

```sql
BEGIN;
SELECT stock FROM products WHERE id = 1 FOR UPDATE;
-- 假设查询到stock=10
UPDATE products SET stock = stock - 1 WHERE id = 1;
COMMIT;

```

`FOR UPDATE`加排他锁，确保在事务期间其他事务无法修改`id = 1`的记录。

### 6. **可能的问题**

- **死锁**：当多个事务互相等待对方持有的排他锁时，可能发生死锁。InnoDB会自动检测死锁并回滚一个事务。
- **性能影响**：排他锁会阻塞其他事务，可能降低并发性能，尤其在高并发场景下。
- **锁范围过大**：若锁住的范围过大（例如表锁或范围查询的间隙锁），可能导致更多事务阻塞。

### 7. **如何优化排他锁的使用**

- **尽量使用行级锁**：确保查询条件使用索引，避免锁住过多行。
- **缩短事务时间**：尽快提交或回滚事务，减少锁的持有时间。
- **避免死锁**：按照固定顺序访问资源（如按表或主键顺序加锁）。
- **选择合适的隔离级别**：如降低隔离级别（从`REPEATABLE READ`到`READ COMMITTED`），减少间隙锁的使用。

### 8. **与共享锁的对比**

- **共享锁（S锁）**：允许多个事务同时读取数据，但不允许修改。常用于`SELECT ... LOCK IN SHARE MODE`。
- **排他锁（X锁）**：只允许一个事务修改数据，阻塞其他读写操作。

### 总结

MySQL的排他锁是确保数据写操作一致性的重要机制，广泛用于事务性操作。通过合理设计查询和事务，可以最大程度减少锁冲突和性能问题。在高并发场景下，建议结合索引优化、事务管理以及合适的隔离级别来平衡一致性和性能。
