sql - 并发交易: reading data being modified

标签 sql postgresql concurrency

鉴于此 SQL 事务函数(知道“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 服务器中,如果针对同一个 PK 同时运行该事务函数的两个实例,内部会发生什么?

  • 假设事务 1 (t1) 刚刚执行完步骤 2,当事务 2 (t2) 到达步骤 1 时:t2 会读取因 t1 尚未提交而被 t1 删除的值吗?
  • 然后,如果 t1 仍在步骤 2 和步骤 3 之间,并且 t2 执行步骤 2:是否为 t2 启动回滚?

后续

如果确实存在并发问题,那么如何才能在不借助表锁的情况下使这样的函数正常工作?

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

最佳答案

回答第一个问题

第二个事务仍然可以读取第一个事务已删除的值,因为第一个事务尚未提交,读取数据不需要行锁。

相关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

或使用更高的transaction isolation levelREAD COMMITTED

相关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.

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

关于sql - 并发交易: reading data being modified,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70859052/

相关文章:

sql - 在Oracle SQL Developer中按相对路径执行脚本

sql - 创建具有多个表的属性作为属性类型

sql - 如何在 SQL 中实现 `set field = max(1, x)`?

c# - 连接 PostgreSQL 数据库失败

c++ - 如何正确使用 std::condition_variable?

mysql - 通过消除表中的重复项来显示序列号(rownum)以及 mysql 中的数据?

java - 如何为 Spring Boot JPA Timestamp 指定 UTC 时区

java - Liferay 并发文件条目上传

concurrency - x86_64 内存重新排序

sql - 有没有更好的方法来计算中位数(不是平均值)