【发布时间】:2011-06-27 18:56:25
【问题描述】:
我有一个可以支持一定数量的并发操作的应用程序。这由 postgres 中的“槽”表表示。当节点上线时,它们会在表中插入一些行,每个槽一个。当作业占用插槽时,它们会更新表中占用其中一个插槽的行,并在完成时再次释放它。
插槽表如下所示:
CREATE TABLE slots (
id INT8 PRIMARY KEY DEFAULT nextval('slots_seq'),
node_name TEXT NOT NULL,
job_name TEXT
);
在任何时候,它都有一些半固定数量的行,每行可能填写也可能不填写 job_name。
当一个新作业想要启动时,它会运行这些查询来获取它应该运行的节点的名称:
BEGIN;
LOCK TABLE slots IN ACCESS EXCLUSIVE MODE;
SELECT id, node_name
FROM slots
WHERE job_name IS NULL
LIMIT 1
FOR UPDATE;
(从游标中读出node_name和id)
UPDATE slots
SET job_name = %(job_name)s
WHERE id = %(slot_id)s;
COMMIT;
这通常能够在不丢失任何更新的情况下声明行,但具有更高级别的并发性,在执行许多 SELECT ... FOR UPDATE 和 UPDATE 查询时只会声明几行。最终结果是,我们最终运行的作业远远多于它们的插槽。
我是否犯了锁定错误?有没有更好的方法来解决这个问题?不使用表锁的东西?
事务级别 SERIALIZABLE 并没有削减它,只有少数行被填充。
我使用的是 postgresql 8.4 版。
【问题讨论】:
-
好吧,我会在更新之前粘贴一个调试选择/通知组合,以显示有多少行与您的 job_name = %(job_name)s 限制匹配(您使用的任何语言都会自动执行 '引用' %(foo)s 语法?)在其他新闻中,我会在保留步骤期间进行安全检查,以检查 job_name 是否已经保留了一个插槽。
-
我会试一试。谢谢,赛斯。 (是的,%(variable)s 东西是由我正在使用的 python 接口填充的。)
-
我希望您能够处理这样一个事实,即您的第一个限制为 1 的请求可以成功,而无需返回任何可用的插槽。您使用了 2 个不同的表名 'slots' 和 'server_slots',错了吗?还有为什么不释放的时候复用slot_id,如果2个job同名会释放太多slot呢?
-
regilero:我确实会处理没有可用插槽、请求代码获取并返回错误的情况。我刚刚修复了 slot/server_slots 问题。作业名称是全局唯一的。实际上是 UUID。
-
我刚刚编辑以简化问题。进一步的实验表明,我可以在不释放任何插槽的情况下删除更新。
标签: python sql postgresql