Skip to content

在数据库管理系统中,事务通常会通过锁机制来控制并发访问,以确保数据的一致性和隔离性。锁的使用确保了多个事务能够在并发执行时遵循预定的 隔离级别,防止出现诸如 脏读不可重复读幻读 等问题。

1. 什么时候会加锁?

加锁的时机取决于事务的 操作类型隔离级别数据库引擎(例如 MySQL 使用 InnoDB 引擎)。下面是一些常见的操作和加锁的场景:

1.1 查询操作(SELECT)

  • REPEATABLE READ 隔离级别下,执行查询时,数据库会根据查询的条件和加锁策略决定是否需要加锁。
    • 对于 SELECT FOR UPDATESELECT LOCK IN SHARE MODE 语句,数据库会加锁。
    • 对于普通的 SELECT 查询,通常不加锁,除非涉及到 范围查询多行查询,此时会加上 行锁间隙锁 来避免幻读。

1.2 插入操作(INSERT)

  • 当插入数据时,数据库会对插入的行加 排他锁(Exclusive Lock),防止其他事务同时插入相同的数据或修改正在插入的行。

1.3 更新操作(UPDATE)

  • 执行更新时,数据库会加 排他锁(Exclusive Lock),确保其他事务不能读取或修改这些数据,直到当前事务提交。

1.4 删除操作(DELETE)

  • 删除数据时,数据库会对被删除的行加 排他锁(Exclusive Lock),直到事务提交。

2. 锁住哪些行?

锁住的行通常取决于以下因素:

  • 查询条件:锁的范围通常由查询的条件决定,可能是某一行或某一范围内的多行。
  • 事务操作的类型:插入、更新、删除等操作都会影响锁的范围。
  • 隔离级别:不同的隔离级别(如 READ COMMITTEDREPEATABLE READSERIALIZABLE)会影响数据库如何加锁。

2.1 行级锁(Row-Level Lock)

行级锁会锁住查询、插入、更新或删除的具体行,常见的情况如下:

  • SELECT FOR UPDATE:会锁住所有查询到的行,并且不允许其他事务修改这些行,直到当前事务提交。
  • SELECT ... WHERE ... FOR UPDATE:锁定符合条件的行。只有当前事务能够对这些行进行修改,其他事务必须等待。
  • INSERT:插入操作时,锁住新插入的行。
  • UPDATE:更新操作时,锁住更新的行。

2.2 间隙锁(Gap Lock)

间隙锁会锁住某些 不存在的行,即锁住数据范围之间的空隙,防止其他事务插入数据。它用于 REPEATABLE READ 隔离级别下,防止幻读的发生。间隙锁通常用于 范围查询

例如,对于一个查询:

sql
SELECT *
FROM products
WHERE price BETWEEN 100 AND 200;

MySQL 会在 100200 之间的范围加锁,即使这个范围内没有数据,也会加锁。

2.3 表级锁(Table-Level Lock)

当数据库无法应用行级锁时,会退而求其次,采用 表级锁。例如,在某些操作中,如果查询条件不明确或涉及多个表的联接,可能会对整个表加锁,防止其他事务对整个表进行修改。

2.4 排他锁(Exclusive Lock)与共享锁(Shared Lock)

  • 排他锁(Exclusive Lock):当一个事务更新、删除或插入数据时,会对数据加上排他锁,其他事务不能读取或修改这些行,直到事务提交。
  • 共享锁(Shared Lock):当一个事务只读取数据时,会加上共享锁。其他事务可以读取这些数据,但不能修改它们。

3. 其他事务在锁住的时候可以执行哪些操作?

其他事务的操作受锁机制和事务隔离级别的影响。下面是不同类型锁的操作影响:

3.1 排他锁(Exclusive Lock)

当一个事务对某行加了 排他锁 时,其他事务的行为如下:

  • 读取(SELECT):其他事务无法读取已加锁的行。
  • 更新(UPDATE):其他事务无法修改已加锁的行。
  • 删除(DELETE):其他事务无法删除已加锁的行。
  • 插入(INSERT):其他事务可以插入新的行,前提是插入的数据不冲突(例如,主键不重复)。

3.2 共享锁(Shared Lock)

当一个事务对某行加了 共享锁 时,其他事务的行为如下:

  • 读取(SELECT):其他事务可以读取已加共享锁的行。
  • 更新(UPDATE):其他事务不能修改已加共享锁的行,必须等到共享锁释放。
  • 删除(DELETE):其他事务不能删除已加共享锁的行,必须等到共享锁释放。
  • 插入(INSERT):其他事务可以插入新的行。

3.3 间隙锁(Gap Lock)

当事务加了 间隙锁 时,其他事务通常不能在锁住的范围内插入数据。对于已经存在的行,其他事务仍然可以进行读取或修改,只要不与间隙锁锁定的范围冲突。

3.4 无锁读取(例如 SELECT 查询)

如果一个事务只执行 SELECT 查询且没有加锁,其他事务仍然可以并发地对数据进行修改。读取操作的隔离级别决定了其他事务对数据修改的可见性。

  • READ COMMITTED 隔离级别下,事务在执行时会读取已提交的最新数据,即使其他事务在修改数据时,当前事务也能读取到其他事务已经提交的更改。
  • REPEATABLE READ 隔离级别下,事务会保证查询的一致性,即使其他事务正在修改数据,当前事务也看不到这些修改。
  • SERIALIZABLE 隔离级别下,事务不仅阻止其他事务修改数据,还会阻止其他事务插入可能影响查询结果的数据。

4. 总结

  1. 加锁的时机:事务在进行读取、插入、更新、删除等操作时,数据库会根据操作的类型、查询条件和事务隔离级别决定是否加锁。
  2. 锁住的行 :具体加锁的行取决于事务的操作类型、查询条件以及隔离级别。可以是单行、范围内的多行、甚至是数据之间的间隙(在 REPEATABLE READ 隔离级别下)。
  3. 其他事务的操作
    • 如果加了 排他锁,其他事务不能读取或修改这些行。
    • 如果加了 共享锁,其他事务可以读取但不能修改这些行。
    • 其他事务在加锁时仍然可以执行某些操作,具体取决于锁的类型和隔离级别。

不同的隔离级别和锁类型控制并发事务之间的操作冲突,确保数据一致性,但也会影响系统的并发性能。在设计并发系统时,理解这些锁的行为对于性能优化和事务设计非常重要。

✨ 网站运行时间: 3年11月15天 ❤️ 道阻且长,行则将至 - 微信号: heikedreamer