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 保证(一致性)将确保这始终有效。您必须在函数中捕获并处理异常。
请注意,这不是实现高可用性集群的好方法:它在数据库的形式上存在单点故障。
我有一个集群,其中一个节点是主节点。 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 保证(一致性)将确保这始终有效。您必须在函数中捕获并处理异常。
请注意,这不是实现高可用性集群的好方法:它在数据库的形式上存在单点故障。