cpp*_*ner 13 sql postgresql concurrency foreign-keys subquery
我们有 2 个表定义如下
CREATE TABLE foo (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE bar (
foo_id BIGINT UNIQUE,
foo_name TEXT NOT NULL UNIQUE REFERENCES foo (name)
);
Run Code Online (Sandbox Code Playgroud)
我注意到在同时执行以下两个查询时
INSERT INTO foo (name) VALUES ('BAZ')
Run Code Online (Sandbox Code Playgroud)
INSERT INTO bar (foo_name, foo_id) VALUES ('BAZ', (SELECT id FROM foo WHERE name = 'BAZ'))
Run Code Online (Sandbox Code Playgroud)
在某些情况下,最终可能会在barwhere foo_idis 中插入一行NULL。这两个查询由两个完全不同的进程在不同的事务中执行。
这怎么可能?我希望第二条语句要么由于外键违规而失败(如果记录foo不存在),要么以非空值foo_id(如果是)成功。
是什么导致了这种竞争条件?是由于子选择,还是由于检查外键约束的时间?
我们使用隔离级别“已提交读”和 postgres 10.3 版。
编辑
我认为这个问题并不清楚是什么让我感到困惑。问题是关于在执行单个语句期间如何以及为什么观察到数据库的 2 个不同状态。subselect 观察到 foo 中的记录不存在,而 fk 检查认为它存在。如果只是没有规则阻止这种竞争条件,那么这本身就是一个有趣的问题 - 为什么不能使用事务 ID 来确保对两者都观察到相同的数据库状态?
Lau*_*lbe 10
中的子选择INSERT INTO bar无法看到同时插入的新行,foo因为后者尚未提交。
但是到执行检查外键约束的查询时,INSERT INTO foo已提交,因此外键约束不会报告错误。
解决此问题的一种简单方法是将REPEATABLE READ隔离级别用于INSERT INT bar. 然后外键检查使用与 相同的快照INSERT,它不会看到新提交的行,并且会抛出约束违规错误。
| 归档时间: |
|
| 查看次数: |
418 次 |
| 最近记录: |