并发事务:读取正在修改的数据

Concurrent transactions : reading data being modified

鉴于此 SQL t运行功能函数(知道“PK”表示“主键”):

1) READ a value from given PK (if exists)
2) DELETE row of given PK (if exists)
3) INSERT row for same PK

问题

在 Postgres 服务器中,如果该 运行sactional 函数的两个实例同时 运行 用于同一个 PK,内部会发生什么?

跟进

如果确实存在并发问题,那么在不借助table锁的情况下,究竟如何才能让这样的功能正常运行?

在我看来,行锁会很好,但我的理解是它们不会阻止读取的发生,因此第二个函数仍可能读取错误值(例如,如果条目被删除并且还没有重新插入,那么它会假设它不应该计算它,而实际上它应该等待新的插入)。

第一个问题的答案

第二个事务仍然可以读到第一个事务删除的值,因为第一个事务还没有提交,读数据不需要行锁。

相关Postgres documentation(重点是我的):

Read Committed is the default isolation level in PostgreSQL. When a transaction uses this isolation level, a SELECT query (without a FOR UPDATE/SHARE clause) sees only data committed before the query began; it never sees either uncommitted data or changes committed during query execution by concurrent transactions. In effect, a SELECT query sees a snapshot of the database as of the instant the query begins to run. However, SELECT does see the effects of previous updates executed within its own transaction, even though they are not yet committed. Also note that two successive SELECT commands can see different data, even though they are within a single transaction, if other transactions commit changes after the first SELECT starts and before the second SELECT starts.

第二个问题的答案

第二个事务中的删除会被第一个事务获取的行锁阻塞,一直挂起直到第一个事务完成。

解决方案

为避免此类竞争条件,请使用

执行第一步
SELECT ... FOR UPDATE

或使用比 READ COMMITTED 更高的 transaction isolation level

相关Postgres documentation(重点是我的):

FOR UPDATE causes the rows retrieved by the SELECT statement to be locked as though for update. This prevents them from being locked, modified or deleted by other transactions until the current transaction ends. That is, other transactions that attempt UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE or SELECT FOR KEY SHARE of these rows will be blocked until the current transaction ends; conversely, SELECT FOR UPDATE will wait for a concurrent transaction that has run any of those commands on the same row, and will then lock and return the updated row (or no row, if the row was deleted). Within a REPEATABLE READ or SERIALIZABLE transaction, however, an error will be thrown if a row to be locked has changed since the transaction started. For further discussion see Section 13.4.

因此,前一种解决方案会采用行锁并提前阻塞并发事务,后者会使其中一个事务失败。