-- 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 &&);
我正在尝试编写一个实验框架,用户可以在其中基于location-ids和time安排一些实验。
我的表架构看起来像:
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
我用enable = FALSE
然后,我检查是否还有其他已启用且已与当前插入重叠。
IF
问题:如果存在并发冲突的插入,那么当两个人都清除了步骤3时,两者都会得到enable = TRUE
[我考虑过是否让事务隔离级别处于未提交状态,那么我也无法区分正在处理的和已经在进行的操作[[enable = TRUE
然后我想,如果我将enable标记为枚举[IN_PROGRESS,ENABLED,DISABLED],则方法将如下所示。APPROACH-2
enable
列,该列告诉某些条目是否为[IN_PROGRESS,ENABLED,DISABLED]]]enable = IN_PROGRESS
中插入实验时间表条目,enable = ENABLED
或enable = IN_PROGRESSIF
有这样的条目,然后我更新enable = DISABLEDenable = DISABLED。如果事务隔离级别为 READ-COMMITTED,则只有在每个步骤都是一个事务的情况下才起作用,而不是将整个过程视为一个事务。如果事务隔离级别为READ-UNCOMMITTED,则可以将其作为一个事务处理,而将DISABLED状态也可以作为ROLLBACK步骤。 APPROACH-3
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
我想知道以下事情:
哪种方法实际上是在确保保持数据一致性?
[我可以在这里使用和错过的任何其他方法!
POSTGRES的新手,将了解示例或链接
-- 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 &&);
-- 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 &&);