【问题标题】:Understanding transactions in sql了解sql中的事务
【发布时间】:2017-07-09 02:43:41
【问题描述】:

关系 R(x) 由一组整数组成 --- 即具有整数分量的单分量元组。 Alice 的交易是一个查询:

SELECT SUM(x) FROM R;
COMMIT;

Betty 的事务是一个插入序列:

INSERT INTO R VALUES(10);
INSERT INTO R VALUES(20);
INSERT INTO R VALUES(30);
COMMIT;

Carol 的事务是一系列删除操作:

DELETE FROM R WHERE x=30;
DELETE FROM R WHERE x=20;
COMMIT;

在任何这些交易执行之前,R 中的整数之和为 1000,并且这些整数都不是 10、20 或 30。如果 Alice、Betty 和 Carol 的交易几乎同时运行,并且每个在隔离级别 READ COMMITTED 下运行,Alice 的事务可以产生哪些总和?从下面的列表中找出其中一项。

a)1040 b)950 c)1030 d)1080

这个问题让我有点困惑,因为没有任何东西可以告诉我们哪个事务首先提交或者它们是否同时提交。你如何解释这个问题?

【问题讨论】:

  • "Alice 的交易可以产生哪些金额?" --- 这个问题意味着您需要评估不同的结果(取决于事务提交顺序)。
  • 我更新了帖子
  • 为什么是 950?指定非元组包含 10,20 或 30
  • 问题是允许幻读

标签: sql postgresql


【解决方案1】:

如果它们同时运行并同时提交,那么 Alice 应该得到一个 1000 的值。这是因为隔离级别是 READ COMMITTED,它不会从另一个读取任何插入或删除事务直到它们被提交。

这实际上取决于 COMMIT 的顺序。不知道这一点,你就无法确定答案是什么。

所以,我的答案是 E) 1000。你能和给你这个问题的人澄清一下提交顺序是什么吗?

【讨论】:

  • 1000 - 当 Alice 的 SELECT 在任何其他 COMMIT 之前运行时
  • 没错,就像你在回答中所说的那样,SELECT 不需要 COMMIT。
【解决方案2】:

对于 CRUD 事务,不存在“大约在同一时间”这样的事情。

只要它是 READ COMMITTED 这取决于在 Alice 的 SELECT 运行之前首先提交的 CRUD 事务。没有一个答案是正确的。

对于SELECT事务COMMIT没有意义。

只有三个可能的答案

1000、1060、1010

【讨论】:

  • 答案是 1040,因为记住读已提交保证不允许读取脏数据,但它没有说明新插入,所以允许幻读
  • @daniel 你能用数学计算一下,当一个事务插入 60 而另一个事务删除 50 时,你是如何得到 1040 的?什么是“幻读”?
  • 幻读是插入新数据时。这与更新数据时的脏读不同。
  • 所以你通过以下方式得到 1040:首先执行 Betty 的事务,一旦执行了一次插入,执行 carol 的事务。
  • 请注意,幻读是允许的,因此所有插入对任何事务都是可见的。
【解决方案3】:

两个规则在这个问题中发挥作用:

  • Read Committed 隔离级别:在事务内部,每个 SQL 语句 看到一个新版本的数据(一个新的“快照”),包括另一个 并发事务可能刚刚提交。 这意味着要回答这个问题,必须考虑每个事务中语句的相对顺序,而不仅仅是每个事务开始或结束的时间。

  • 原子性:其他人看不到事务中的更改 事务直到它提交。脏读是不可能的 Transaction Isolation

  • 文档章节中提到的 PostgreSQL

让我们列举语句:

爱丽丝 A1:从 R 中选择 SUM(x); A2:提交; 贝蒂 B1:插入 R 值(10); B2:插入 R 值(20); B3:插入 R 值(30); B4:提交; 颂歌 C1:从 R 中删除 x=30; C2:从 R 中删除 x=20; C3:提交;

对于每个值a)1040 b)950 c)1030 d)1080,您想找出这些语句的执行顺序是否使得在A1 步骤中的SELECT SUM(x) FROM R 恰好产生该值,给定上述事务规则。

a=1040

1040 可以用这个执行顺序(以及其他)读取:

B1 B2 C1 -- DELETE WHERE x=30 什么都不做,因为 x=30 在 R 中不存在。 B3 B4 - 现在 A1 在 R 中会看到 10,20,30,所以此时 SUM(x) 为 1060 C2 -- DELETE WHERE x=20 确实删除了存在且对 C 可见的 x=20 C3 -- 提交删除 x=20 A1 -- 现在 A1 可以看到 10,30 所以 SUM(x)=1040 A2 -- 提交(无效,A 只读取)

编辑:有些人不相信这个结果是可能的,所以请在问题底部查看从 postgres 会话粘贴的实际演示副本。

b=950

值20,30一开始不在表中,所以如果C1和C2可以去掉30和20,那是因为B2和B3加了它们,如果这样做了,B1先加了10,所以结果至少是 960。 在 C1 和 C2 因尚未写入而无法删除 30 或 20 的情况下,结果 SUM(x) 必然大于 960。 所以 A1 永远看不到 SUM(x) 小到 950,这是不可能的。

c=1030

要让 A1 读取 1030,它必须在表中看到两行 (10)、(20) 或一行 (30),不包括其他任何内容,因为其他组合的总和不等于 30。

由于原子性,由 B1,B2,B3 插入的 (10,20,30) 行将同时变为可见。 C1 和 C2 可以分别撤消 B3 和 B2 的作用,但是如果 C1 找到并删除 x=30,那么 C2 也必然会找到并删除 x=20。 A1 在 C3 运行之前看不到 C1 的结果,如果 C3 已经运行,C2 也有,所以 30 和 20 都从 A 的可见性中消失了,所以它只看到 (10),而不是 (10),(20) . 单独看到 (30) 也是不可能的,因为如果 (30) 存在,那么 (10) 也会存在, 因为 x=10 是由 B1 插入的,之后就再也没有删除过。

所以 A1 永远看不到 SUM(x)=1030。

d=1080

1000+10+20+30 = 1060 且代码不再添加任何行,因此无论执行顺序如何,都不可能超过 1060。


编辑:

postgresql 9.5 的实际演示

# 在里面 $ psql -d 测试 test=# 创建表 R(x int); 创建表 test=# 插入 R 值(1000); 插入 0 1 测试=# 显示事务隔离; 交易隔离 ---------------------- 读已提交 (1 行) 测试=# \q

** 启动 3 个不同的并发会话 A、B、C,并使用不同的提示进行区分:

$ psql -d 测试 \set PROMPT1 '爱丽丝%R%# ' 爱丽丝=# $ psql -d 测试 \set PROMPT1 '贝蒂%R%# ' 贝蒂=# $ psql -d 测试 \set PROMPT1 'carol%R%#' 卡罗尔=#

现在让我们按照上面提到的顺序运行命令,哪个会话做什么由提示显而易见。

爱丽丝=#开始; 开始 贝蒂=#开始; 开始 卡罗尔=#开始; 开始 betty=# 插入 R 值(10); 插入 0 1 贝蒂=#插入R值(20); 插入 0 1 carol=# 从 R 中删除 x=30; 删除 0 贝蒂=#插入R值(30); 插入 0 1 贝蒂=#提交; 犯罪 carol=# 从 R 中删除 x=20; 删除 1 卡罗尔=#提交; 犯罪 alice=# 从 r 中选择 sum(x); 和 ------ 1040 (1 行) 爱丽丝=#提交; 犯罪

【讨论】:

  • 答案不正确。如果它对 Postgres 是正确的,则意味着它是垃圾。实际上还有一个锁定机制。在与 Oracle 合作的 20 年中,我从未见过这种行为,并且现在使用 SQL-Server 明确测试了场景。没有“幻读”之类的东西。插入不可见或表被 INSERT 事务锁定。所以...如果 INSERT 先开始,那么答案是 1010。如果 DELETE 答案是 1060,如果 SELECT 答案是 1000
  • @Vadim:幻读有大量文档,包括 Oracle 本身:On Transaction Isolation Levelsstackoverflow: what is difference between non-repeatable read and phantom read?。除此之外,我挑战你用 postgres 测试你的答案。
猜你喜欢
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
  • 2016-09-30
相关资源
最近更新 更多