PAYATHON 2026

并发追加 JSONB 数组时,UPDATE ... RETURNING 会包含后续更新吗?

数据小许

结论

在 PostgreSQL 默认的 Read Committed 隔离级别下,题目中 A、B 对同一行执行的这两次 UPDATE 会按顺序生效。A 的 RETURNING 返回 A 更新后的行版本,不会包含 B 后续追加的值。A 的事务结束后,B 基于已提交的新行版本执行更新,并返回包含两次追加的行。

因此,若初始值为 [],且 A 先取得该行的更新锁,通常 A 返回 [thumb1],B 返回 [thumb1, thumb2]。这是 PostgreSQL 在该隔离级别下处理同一行并发更新的方式。

并发更新时发生什么

PostgreSQL 文档说明,UPDATE 找到目标行后,如果该行已被另一个事务更新,就会等待对方提交或回滚。若对方提交,当前更新会尝试作用于更新后的行版本,并重新检查 WHERE 条件。RETURNING 默认返回本条 UPDATE 更新后的列值。PostgreSQL:事务隔离;PostgreSQL:UPDATE

套用到这个例子:

A:[] → [thumb1],RETURNING 得到 [thumb1]
B:等待 A 完成
B:以 [thumb1] 为基础更新为 [thumb1, thumb2],RETURNING 得到 [thumb1, thumb2]

B 拿到行锁后,会使用最新已提交的目标行版本。因此,两个追加不会因为都基于旧值 [] 计算而互相覆盖。RETURNING 返回的是这条 UPDATE 实际更新的行值,不是之后重新读取到的最新状态。

隔离级别的影响

这个结论适用于 PostgreSQL 默认的 Read Committed。若事务使用 Repeatable Read,而事务快照中的目标行之后被另一个事务更新并提交,后续更新可能因并发更新而报序列化失败。应用需要回滚并重试整个事务。使用 Serializable 时,应用也需要处理序列化失败。PostgreSQL:事务隔离

EF Core 中的观察

核对时要区分数据库返回的行值和应用最终读取到的 EF Core 实体。如果同一个 DbContext 已跟踪该实体,跟踪状态和实体复用可能影响你观察到的对象状态。要确认数据库返回的行值,可以在独立上下文中检查,或使用不跟踪查询,并确认实际事务隔离级别。

压力测试有助于发现实现问题,但不能单独证明数据库语义。这里的保证依据是 PostgreSQL 对并发 UPDATE 和 RETURNING 的定义。

备注:内容仅供参考。