在标准SQL规范中,事务隔离级别一共有四种:读未提交、读已提交、可重复读和串行化。理论上讲,读未提交是隔离程度最低的一级,允许脏读发生,也就是说一个事务可以读取到另一个事务尚未提交的修改。然而在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