我有一个 postgres 表:
CREATE TABLE clicks (
page_id UUID REFERENCES pages (id) ON DELETE CASCADE,
status TEXT NOT NULL,
click BIGINT NOT NULL,
PRIMARY KEY (page_id, status)
);
和两个执行块:
代码块_1:
BEGIN;
LOCK TABLE clicks IN SHARE UPDATE EXCLUSIVE MODE;
INSERT INTO clicks (page_id, status, count)
VALUES ($1, $2, $3 )
ON CONFLICT (page_id, status) DO UPDATE SET count = clicks.count + EXCLUDED.count;
-- that insert query can run more than once on different rows in the table. so imagine that there is a code that generates that INSERT INTO inside a for loop inside that one transaction
COMMIT;
代码块_2:
BEGIN;
LOCK TABLE clicks IN SHARE ROW EXCLUSIVE MODE;
INSERT INTO clicks (page_id, status, count)
VALUES ($1, $2, $3)
ON CONFLICT (page_id, status) DO UPDATE SET
count = EXCLUDED.count;
-- that insert query can run more than once on different rows in the table. so imagine that there is a code that generates that INSERT INTO inside a for loop inside that one transaction.
COMMIT;
由 code_block_1 更新的行可能由多个线程并行更新。 但是由 code_block_2 更新的行可以保证它们不会被另一个线程并行更新。 有了这些信息,如果我不想陷入死锁,我可以放弃哪个锁(如果有)?
您不需要任何一把锁。 两个插入都是原子的。
ON CONFLICT DO UPDATE 保证原子 INSERT 或 UPDATE 结果;只要不存在独立错误,即使在高并发情况下,也可以保证这两种结果之一。这也称为 UPSERT —“更新或插入”。