sql - 涉及子选择和外键的 Postgres 竞争条件
问题描述
我们有 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)
);
我注意到在同时执行以下两个查询时
INSERT INTO foo (name) VALUES ('BAZ')
INSERT INTO bar (foo_name, foo_id) VALUES ('BAZ', (SELECT id FROM foo WHERE name = 'BAZ'))
bar
在某些情况下,最终可能会在where foo_id
is中插入一行NULL
。这两个查询由两个完全不同的进程在不同的事务中执行。
这怎么可能?我希望第二条语句要么因外键违规而失败(如果其中的记录foo
不存在),或者以非空值foo_id
(如果存在)成功。
是什么导致了这种竞争状况?是由于子选择,还是由于检查外键约束的时间?
我们正在使用隔离级别“已提交”和 postgres 版本 10.3。
编辑
我认为这个问题并不是特别清楚让我感到困惑的是什么。问题是关于在执行单个语句期间如何以及为什么观察到数据库的 2 种不同状态。子选择正在观察 foo 中的记录不存在,而 fk 检查将其视为存在。如果只是没有规则阻止这种竞争条件,那么这本身就是一个有趣的问题——为什么不能使用事务 ID 来确保两者都观察到相同的数据库状态?
解决方案
中的子选择INSERT INTO bar
看不到同时插入的新行,foo
因为后者尚未提交。
但是到执行检查外键约束的查询时,INSERT INTO foo
已经提交,因此外键约束不会报告错误。
解决此问题的一种简单方法是REPEATABLE READ
使用INSERT INT bar
. 然后外键检查使用与 相同的快照INSERT
,它不会看到新提交的行,并且会抛出约束冲突错误。
推荐阅读
- r - 使用 conda 安装 R 3.6.1 无限期挂起
- angular - 类型参数不可分配给“OperatorFunction”类型的参数
' - python - 将python应用程序部署到Azure Web App时为tensorflow获取ReadTimeoutError
- image - 将图像与响应式文本引导程序对齐
- pip - 带有 openssl 1.1 的 gdal 破坏了 pip 安装和其他依赖于 macos 上的 openssl 1.0 的东西
- java - JLabel 直到循环结束才更新
- javascript - D3 单击附加到线的符号
- java - Java 初学者:使用循环将输入值分成两半,但不能使用数组、内置排序例程或任何其他 Java 集合类
- python - 在 PyCharm 中使用 requests 和 BeautifulSoup 后没有输出
- node.js - nodejs 导入模块没有按预期工作,