事务隔离级别选错了,数据可能被“吞”掉——从脏读到幻读,一次讲透

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介: 事务隔离级别是数据库并发控制的核心机制,但很多开发者和DBA对脏读、不可重复读、幻读的区别一知半解,遇到问题只能“加锁试试”。本文从四个隔离级别出发,用真实SQL案例讲透三种并发问题的本质差异,对比MySQL与PostgreSQL在默认隔离级别上的不同选择,并结合业务场景给出选型建议,帮助读者写出更可靠的事务代码。

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

你有没有遇到过这种场景:一个事务里查了两次同样的数据,结果不一样;或者查了两次同样的范围条件,结果集多了一行。很多开发遇到这种情况第一反应是“是不是缓存有问题”,但其实大概率是事务隔离级别没选对。

事务隔离级别决定了多个事务同时运行时,彼此能看到多少对方的数据。选得太低,数据可能被“污染”;选得太高,并发性能会急剧下降。今天我们从四个隔离级别出发,把脏读、不可重复读、幻读这三个概念彻底讲透。

一、先搞懂四个隔离级别

SQL标准定义了四种隔离级别,从低到高依次是:

1. READ UNCOMMITTED(读未提交) ——最低级别。事务可以读取其他事务尚未提交的数据。几乎不提供并发控制,实际生产中极少使用。

2. READ COMMITTED(读已提交,简称RC) ——只能读取其他事务已经提交的数据。避免了脏读,但无法避免不可重复读。

3. REPEATABLE READ(可重复读,简称RR) ——保证同一事务内多次读取同一数据结果一致。MySQL InnoDB的默认隔离级别。

4. SERIALIZABLE(可串行化) ——最高级别,事务完全串行执行,避免所有并发问题,但性能开销最大。

不同隔离级别能解决的问题,可以浓缩成一张表:

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED 可能 可能 可能
READ COMMITTED 避免 可能 可能
REPEATABLE READ 避免 避免 InnoDB基本避免
SERIALIZABLE 避免 避免 避免

二、三种并发问题,用真实SQL讲清楚

脏读:读到别人“还没想好”的数据

脏读指的是一个事务读取了另一个事务尚未提交的修改。如果那个事务最终回滚了,读到的就是“脏数据”——从未真正存在过的数据。

用电商库存场景来理解:事务A开始扣减库存,把商品X的库存从100改为99,但还没提交;此时事务B查询库存,读到了99。如果事务A因支付失败回滚了,库存恢复为100,但事务B已经基于99做了后续操作——超卖了。

-- 事务A(未提交)
START TRANSACTION;
UPDATE products SET stock = stock - 1 WHERE id = 1;  -- stock从100变99
-- 此时事务B读取到stock=99(未提交的数据)
-- 事务A回滚
ROLLBACK;  -- stock恢复为100,但事务B已经用了99的数据

解决脏读,至少要用READ COMMITTED级别。

不可重复读:同一事务内,数据“变了”

不可重复读指在同一个事务内,多次读取同一数据时结果不一致。它与脏读的关键区别在于:脏读读的是未提交的数据,不可重复读读的是已提交的数据。

用财务场景理解:财务人员在事务A中查询员工张三的工资,第一次看到5000元;此时HR在事务B中给张三加薪至6000元并提交;事务A再次查询时,工资变成了6000元。同一个事务里两次查询结果不一样——这就是不可重复读。

-- 事务A(隔离级别READ COMMITTED)
START TRANSACTION;
SELECT salary FROM employees WHERE id = 1;  -- 返回5000

-- 事务B(同时执行)
START TRANSACTION;
UPDATE employees SET salary = 6000 WHERE id = 1;
COMMIT;  -- 提交修改

-- 事务A再次查询
SELECT salary FROM employees WHERE id = 1;  -- 返回6000,和第一次不一样
COMMIT;

READ COMMITTED级别无法避免这个问题。需要升级到REPEATABLE READ。

幻读:结果集“凭空多了一行”

幻读指在同一个事务内,多次查询某个范围的数据时,结果集的数量发生了变化——第一次查有5行,第二次查变成了6行。

打个比方:你在图书馆按分类找书,第一次看到书架上有5本书;等你转了一圈回来再数,变成了6本——多出来那本就是你不在的时候别人放上去的。

-- 事务A(隔离级别REPEATABLE READ)
START TRANSACTION;
SELECT * FROM orders WHERE status = 'pending';  -- 返回5行

-- 事务B(同时执行)
START TRANSACTION;
INSERT INTO orders (id, status) VALUES (100, 'pending');
COMMIT;

-- 事务A再次查询
SELECT * FROM orders WHERE status = 'pending';  -- 返回6行,多了一行!
COMMIT;

三、MySQL的RR为什么能“基本避免”幻读?

根据SQL标准,REPEATABLE READ级别应该允许幻读。但MySQL InnoDB通过MVCC(多版本并发控制)+ Gap Lock(间隙锁) 的组合,在大多数场景下实际避免了幻读。

  • MVCC​:事务开始时拍一次快照,后面的普通SELECT都读这个快照,别人提交了也看不见
  • Gap Lock(间隙锁) :锁定索引记录之间的空隙,阻止其他事务在范围内插入新行

但需要注意的是,​DML语句(UPDATE/DELETE)不遵循快照读​,可能看到其他事务刚提交的行。所以在某些边缘场景下,RR级别仍可能出现幻读。

四、MySQL vs PostgreSQL:默认隔离级别为什么不同?

这是一个容易被忽略但很重要的差异。

  • MySQL InnoDB默认使用REPEATABLE READ(RR)
  • PostgreSQL默认使用READ COMMITTED(RC)

为什么不同?因为两者的MVCC实现机制不同:

对比维度 MySQL InnoDB(RR) PostgreSQL(RC)
快照时机 事务开始时拍一次快照 每条SELECT重新生成快照
不可重复读 避免 RC级别会出现
幻读 基本避免(MVCC+间隙锁) RC级别会出现
适用场景 高并发CRUD 复杂查询、数据分析

MySQL选择RR作为默认,是为了在并发性能和数据一致性之间取得平衡,适合大多数互联网业务场景。PostgreSQL选择RC作为默认,则是因为它在RR级别下对幻读的处理更加严格,可能导致更多死锁。

五、业务场景中怎么选隔离级别?

业务场景 推荐级别 理由
金融账务、库存扣减 SERIALIZABLE 或 RR 数据一致性要求极高
电商订单、用户中心 RR(MySQL默认) 平衡性能与一致性
报表查询、数据分析 RC 查询为主,可接受不可重复读
高并发日志写入 RC 或 READ UNCOMMITTED 写入为主,对一致性要求低

选型原则​:不是隔离级别越高越好。隔离级别越高,并发性能越低。应该根据业务对一致性和性能的要求做权衡。

六、实战避坑:两个常见的隔离级别陷阱

陷阱1:在RC级别下做“先查后改”

很多业务逻辑是“先查询状态,再根据状态做更新”。在RC级别下,两次查询之间数据可能被其他事务修改,导致“查的时候是A,改的时候已经变成B了”——数据被“吞”了。解决方案:要么用RR级别,要么在查询时加FOR UPDATE锁住数据。

陷阱2:RR级别下的间隙锁导致死锁

RR级别通过间隙锁避免幻读,但间隙锁也是死锁的常见来源。在RR级别下做范围删除或范围更新,可能锁住大量不存在的“间隙”,多个事务互相等待形成死锁。如果业务场景不需要防止幻读,可以考虑降级到RC级别,牺牲一点一致性换取更高的并发性能。

总结

事务隔离级别是数据库并发控制的核心机制,直接影响数据的正确性和系统的吞吐量。理解脏读、不可重复读、幻读的本质差异,知道MySQL为什么选择RR、PostgreSQL为什么选择RC作为默认,你就能在业务场景中做出更合理的选择,写出更可靠的事务代码。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

相关文章
|
2天前
|
云安全 人工智能 运维
阿里云SecOps Agent,全新安全跨产品执行体验
自然语言驱动 云安全中心/WAF/CFW/ 等多款安全产品联动
1583 2
|
2天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
482 2
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
13天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
14天前
|
JavaScript 定位技术 API
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
CodeGraph 是一款爆火的本地代码智能工具,通过 tree-sitter 解析 AST 构建结构化知识图谱(存于 SQLite),为编程 Agent 提前生成“代码地图”。它显著降低 Agent 在中大型项目中的探索成本——实测工具调用减少71%、Token 降57%、速度提升46%,支持19+语言及主流框架路由识别,完全离线、无需 API Key。
874 11
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
|
2天前
|
数据采集 人工智能 搜索推荐
企业智能体的下半场,如何让智能体越用越聪明?
AgentLoop 正在邀测期,点击申请邀测资格。
192 124
|
14天前
|
人工智能 运维 JavaScript
阿里云Qoder CN(原通义灵码)全解析 产品形态、版本划分与技术适配说明
在AI辅助开发与智能办公工具持续普及的当下,阿里云旗下原通义灵码正式更名为Qoder CN,同时延伸出QoderWork CN、Qoder CN CLI、Qoder CN Mobile等多款配套产品,形成覆盖代码开发、日常办公、终端交互、移动端使用的完整工具矩阵。Qoder CN核心定位为AI智能编码助手,深度适配主流代码编辑器、集成开发环境以及终端场景;QoderWork CN则偏向桌面端综合办公辅助,二者面向不同使用场景,划分了多个版本档位,搭配差异化资源配额、功能权限与计费规则,同时兼容多款主流大模型。
940 8
|
9天前
|
人工智能 自然语言处理 算法
阿里云百炼Qwen 3.7 Plus与Max实测全解:性价比与多模态能力、成本深度对比
2026年,阿里云百炼平台推出的Qwen 3.7系列成为企业与开发者落地AI应用的核心选择,其中Qwen 3.7 Max与Plus作为两大旗舰版本,定位差异显著:Max是纯文本推理旗舰,专注高强度智能体与复杂逻辑任务;Plus则是多模态全能版,在保留强大文本能力的同时,补齐图像、视频理解能力,且价格大幅降低。本文基于2026年最新实测数据,从核心参数、文本能力、多模态能力、智能体表现、性价比与场景选型六大维度,全面解析两款模型的差异,为用户提供精准选型参考。
469 0
|
14天前
|
JSON 缓存 安全
通过 CC Switch 本地路由让 Codex CLI 接入 DeepSeek 等第三方模型
CC Switch 通过本地路由(`127.0.0.1:15721`)实现协议转换:将 Codex 的 Responses API 请求自动映射为 DeepSeek 等厂商的 Chat Completions 接口,兼容流式响应与工具调用,无需修改 Codex 源码,安全隔离 API Key。(239字)
2561 7
通过 CC Switch 本地路由让 Codex CLI 接入 DeepSeek 等第三方模型