Skip to content

where name > "test1" 会加锁吗?为什么?

约 747 字大约 2 分钟

MySQL字节

2025-03-20

⭐ 题目日期:

字节 - 2024/12/17

📝 题解:

在 MySQL 中,WHERE name > "test1" 是否加锁取决于 查询是否显式加锁隔离级别。以下是详细分析:


1. 普通查询(不加锁)

如果执行普通的 SELECT 查询(不显式加锁):

SELECT * FROM users WHERE name > "test1";
  • 不会加锁,其他事务可以自由插入、修改或删除数据。
  • 可能发生幻读:其他事务插入 name > "test1" 的新记录时,当前事务再次查询会出现幻读。

2. 显式加锁的查询

如果通过 FOR UPDATELOCK IN SHARE MODE 显式加锁:

SELECT * FROM users WHERE name > "test1" FOR UPDATE;

此时加锁行为如下:

2.1 锁的类型与范围

  • 锁类型:Next-Key Lock(行锁 + 间隙锁)。
  • 锁范围
    取决于 name 字段是否有索引:
    • (1) name 有索引
      • 锁定所有满足 name > "test1"现有记录间隙
      • 例如:若表中存在 name = "test2"name = "test3",则锁定 ("test1", "test2")("test2", "test3") 等间隙。
    • (2) name 无索引
      • 退化为表级锁,锁定整个表的所有记录和间隙。
      • 性能极差,并发操作会被完全阻塞。

2.2 示例验证

假设表结构:

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50) INDEX idx_name  -- name 字段有索引
);

现有数据:

id | name
---+-------
1  | "test1"
2  | "test2"
3  | "test3"
  • 事务 A 执行:
    BEGIN;
    SELECT * FROM users WHERE name > "test1" FOR UPDATE;
    • 锁定 name > "test1" 的范围(("test1", "test2")("test2", "test3") 等间隙)。
  • 事务 B 尝试插入 name = "test4"
    INSERT INTO users (id, name) VALUES (4, "test4");  -- 被阻塞
    • 因为 "test4" > "test1",属于锁定范围。

3. 为什么需要索引才能精确加锁?

  • 索引的作用
    Next-Key Lock 依赖索引确定锁定范围。若字段无索引,无法定位间隙,只能锁全表。
  • 性能对比
    场景加锁范围并发性能适用场景
    有索引精确锁定范围高频范围查询
    无索引锁定全表极低几乎不推荐使用

4. 总结

问题答案
普通查询是否加锁?不加锁,可能发生幻读。
显式加锁(如 FOR UPDATE)是否加锁?加锁,锁类型为 Next-Key Lock(有索引)或表锁(无索引)。
为什么有时加锁失败?无索引时退化为表锁,但需注意性能问题和锁冲突。

5. 建议

  1. 显式加锁场景
    • 若需避免幻读,使用 SELECT ... FOR UPDATE 并确保查询字段有索引。
    • 示例:
      -- name 字段需有索引
      SELECT * FROM users WHERE name > "test1" FOR UPDATE;
  2. 索引优化
    • 为高频查询的字段(如 name)添加索引,避免全表锁。
  3. 隔离级别选择
    • 默认的 REPEATABLE READ 配合 Next-Key Lock 可解决幻读,但需合理设计索引。

最终结论

  • WHERE name > "test1" 的查询 仅在显式加锁时才会加锁,锁的范围取决于是否有索引。
  • 无索引时加锁会导致性能灾难,务必通过索引优化锁的粒度!