gpt4 book ai didi

sql - Postgresql 可序列化事务未按预期工作

转载 作者:行者123 更新时间:2023-11-29 11:22:32 25 4
gpt4 key购买 nike

我正在尝试实现一个任务分配系统。用户可以从池中请求任务。即使设置为 SERIALIZABLE,事务有时也会将相同的任务交给多个用户,即使它不应该这样做。

简化架构:

CREATE TABLE tasks(
_id CHAR(24) PRIMARY KEY,
totalInstances BIGINT NOT NULL
);

CREATE TABLE assigned(
_id CHAR(24) PRIMARY KEY,
_task CHAR(24) NOT NULL
);

任务表充满了很多行,假设每一行都有 totalInstances = 1,这意味着每个任务最多应该分配一次。

assigned中添加一行的查询:

WITH task_instances AS (
SELECT t._id, t.totalInstances - COUNT(assigned._id) openInstances
FROM tasks t
LEFT JOIN assigned ON t._id = assigned._task
GROUP BY t._id, t.totalInstances
),

selected_task AS (
SELECT _id
FROM task_instances
WHERE openInstances > 0
LIMIT 1
)

INSERT INTO assigned(_id, _task)
SELECT $1, _id
FROM selected_task;

$1 是传递给每个查询的随机 ID。

症状

我们有大约 100 个活跃用户定期请求任务。这按预期工作,除了可能在 1000 个请求中有一次。然后,根据并行请求为相同 _task id 创建两个assigned 行。我希望可序列化的执行回滚第二个,因为第一个应该将 openInstances 减少到 0。

设置

我们使用 Postgres 10.3,查询是通过 Slick 3.2.3 使用 withTransactionIsolation(Serializable) 从 Scala 代码运行的。没有其他查询从 assigned 表中删除或插入。

Postgres 日志显示请求在不同的 session 中运行,并且 SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE; 在每个任务分配查询之前执行。

我尝试以不同的风格重写查询,包括对 WITH 子查询使用 VIEW,并用 BEGIN 包围查询和 COMMIT 但没有效果。

感谢任何帮助。

编辑

我应该补充一点,有时预期的序列化错误/回滚确实会出现,我们的应用程序会根据这些错误/回滚重试查询。我在过去几个小时的日志中看到此正确行为 10 次,但有 2 次它仍然错误地分配了两次相同的任务,如上所述。

最佳答案

可序列化隔离级别并不意味着事务是字面意义上的串行。它只保证读取提交、可重复读取和不存在幻读。而且您所描述的行为看起来并不违规。

为了避免重复记录,你可以简单地做

select ... from task_instances for update

由于这个“for update”子句,所选行将在事务生命周期内被锁定。因此只有一个事务能够更新,第二个事务必须等到第一个事务提交。因此,第二个事务将读取第一个事务更新的值 - 这正是您在这里需要的保证。

同样重要的是,如果您在这种情况下使用“select for update”,您甚至不需要 Serializable 隔离级别,read committed 就足够了。

关于sql - Postgresql 可序列化事务未按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50020399/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com