当存在可以引起冲突的并发插入时,如何在postgres中进行条件插入?

问题描述 投票:0回答:1

我正在尝试编写一个实验框架,用户可以在其中基于location-idstime安排一些实验。

我的表架构看起来像:

TABLE experiment (
    id INT NOT NULL PRIMARY KEY,
    name varchar(20) NOT NULL,
    locationIds varchar[] NOT NULL,
    timeStart timestamp NOT NULL,
    timeEnd timestamp NOT NULL,
    createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
)

存在插入操作,但条件是位置和时间不应重叠。我想知道在位置或时间重叠的情况下,当有两个并发插入发生时如何避免数据状态不一致。

理想情况下,我希望插入操作之一成功,但是如果两者都失败,并且应用程序应该重试,那很好。

我尝试思考的方法很少:

方法:

APPROACH-1

  1. 具有enable列,该列告诉某些条目是否有效是否。
  2. 我用enable = FALSE

  3. 插入实验计划条目
  4. 然后,我检查是否还有其他已启用且已与当前插入重叠。

  5. IF

  6. 有这样的条目,那么我什么也不做,那个实验不是预定的。 Else我将条目更新为enable = TRUE

    问题:如果存在并发冲突的插入,那么当两个人都清除了步骤3时,两者都会得到enable = TRUE

[我考虑过是否让事务隔离级别处于未提交状态,那么我也无法区分正在处理的和已经在进行的操作[[enable = TRUE

然后我想,如果我将enable标记为枚举[IN_PROGRESS,ENABLED,DISABLED],则方法将如下所示。

APPROACH-2

  1. 具有

    enable

    列,该列告诉某些条目是否为[IN_PROGRESS,ENABLED,DISABLED]]]
  • 我在

    enable = IN_PROGRESS

    中插入实验时间表条目,
  • 然后检查是否有其他任何条目,这些条目是

    enable = ENABLED

    enable = IN_PROGRESS
  • 并与当前插入物重叠。
  • IF

    有这样的条目,然后我更新enable = DISABLED
  • 并且该实验不是预定的。 Else我将条目更新为enable = ENABLED
    问题:如果存在并发冲突的插入,那么当两个人都清除了步骤3并获得了这样的重叠条目时,两者都会得到

    enable = DISABLED。如果事务隔离级别为

    READ-COMMITTED,则只有在每个步骤都是一个事务的情况下才起作用,而不是将整个过程视为一个事务。如果事务隔离级别为READ-UNCOMMITTED,则可以将其作为一个事务处理,而将DISABLED状态也可以作为ROLLBACK步骤。

    APPROACH-3

    在我使用POSTGRES时使用基于触发器的解决方案,我可以为每个插入操作添加一个触发器,在插入后检查该重叠条目,如果没有,则将行更新为具有[[enable = TRUE] >

    CREATE OR REPLACE FUNCTION enable_if_unique() RETURNS TRIGGER AS $$ BEGIN IF (TG_OP = 'INSERT') THEN UPDATE experiment SET NEW.enable=true WHERE (SELECT count(1) FROM experiment WHERE enable= true AND location_Ids && OLD.location_ids AND (OLD.timeStart, OLD.timeEnd) OVERLAPS (timeStart, timeEnd) ) = 0; RETURN NEW; END IF; END; $$ LANGUAGE 'plpgsql';

    CREATE TRIGGER enable_if_unique_trigger BEFORE INSERT ON experiment FOR EACH ROW EXECUTE PROCEDURE enable_if_unique();
    我不确定方法3,因为我认为对于每个插入操作,它都需要触发器以串行方式进行操作,以便实际上启用了一个实验,而其余的重叠部分则被禁用。

    APPROACH-4

    从在线搜索中寻找其他可能的解决方案,我看到使用Select语句和WHERE子句占用的插入内容有助于添加所需条件。INSERT INTO experiment(id, name, locationIds, timeStart, timeEnd) SELECT 1, 'exp-1', ARRAY[123,234,345], '2020-03-13 12:00:00' WHERE ( SELECT count(1) FROM EXPERIMENT WHERE enable= true AND location_Ids && OLD.location_ids AND (OLD.timeStart, OLD.timeEnd) OVERLAPS (timeStart, timeEnd) ) = 0;

    我觉得仍然存在一致性问题的可能性,因为两个并发操作将无法在检查约束的SELECT语句中读取每个。

    最终方法:方法2

    我想知道以下事情:

      在可伸缩性和高吞吐量方面,哪种方法最好?
    1. 哪种方法实际上是在确保保持数据一致性?

  • [我可以在这里使用和错过的任何其他方法!

  • POSTGRES的新手,将了解示例或链接

  • 我正在尝试编写一个实验框架,用户可以在该框架中根据位置ID和时间安排一些实验。我的表架构如下所示:TABLE实验(id INT NOT NULL ...

    如@a_horse_with_no_name所提及
    我们可以使用排除约束:

    -- this prevents overlaps in the locationids AND the time range alter table experiment add constraint no_overlap exclude using gist (locationids with &&, tsrange(timestart, timeend) with &&);

    postgresql triggers transactions sql-insert distributed-transactions
    1个回答
    0
    投票
    我们可以使用排除约束:

    -- this prevents overlaps in the locationids AND the time range alter table experiment add constraint no_overlap exclude using gist (locationids with &&, tsrange(timestart, timeend) with &&);

    © www.soinside.com 2019 - 2024. All rights reserved.