4

我想知道我正在对一张大表进行更新,以及是否需要担心锁。

我有一张看起来像这样的桌子:

CREATE TABLE "ItemsToProcess"( 
"id" text, 
"WorkerInstanceId" text, 
"ProcessingStartTime" timestamp with time zone, 
"UpdatedTime" timestamp with time zone, 
CONSTRAINT "ITP_PK" PRIMARY KEY ("id")
)WITH (
  OIDS=FALSE
);

最初,该表有大约 200 万行,并且仅id填充了列 -默认情况下,WorkerInstanceId两个时间戳是NULL在运行开始时。

会发生什么情况是,一些工作应用程序(至少两个,但在生产中大约 10-13 个)将从该表中标记一批 ID(我计划将 batchSize 设置为 200)以供他们处理。处理过程中发生的事情现在并不重要。

批次的标记如下所示:

UPDATE "ItemsToProcess" 
   SET "WorkerInstanceId" = ?, "ProcessingStartTime" = current_timestamp()
 WHERE "WorkerInstanceId" is NULL
 LIMIT 200;

我的问题是,在进行更新之前,我是否需要担心锁定要更新的行?

Postgres 文档说:

排独家

与 SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS EXCLUSIVE 锁模式冲突。

命令 UPDATE、DELETE 和 INSERT 在目标表上获取此锁定模式(除了任何其他引用表上的 ACCESS SHARE 锁定)。一般来说,任何修改表中数据的命令都会获得这种锁定模式。

所以我认为,每当其中一个工作人员进行此更新时,整个表都会被锁定,更新 200 行并最终释放锁定。在锁到位之前,其他工作人员正在等待锁释放。这是对的还是我错过了什么?

4

2 回答 2

8

UPDATE锁定行,因此您不需要先锁定它。如果您尝试同时UPDATE重叠多组行,第二个UPDATE将等待第一个事务提交或回滚。

UPDATE您的方法的大问题 - 除了没有子句的事实之外LIMIT- 多个工作人员都会尝试获取相同的行。这是发生的事情:

  • worker1:过滤表找到200行并加锁
  • worker1:开始更新行
  • worker2:过滤表以找到 200 行
  • worker2:尝试开始更新行,但选择了与 worker1 相同的行,因此它阻塞了 worker1 的锁
  • worker1:完成更新行
  • worker2:锁释放后,重新检查 WHERE 条件,发现没有任何行匹配,因为 worker1 更新了它们。更新零行。

...并重复!

您需要:

  • 有一个中央队列以适当的并发安全方式分发行;或者
  • 为工作人员分配不重叠的 ID 范围以进行工作

至于LIMIT-您可以使用WHERE id IN (SELECT t.id FROM thetable t LIMIT 200 ORDER BY id)-但是两个工作人员选择要更新的同一组行时会遇到同样的问题。

于 2012-08-01T23:34:14.187 回答
2

你错过了几件事。

首先,PostgreSQL 不提供LIMIT更新选项。请参阅UPDATE 的文档

其次,注意ROW EXCLUSIVE不与自身相冲突,与SHARE ROW EXCLUSIVE哪个相冲突是不同的。因此,您的UPDATE语句可以安全地从多个工作人员同时运行。您仍然希望您的更新时间较短。batchSize但是,如果遇到问题,您已经有一种内置方法可以通过降低您的值来调整它。

于 2012-08-01T14:55:14.187 回答