2195 字
11 分钟
MySQL共享锁和排他锁的区别

共享锁和排他锁的区别#

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的行,但阻止其他事务修改该行。

      SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
  • 排他锁

    • 用于写操作,如UPDATEDELETEINSERT

    • 典型场景:需要修改数据并确保数据一致性(如库存扣减、余额更新)。

    • 显式获取方式:SELECT ... FOR UPDATE

    • 示例: 锁定id = 1的行,阻止其他事务读取或修改,直到当前事务结束。

      SELECT * FROM users WHERE id = 1 FOR UPDATE;

4. 获取方式#

  • 共享锁
    • 自动:某些情况下,InnoDB在SELECT查询时可能隐式加S锁(取决于隔离级别)。
    • 显式:SELECT ... LOCK IN SHARE MODE
  • 排他锁
    • 自动:执行UPDATEDELETE等写操作时,InnoDB自动为受影响的行加X锁。
    • 显式:SELECT ... FOR UPDATE

5. 锁粒度#

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

6. 性能影响#

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

7. 典型应用场景对比#

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

8. 死锁风险#

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

示例对比#

假设有products表,字段包括idstock

-- 事务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 COMMITTEDREPEATABLE READ)合理选择锁类型,以平衡一致性和性能。

排他锁#

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

1. 什么是排他锁?#

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

  • 特点
    • 排他锁与任何其他锁(包括共享锁和排他锁)都不兼容。
    • 持有排他锁的事务可以安全地修改数据,而不被其他事务干扰。
    • 常用于UPDATEDELETEINSERT等写操作。

2. 排他锁的工作机制#

在MySQL的InnoDB存储引擎中(默认支持事务和行级锁),排他锁主要通过以下方式实现:

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

当一个事务执行写操作(如UPDATEDELETE)时,InnoDB会自动为受影响的行加排他锁。例如:

UPDATE users SET age = 30 WHERE id = 1;

MySQL会对id = 1的行加排他锁,直到事务提交(COMMIT)或回滚(ROLLBACK)才会释放锁。

3. 排他锁的获取方式#

  • 隐式获取:通过DML操作(如UPDATEDELETE)自动加锁。例如:

    UPDATE table_name SET column = value WHERE condition;

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

  • 显式获取:使用SELECT ... FOR UPDATE语句显式加排他锁。例如:

    SELECT * FROM users WHERE id = 1 FOR UPDATE;

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

4. 排他锁的兼容性#

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

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

5. 排他锁的场景#

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

示例(库存扣减):

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 READREAD COMMITTED),减少间隙锁的使用。

8. 与共享锁的对比#

  • 共享锁(S锁):允许多个事务同时读取数据,但不允许修改。常用于SELECT ... LOCK IN SHARE MODE
  • 排他锁(X锁):只允许一个事务修改数据,阻塞其他读写操作。

总结#

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