LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

为什么你的 SELECT 有时会阻塞?

zhenglin
2026年1月8日 15:21 本文热度 385

一、基本概念:MySQL 的两种“读”法

在 Mysql 中,并非所有 SELECT 都一样。根据是否加锁、是否读最新数据,分为两类:

快照读(Snapshot Read)

  • 定义:读取事务开始时(或语句开始时)的一致性视图(Read View),不加任何锁

  • 典型语句

  SELECT * FROM orders WHERE user_id = 1001;

  • 底层机制:基于 MVCC(多版本并发控制),通过 undo log 构建历史版本。

  • 特点

    • 读不阻塞写,写不阻塞读;

    • 在 RR 隔离级别下,整个事务看到同一快照(可重复读);

    • 在 RC 级别下,每次 SELECT 都生成新快照(不可重复读)


当前读(Current Read)

  • 定义:读取数据库中最新的、已提交的数据,并加锁以防止其他事务修改。

  • 典型语句

  SELECT * FROM orders WHERE user_id = 1001 FOR UPDATE;        -- 排他锁

  SELECT * FROM orders WHERE user_id = 1001 LOCK IN SHARE MODE; -- 共享锁(MySQL 8.0+ 可用 FOR SHARE)

  UPDATE orders SET status = 'paid' WHERE id = 123;

  DELETE FROM orders WHERE id = 123;

  • 底层机制:直接访问聚簇索引或二级索引的最新记录,并根据隔离级别加 记录锁 / 间隙锁(RR) / Next-Key Lock

  • 特点

    • 会阻塞其他写操作(甚至读操作,取决于锁类型)

    • 是实现“悲观锁”和防止并发冲突的关键手段。



快照读 = 安静地看历史;当前读 = 大声宣布“我要改这里,请别动!”


二、使用场景:什么时候该用哪种读?

场景 1:只读查询 → 用快照读

  • 用户查看订单列表、商品详情等;

  • 对数据一致性要求不高,或能接受“稍旧”数据;

  • 优势:零锁开销,高并发无压力。


场景 2:先查后改(Check-Then-Act)→ 必须用当前读!

// 伪代码:错误示范(快照读)

Order order = select("SELECT * FROM orders WHERE id = 123"); // 快照读

if (order.status == "unpaid") {

    update("UPDATE orders SET status = 'paid' WHERE id = 123");

}

 问题:两个线程同时执行,都看到 status=unpaid,导致重复支付!

正确做法(当前读):

-- 加锁读取最新状态(仅用来体现当前读的作用,高并发场景下不建议使用 FOR UPDATE)

SELECT * FROM orders WHERE id = 123 FOR UPDATE;

-- 再判断并更新


场景 3:防止幻读(RR 级别下)

  • 业务要求“范围内不能有新数据插入”,如库存扣减、唯一编号生成;

  • 必须用 SELECT ... FOR UPDATE 触发 Next-Key Lock(记录 + 间隙锁)

  • 否则即使快照读看不到新数据,别人仍可插入,破坏业务逻辑。


三、避坑指南

问题 1:FOR UPDATE 导致大量阻塞甚至死锁

  • 原因:范围查询未走索引,InnoDB 在 RR 下对主键全表加间隙锁;

  • 案例

  SELECT * FROM orders WHERE create_time > '2024-01-01' FOR UPDATE; -- create_time 无索引



锁住整个表,所有 INSERT 被阻塞! 解决方案

  • 确保 WHERE 条件命中索引;

  • 高并发场景考虑降级到 READ COMMITTED(只加记录锁,不加间隙锁);

  • 避免大范围扫描,改用分页或等值查询。



问题 2:快照读 + 当前读混合,误判“幻读”

  • 现象

  -- 事务内

  SELECT COUNT(*) FROM t WHERE id > 10;               -- 快照读,返回 0

  SELECT COUNT(*) FROM t WHERE id > 10 FOR UPDATE;   -- 当前读,返回 1!

  • 误解:“RR 下怎么还有幻读?”

  • 真相:这不是幻读!快照读看历史,当前读看现在,两者本就不该一致。

解决方案

  • 统一读取模式:要么全用快照读(接受只读一致性),要么关键路径全用当前读;

  • 不要用快照读做业务判断后再用当前读更新。



问题 3:RC 级别下 FOR UPDATE 无法防止幻读

  • 现象:在 RC 下,即使用了 FOR UPDATE,别人仍可插入新数据;

  • 原因:RC 不使用间隙锁,只锁已有记录;

  • 影响:如“查无此用户 → 插入”可能失败(唯一键冲突)。


解决方案

  • 依赖 数据库唯一索引 作为最终兜底;

  • 应用层做好异常捕获与重试(如捕获 Duplicate entry);

  • 核心链路若需强一致性,保留 RR + 精准加锁。



快照读和当前读,是 InnoDB 实现高性能与一致性平衡的双翼。

  • 快照读 让读操作如丝般顺滑;

  • 当前读 为写操作筑起安全防线。


但在实际开发中,最大的风险不是技术本身,而是“不知道自己在用哪种读”

下次当你写下 SELECT 时,不妨多想一步 “我需要的是历史快照,还是此刻的真实?”


参考文章:原文链接


该文章在 2026/1/8 15:23:12 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved