Postgres 中带有嵌套 SELECT 子句的原子更新

Atomic UPDATE with nested SELECT clause in Postgres

我有一个集群,其中一个节点是主节点。 synchronization/decision 确定谁将成为新主人是通过数据库完成的(通过 java/spring/jpa 访问 Postgres)。

这是我目前拥有的

@Repository
public interface ServiceRepository{


@Transactional
@Modifying
@Query("UPDATE ServiceInstance e SET e.master=true"
        + " where e.id=:serviceId AND NOT"
        + " EXISTS (SELECT master from ServiceInstance where master = true)")
int tryToBecomeMaster(@Param("serviceId") String serviceId);

对于集群中的每个节点,数据库中都有一行 ServiceInstance。每个节点都试图更新自己的行,但前提是没有行已标记为 'master'。该查询是每个节点定期 运行。

该查询是 atomic 还是存在竞争条件?如果它不是原子的,我在这里需要什么 t运行saction 隔离级别?

我主要担心的是,如果允许并行进行 2 个查询,它们可能会将 SELECT 子句计算为假(无主)并更新它们的行。

如果你想确保你的 SELECT 子句不能被并行读取,你可以使用 SELECT FOR UPDATE 和聚合函数来检测 master 而不是 WHERE条款;这将自动锁定 table 的所有行,直到 UPDATE 完成。

另一种方法是手动执行 LOCK TABLE EXCLUSIVE 命令。

两者都应该适合您的情况。

为了确保只能有一个master,在table上创建一个唯一的部分索引:

CREATE UNIQUE INDEX ON serviceinstance ((1)) WHERE master;

一旦您尝试将 master 设置为 TRUE 以获得 table 中的多行,这将给您一个错误,并且 ACID 的 C 保证(一致性)将确保这始终有效。您必须在函数中捕获并处理异常。

请注意,这不是实现高可用性集群的好方法:它在数据库的形式上存在单点故障。