对于从作业队列中拉到 运行 的工作人员,我应该使用什么隔离级别?

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 提交结果。

理想情况下,

  1. 工人在任何情况下都无法得到相同的 sim_id

  2. 两个人求职不会出错,一个人只需要等待就可以得到工作。

我认为使用可序列化隔离级别将确保 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