PostgreSQL的手册中有一段讲可串行化的,大意是
可串行化事务读到的数据未必是可靠的,除非它提交成功了。
-----------------------------------------------------------------------------------------
当依赖于可串行化事务阻止异常现象时,来自永久用户表读取的任何数据,直到读取它的事务成功提交为止,都不被认为是有效的。 这即使对于只读事务也是对的,除了在可延期的只读事务中,数据在读到它的时候就是有效的。 因为这样一个事务将一直等到可以获得一个保证不会受此类问题困扰的快照的时候,才开始读取数据。 在所有其他情况下,应用不能依赖于事务期间读到的结果,这个事务之后可能会被终止;取而代之的是,它们应该重试事务直到成功为止。
-----------------------------------------------------------------------------------------
但是,对一个只读的
可串行化事务,我没有找到任何系统会将其终止的案例(也许有),但是很容易可以找到由于只读可串行化事务的出现,使得原本可以成功的写事务失败了。这应该也比较好理解,对与一个
只读的事务,终止它有什么意义呢?该读的和不该读的数据它都已经读到了,只能迁就它,其它写事务失败了。下面是个例子
例子1: 2个读写事务不发生冲突
会话(事务)A
-
postgres=# begin transaction isolation level serializable;
-
BEGIN
-
postgres=# select * from tb1;
-
id | name
-
----+------
-
(0 rows)
-
-
postgres=# insert into tb1 values(1,'a');
-
INSERT 0 1
会话(事务)B
-
postgres=# begin transaction isolation level serializable;
-
BEGIN
-
postgres=# insert into tb1 values(1,'b');
-
INSERT 0 1
-
postgres=# commit;
-
COMMIT
会话(事务)A
-
postgres=# commit;
-
COMMIT
虽然,事务B先提交的,但逻辑上,事务A被排在了事务B前面,因为事务A没有看到事务B提交的修改,这就是所谓的rw依赖。
例子2: 2个读写事务+1个只读事务导致并发冲突
如果在例子1的两个事务之外在插入一个只读的事务C,可能就会使A,B,C之间的依赖关系形成环路,而不得不终止掉一个事务。
会话(事务)A
-
postgres=# begin transaction isolation level serializable;
-
BEGIN
-
postgres=# select * from tb1;
-
id | name
-
----+------
-
(0 rows)
-
-
postgres=# insert into tb1 values(1,'a');
-
INSERT 0 1
会话(事务)B
-
postgres=# begin transaction isolation level serializable;
-
BEGIN
-
postgres=# insert into tb1 values(1,'b');
-
INSERT 0 1
-
postgres=# commit;
-
COMMIT
-
会话(事务)C
-
postgres=# begin transaction isolation level serializable read only;
-
BEGIN
-
postgres=# select * from tb1;
-
id | name
-
----+------
-
1 | b
-
(1 row)
-
-
postgres=# commit; --这里不忙提交,让事务A先提交结果也是一样的。
-
COMMIT
会话(事务)A
-
postgres=# commit;
-
ERROR: could not serialize access due to read/write dependencies among transactions
-
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
-
HINT: The transaction might succeed if retried.
例子2中,事务C读到了事务B已提交的数据,所以应该在B之后(即wr依赖);
事务C没有读到事务A未提交的修改,所以应该在事务A之前(即rw依赖);而根据例1,事务A又应该在事务B之前。这就形成了环路,也就是冲突。
参考
src/backend/storage/lmgr/README-SSI
阅读(5555) | 评论(0) | 转发(1) |