我有简单的表"计数器":
"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
(此查询由Entity Framework在相同的连接和事务中执行)
UPDATE "Counters" SET "Value" = @Value WHERE "CounterId" = @CounterId
其中一个交易回滚,错误40001
由于事务之间的读/写依赖性,无法序列化访问
我正在重试错误事务(5次),但仍然会发生此错误。
这可能是由第一次和第三次查询中的不同谓词引起的?
答案 0 :(得分:4)
如果上面描述的两个事务同时运行,则会发生以下情况:
事务1使用SELECT ... FOR UPDATE
锁定一行。
事务2尝试锁定同一行并被阻止。
事务1修改值并提交。
事务2未被阻塞,在锁定行之前,必须重新检查它要锁定的行版本是否仍然是最新版本。由于事务1的修改,不再是这种情况,并且抛出了序列化错误。
如果隔离级别为REPEATABLE READ
或更高级别的多个事务正在尝试修改相同的行,则无法避免该问题。准备好经常重试!
事务似乎实际上锁定了比修改更多的行。这加剧了这个问题。只锁定那些需要更改的行!