对于从作业队列中拉到 运行 的工作人员,我应该使用什么隔离级别?
What isolation level should I use for workers pulling from a queue of jobs to run?
我有一个 PostgreSQL 数据库 (v9.5.3),它正在托管 "jobs" 供工作人员拉取、运行 并返回。
当一个工人想要一份工作时,运行会产生以下影响:
SELECT MIN(sim_id) FROM job WHERE job_status = 0;
-- status 0 indicates it's ready to be run
job
是具有以下架构的 table:
CREATE TABLE commit_schema.job (
sim_id serial NOT NULL,
controller_id smallint NOT NULL,
controller_parameters smallint NOT NULL,
model_id smallint NOT NULL,
model_parameters smallint NOT NULL,
client_id smallint,
job_status smallint DEFAULT 0,
initial_glucose_id smallint NOT NULL
);
然后用这个sim_id
把一堆参数拼凑成一个JOIN
:
SELECT a.par1, b.par2 FROM
a INNER JOIN b ON a.sim_id = b.sim_id;
然后将这些参数 return 连同 sim_id
一起提供给工作人员,而工作是 运行。通过使用 UPDATE
:
将 job.job_status
设置为 1 来锁定 sim_id
UPDATE job SET job_status = 1 WHERE sim_id = ;
然后使用相同的 sim_id
提交结果。
理想情况下,
工人在任何情况下都无法得到相同的 sim_id
。
两个人求职不会出错,一个人只需要等待就可以得到工作。
我认为使用可序列化隔离级别将确保 MIN()
始终是 return 唯一的 sim_id
,但我相信这也可以使用已提交读来实现隔离级别。再一次,MIN()
可能无法同时并确定性地向两个并发工作人员提供唯一的 sim_id
?
对于使用默认隔离级别 Read Committed and FOR UPDATE SKIP LOCKED
(pg 9.5 中的新功能)的并发访问,这应该可以正常工作:
UPDATE commit_schema.job j
SET job_status = 1
FROM (
SELECT sim_id
FROM commit_schema.job
WHERE job_status = 0
ORDER BY sim_id
LIMIT 1
FOR UPDATE SKIP LOCKED
) sub
WHERE j.sim_id = sub.sim_id
RETURNING sim_id;
job_status
应该定义为 NOT NULL
.
警惕某些极端情况 - dba.SE 上的相关回答中有详细解释:
地址
从函数 return 有多种方法:
- 让它成为一个简单的 SQL 函数而不是 PL/pgSQL。
- 或使用
RETURN QUERY
和 PL/pgSQL。
- 或者将结果分配给一个带有
RETURNING ... INTO
的变量 - 它可以是一个 OUT
参数,所以它会在函数结束时自动被 return 编辑。或任何其他变量和 return 显式。
相关(带代码示例):
- Can I make a plpgsql function return an integer without using a variable?
- Returning from a function with OUT parameter
我有一个 PostgreSQL 数据库 (v9.5.3),它正在托管 "jobs" 供工作人员拉取、运行 并返回。
当一个工人想要一份工作时,运行会产生以下影响:
SELECT MIN(sim_id) FROM job WHERE job_status = 0;
-- status 0 indicates it's ready to be run
job
是具有以下架构的 table:
CREATE TABLE commit_schema.job (
sim_id serial NOT NULL,
controller_id smallint NOT NULL,
controller_parameters smallint NOT NULL,
model_id smallint NOT NULL,
model_parameters smallint NOT NULL,
client_id smallint,
job_status smallint DEFAULT 0,
initial_glucose_id smallint NOT NULL
);
然后用这个sim_id
把一堆参数拼凑成一个JOIN
:
SELECT a.par1, b.par2 FROM
a INNER JOIN b ON a.sim_id = b.sim_id;
然后将这些参数 return 连同 sim_id
一起提供给工作人员,而工作是 运行。通过使用 UPDATE
:
job.job_status
设置为 1 来锁定 sim_id
UPDATE job SET job_status = 1 WHERE sim_id = ;
然后使用相同的 sim_id
提交结果。
理想情况下,
工人在任何情况下都无法得到相同的
sim_id
。两个人求职不会出错,一个人只需要等待就可以得到工作。
我认为使用可序列化隔离级别将确保 MIN()
始终是 return 唯一的 sim_id
,但我相信这也可以使用已提交读来实现隔离级别。再一次,MIN()
可能无法同时并确定性地向两个并发工作人员提供唯一的 sim_id
?
对于使用默认隔离级别 Read Committed and FOR UPDATE SKIP LOCKED
(pg 9.5 中的新功能)的并发访问,这应该可以正常工作:
UPDATE commit_schema.job j
SET job_status = 1
FROM (
SELECT sim_id
FROM commit_schema.job
WHERE job_status = 0
ORDER BY sim_id
LIMIT 1
FOR UPDATE SKIP LOCKED
) sub
WHERE j.sim_id = sub.sim_id
RETURNING sim_id;
job_status
应该定义为 NOT NULL
.
警惕某些极端情况 - dba.SE 上的相关回答中有详细解释:
地址
从函数 return 有多种方法:
- 让它成为一个简单的 SQL 函数而不是 PL/pgSQL。
- 或使用
RETURN QUERY
和 PL/pgSQL。 - 或者将结果分配给一个带有
RETURNING ... INTO
的变量 - 它可以是一个OUT
参数,所以它会在函数结束时自动被 return 编辑。或任何其他变量和 return 显式。
相关(带代码示例):
- Can I make a plpgsql function return an integer without using a variable?
- Returning from a function with OUT parameter