当可能存在可能造成冲突的并发插入时,如何在 postgres 中进行条件插入?
how can I do conditional insert in postgres when there can be concurrent inserts that can create conflict?
我正在尝试编写一个实验框架,用户可以根据 location-ids 和 time.[=14 安排一些实验=]
我的 table 模式看起来像:
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
)
有插入操作要完成,条件是位置和时间不应重叠。
我想知道当在位置或时间重叠的地方占用 2 个并发插入时,可以做些什么来避免数据状态不一致,
理想情况下,我希望其中一个插入成功,但我很好,如果两者都失败并且应用程序应该重试。
很少有人接近我试图思考:
方法:
方法-1
- 有一个 enable 列来告诉某个条目是否有效
或者不是。
我插入带有 enable=FALSE
的实验计划条目
然后我检查是否有任何其他条目已启用并且是
与当前插入重叠。
如果有这样的条目那我什么都不做那个实验不行
预定。 Else 我将条目更新为 enable=TRUE.
问题:如果存在并发的冲突插入,那么当两者都清除步骤 3 时,两者都会得到 enable=TRUE。
我想过如果我让事务隔离级别是未提交的,那么我也无法区分处理中的和已经处理的enable=TRUE
然后我想,如果我将启用标记为枚举 [IN_PROGRESS, ENABLED, DISABLED] 那么方法将如下所示。
方法-2
有一个 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 步骤处理。
方法 3
使用基于触发器的解决方案,因为我正在使用 POSTGRES,我可以为每个插入操作添加一个触发器,post 在我检查重叠条目的地方插入,如果有 none,那么我将行更新为 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,因为我觉得它需要触发器以串行方式为每个插入操作执行操作,以便实际上启用其中一个实验,同时禁用其余的重叠实验。
方法-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 新手,将欣赏示例或链接
如@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 &&);
我正在尝试编写一个实验框架,用户可以根据 location-ids 和 time.[=14 安排一些实验=]
我的 table 模式看起来像:
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
)
有插入操作要完成,条件是位置和时间不应重叠。 我想知道当在位置或时间重叠的地方占用 2 个并发插入时,可以做些什么来避免数据状态不一致,
理想情况下,我希望其中一个插入成功,但我很好,如果两者都失败并且应用程序应该重试。
很少有人接近我试图思考:
方法:
方法-1
- 有一个 enable 列来告诉某个条目是否有效 或者不是。
我插入带有 enable=FALSE
的实验计划条目
然后我检查是否有任何其他条目已启用并且是 与当前插入重叠。
如果有这样的条目那我什么都不做那个实验不行 预定。 Else 我将条目更新为 enable=TRUE.
问题:如果存在并发的冲突插入,那么当两者都清除步骤 3 时,两者都会得到 enable=TRUE。
我想过如果我让事务隔离级别是未提交的,那么我也无法区分处理中的和已经处理的enable=TRUE
然后我想,如果我将启用标记为枚举 [IN_PROGRESS, ENABLED, DISABLED] 那么方法将如下所示。
方法-2
有一个 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 步骤处理。
方法 3
使用基于触发器的解决方案,因为我正在使用 POSTGRES,我可以为每个插入操作添加一个触发器,post 在我检查重叠条目的地方插入,如果有 none,那么我将行更新为 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,因为我觉得它需要触发器以串行方式为每个插入操作执行操作,以便实际上启用其中一个实验,同时禁用其余的重叠实验。
方法-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 新手,将欣赏示例或链接
如@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 &&);