【问题标题】:40001 error in PostgreSQLPostgreSQL 中的 40001 错误
【发布时间】:2017-07-05 18:36:02
【问题描述】:

我有简单的表格“计数器”:

"CounterId" SERIAL (PK)

"CounterName" VARCHAR(50) NOT NULL (UNIQUE INDEX)

"Value" BIGINT NOT NULL

当两个可序列化事务(实际上同时有很多这样的事务)执行查询时:

SELECT NULL 
FROM "Counters" 
WHERE "CounterName" = @Value FOR UPDATE

SELECT "CounterId", "CounterName", "Value" 
FROM "Counters" 
WHERE "CounterName" = @Value
LIMIT 2 

(此查询由实体框架在同一连接和事务中执行)

UPDATE "Counters" SET "Value" = @Value WHERE "CounterId" = @CounterId

其中一个事务被回滚并出现错误 40001

由于事务之间的读/写依赖关系,无法序列化访问

我正在重试错误事务(5 次),但仍然出现此错误。

这可能是由于第一个和第三个查询中的谓词不同造成的?

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    如果您上面描述的两个事务同时运行,则会发生以下情况:

    1. 事务 1 用SELECT ... FOR UPDATE 锁定一行。

    2. 事务 2 尝试锁定同一行并被阻止。

    3. 事务 1 修改值并提交。

    4. 事务 2 被解除阻塞,在它锁定行之前,它必须重新检查它即将锁定的行版本是否仍然是最新版本。由于Transaction 1的修改,不再是这种情况,并抛出序列化错误。

    如果隔离级别为REPEATABLE READ 或更高级别的多个事务尝试修改相同的行,则无法避免该问题。准备好经常重试!

    看起来事务实际上锁定的行数比它修改的行数多。这加剧了问题。只锁定那些需要更改的行!

    【讨论】:

    • 表格中只有一行。我刚刚开始使用 PostgresSQL,看到这样的错误对我来说很奇怪。在 MS SQL 中,我可以执行查询 SELECT NULL FROM SomeTable WITH (UPDLOCK) WHERE Id = SomeId,这不会破坏第二个事务。
    • 那是因为Microsoft SQL Server 默认没有多版本控制(这会导致更差的并发性)。 PostgreSQL 通过确保每个可序列化事务在快照(事务开始时数据库的一致视图)上操作来实现一致性,因此更新稍后更改的值会违反可序列化性。您只有一行的情况是病态的,但它显示了两个数据库系统的不同方法(防御性锁定与尝试、失败、重试)。
    • 感谢您的解释。
    猜你喜欢
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    相关资源
    最近更新 更多