为什么PostgreSQL的读未提交实际上等同于读已提交

来源:IOS教程作者:小团团头衔:草根站长
导读:本期聚焦于小团团创作的《为什么PostgreSQL的读未提交实际上等同于读已提交》,敬请观看详情。学过数据库理论的人都听说过脏读这个概念,即一个事务读到了另一个未提交事务修改的数据。但如果你在PostgreSQL中把隔离级别设置为读未提交,会发现它根本不会产生脏读,行为和读已提交完全一样。这不是实现上的偷懒,而是PostgreSQL基于MVCC多版本并发控制机制做出的有意设计。本文从标准SQL的四种隔离级别讲起,解释脏读、不可重复读、幻读三类异常的区别,再深入PostgreSQL内部元组版本与事务可见性判断的原理,说明为什么旧版本数据天然阻挡了脏读,最后给出查看和设置隔离级别的方法,以及选择隔离级别时的实际建议,帮助读者理解PostgreSQL并发控制的独特之处。

在标准SQL规范中,事务隔离级别一共有四种:读未提交、读已提交、可重复读和串行化。理论上讲,读未提交是隔离程度最低的一级,允许脏读发生,也就是说一个事务可以读取到另一个事务尚未提交的修改。然而在PostgreSQL中,无论你如何设置,读未提交这个级别都不会出现脏读,它的实际表现和读已提交一模一样。这背后其实是PostgreSQL多版本并发控制机制带来的自然结果,而不是一句“不支持”就能概括的。

为什么PostgreSQL的读未提交实际上等同于读已提交

标准SQL中的四种隔离级别与三类异常

要理解这个问题,先得弄清楚SQL标准定义的三类读异常。脏读指的是事务A读到了事务B未提交的数据,一旦B回滚,A读到的数据就成了从未真正存在过的“脏”数据,业务逻辑可能因此出错。不可重复读是指事务A内两次读取同一行,由于事务B在中间提交了修改,导致两次结果不一致。幻读则更隐蔽,事务A两次执行同一查询,事务B在中间插入并提交了新行,第二次查询凭空多出了“幻影”记录。

p>SQL标准用这三类异常来划分四种隔离级别:读未提交允许全部三种异常,读已提交禁止脏读,可重复读进一步禁止不可重复读,串行化则全部禁止。这个划分在锁实现的数据库里比较直观,因为锁可以精确控制“读的时候允不允许别人写”。但PostgreSQL走了另一条路,它用多版本并发控制代替了传统的读写锁。

PostgreSQL的MVCC如何天然阻挡脏读

PostgreSQL对数据的每一次修改都会产生一个新版本的元组,旧版本不会立刻被删除,而是继续保留在表中,依靠每行的头部字段来判断哪个版本对当前事务可见。每个元组记录着创建它的事务ID以及事务状态相关的字段,比如xmin标识插入该版本的事务,xmax标识删除或更新该版本的事务。判断可见性时,PostgreSQL会去检查这些事务ID对应的事务是否已经提交。

关键就在这里:可见性判断的代码逻辑只会把“已提交事务产生的版本”视为可见,进行中的事务无论多小都不会让自己的修改对别人可见。这个判断是硬编码在可见性检查机制中的,没有任何开关可以让一个事务读到另一个未提交事务写入的数据。也就是说,脏读在PostgreSQL的体系结构下根本无法发生,读未提交自然就没有存在的必要了。

可以在psql中做一个简单实验来验证这一点。先开启两个会话,在会话一中执行下面的事务:

-- 会话一
BEGIN;
-- 显式设置为读未提交,实际仍然是读已提交行为
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 此时不要提交,保持事务打开

接着在会话二中查询同一条记录:

-- 会话二
SHOW transaction_isolation;
SELECT balance FROM accounts WHERE id = 1;
-- 查到的仍然是修改前的旧值,不会读到未提交的100元扣减

会话二查到的是该行修改前的旧版本,因为新版本的xmin对应的事务尚未提交,可见性检查直接跳过它。等到会话一回滚,旧版本依旧有效;等到会话一提交,新版本才对其他事务可见,这正是读已提交的语义。

官方文档的说法与设置行为

PostgreSQL官方文档对此有明确说明:读未提交在PostgreSQL中的行为等同于读已提交,这是有意为之的设计。之所以保留这个级别名称,主要是为了兼容SQL标准和那些从其他数据库迁移过来的应用程序。如果一份代码里写了SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED,PostgreSQL不会报错,而是默默接受这个设置,然后按读已提交的方式工作。

可以用下面的语句查看和调整隔离级别:

-- 查看当前会话的隔离级别
SHOW transaction_isolation;

-- 设置当前事务的隔离级别
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 设置整个会话的默认隔离级别
SET session transaction_isolation = 'serializable';

即使你把级别设成read uncommitted,执行SHOW时显示的仍然是read uncommitted,但底层可见性行为没有任何变化。这一点和Oracle对read committed的处理有相似之处:各家数据库在兼容标准的同时,都会基于自身架构调整实际行为,SQL标准的隔离级别定义并非在所有产品上都严格对应。

实际开发中如何选择隔离级别

既然读未提交名存实亡,PostgreSQL使用者实际面对的选择就只有三种:读已提交、可重复读和串行化。读已提交是默认级别,每条语句开始时获取一个新的快照,并发性能好,适合大多数Web应用。但它可能出现不可重复读,同一个事务里两次查询结果不同,写业务逻辑时要留意。

可重复读在事务开始时固定快照,整个事务内看到的数据完全一致,配合PostgreSQL的实现还能防止幻读。不过要注意,并发的更新可能导致序列化失败,事务被中断后需要应用层重试。串行化则基于可序列化快照隔离技术,能检测出写倾斜等更复杂的异常,代价是冲突时必须重试事务,适合对一致性要求极高的金融类场景。

给一个实用的建议:不要依赖“降低隔离级别换取性能”的思路,因为在PostgreSQL里读未提交本来就是读已提交,不存在更低的开销档位。真正需要权衡的是默认的读已提交和更强的可重复读、串行化之间的取舍。理解了MVCC的可见性原理,也就理解了PostgreSQL为什么敢于宣称自己不存在脏读,这比单纯背诵隔离级别表格要有价值得多。

PostgreSQL事务隔离级别读已提交修改时间:2026-09-16 01:54:29

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0916/57639.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。