You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL Serializable隔离级别工作原理及测试异常疑问

为什么Serializable隔离级别下你的并行事务没触发报错?

问题根源:对Serializable隔离级别的误解

你以为Serializable会强制事务严格串行执行,只要并发就会报错,但实际上PostgreSQL的Serializable隔离级别是乐观并发控制——它允许事务并发执行,只在提交阶段检查是否存在串行化异常(即无法找到一个串行执行顺序来等价于当前并发执行的结果),只有存在异常时才会回滚其中一个事务。

你的示例中,两个事务的操作不会产生串行化异常:

  • transaction1是只读事务,它读取的是事务启动时的快照数据;
  • transaction2是写事务,它更新目标行并提交。

这两个事务的并发执行,可以对应两种合法的串行执行顺序:

  1. 先执行transaction1(读旧值),再执行transaction2(更新值);
  2. 先执行transaction2(更新值),再执行transaction1(读新值)。

两种串行顺序的结果都是合理的,PostgreSQL不会检测到冲突,因此不会触发报错。

触发Serializable冲突的正确示例

要触发串行化报错,需要构造存在写偏斜或基于快照读的更新冲突的场景。比如两个事务都基于同一个初始值进行更新:

transaction1.py

import psycopg2
import time

conn = psycopg2.connect("dbname=your_db user=your_user password=your_pwd")
cursor = conn.cursor()

cursor.execute("START TRANSACTION ISOLATION LEVEL SERIALIZABLE;")
# 读取初始余额
cursor.execute("SELECT balance FROM customers WHERE name = '1'")
balance = cursor.fetchall()[0][0]
# 模拟业务处理延迟
time.sleep(10)
# 基于读取的值更新
cursor.execute("UPDATE customers SET balance = %s WHERE name = '1'", (balance + 50,))
cursor.execute("COMMIT")

cursor.close()
conn.close()

transaction2.py

import psycopg2
import time

conn = psycopg2.connect("dbname=your_db user=your_user password=your_pwd")
cursor = conn.cursor()

cursor.execute("START TRANSACTION ISOLATION LEVEL SERIALIZABLE;")
# 读取同一个初始余额
cursor.execute("SELECT balance FROM customers WHERE name = '1'")
balance = cursor.fetchall()[0][0]
# 模拟业务处理延迟
time.sleep(10)
# 基于读取的值更新
cursor.execute("UPDATE customers SET balance = %s WHERE name = '1'", (balance + 100,))
cursor.execute("COMMIT")

cursor.close()
conn.close()

先运行transaction1,再运行transaction2,当第二个事务提交时,PostgreSQL会检测到串行化冲突,抛出类似如下的错误:

psycopg2.errors.SerializationFailure: could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

补充说明

PostgreSQL的Serializable隔离级别依赖快照隔离+串行化冲突检测:

  • 读操作使用快照,不会阻塞写操作;
  • 写操作会获取行级锁,但不会阻塞快照读;
  • 只有当并发事务的执行逻辑无法对应任何串行执行顺序时,才会在提交时触发回滚。

内容的提问来源于stack exchange,提问作者blablatron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 07:33:38