SELECT可以使用Read Committed Isolation Level在一个事务中获得不同的结果吗?

时间:2018-04-15 15:19:05

标签: sql postgresql transactions

所以这里是postgresql 9.6 docs关于Read Committed Isolation Level的完整段落:

  

Read Committed是PostgreSQL中的默认隔离级别。当一个   transaction使用这个隔离级别,一个SELECT查询(没有FOR   UPDATE / SHARE子句仅查看在查询开始之前提交的数据;   它永远不会看到未提交的数据或在此期间提交的更改   并发事务查询执行。实际上是一个SELECT查询   在查询开始的瞬间查看数据库的快照   跑。但是,SELECT确实看到了先前更新执行的效果   在它自己的交易中,即使它们尚未提交。   另请注意,两个连续的SELECT命令可以看到不同的数据,   即使它们属于单一交易,如果是其他交易   事务在第一个SELECT开始之后和之前提交更改   第二个SELECT开始。

基本上是这样的:

  

SELECT查询只查看在查询开始之前提交的数据,并且永远不会看到在查询执行期间提交的更改   并发交易。

但在最后一句中,它指出:

  

另请注意,两个连续的SELECT命令可以看到不同的数据,   即使它们属于单一交易,如果是其他交易   事务在第一个SELECT开始之后和之前提交更改   第二个SELECT开始。

对我来说,它看起来很矛盾。有人可以详细说明吗?两个SELECT查询究竟如何在一个事务中看到不同的数据? Isn`t交易是否被隔离?

2 个答案:

答案 0 :(得分:2)

是的,这是真的。为了避免这种情况,您需要使用更高的隔离级别:"可重复读取"。 或甚至"可序列化"如果您需要完全隔离您的交易。 请记住,更高的隔离度通过性能支付更高的成本。

您可以在此处找到详细说明:https://www.postgresql.org/docs/9.1/static/transaction-iso.html

以下是一个如何实现的例子:

  1. Connection1打开事务并从Table1读取一行 "选择*"
  2. Connection2更新同一行并提交

  3. Connection1再次读取同一行并获取更新数据

  4. 隔离级别是"读取已提交",因此字面上提交的所有内容对其他连接都是可见的。

    如果由于某种原因无法使用更高的隔离级别,有一种方法可以防止出现这种意外情况"发生更新:您的Connection1可以使用"选择...进行更新"代替。这将有效地锁定行,直到Connection1的事务提交或回滚。因此Connection2将等待此提交或回滚以便能够更新该行。

答案 1 :(得分:1)

没有矛盾。事务中的两个连续SELECT语句可能会获取不同的结果。考虑一下 - 你开始交易,然后发出
select * from emp; 你得到2条记录。 另一个会话将记录插入到emp和commit中。 在第一个会话中,您再次发出 select * from emp; 你得到3条记录。这是READ COMMITTED隔离级别的预期行为。

示例代码

tmp=# begin ; 
BEGIN
tmp=# select * from emp;
 id 
----
  1
  2
(2 rows)

tmp=# select * from emp;
 id 
----
  1
  2
  3
(3 rows)

tmp=# commit;