SQL 可序列化事务如何工作? (SELECT/UPDATE vs INSERT 冲突)
How does SQL serializable transaction works? (SELECT/UPDATE vs INSERT conflict)
事务#1 对数据集(WHERE
子句)进行操作,第二个事务对其执行 INSERT
,适合第一个事务的子句。
如果第 2 次提交,第 1 次事务不应该失败吗?
我有以下 table(在 PostgreSQL 9.5 db 中)
CREATE TABLE public.foo (id serial PRIMARY KEY, mynum integer);
及后续数据
id | mynum
----+-------
1 | 10
2 | 10
3 | 10
4 | 10
(4 rows)
I 运行 2 并行序列化事务(2 psql
控制台):
-- both transactions
mydb=# begin;
BEGIN
mydb=# set transaction isolation level serializable;
SET
-- tx1
mydb=# select * from foo where mynum < 100;
id | mynum
----+-------
1 | 10
2 | 10
3 | 10
4 | 10
(4 rows)
--tx1: Shouldn't freeze data visible for tx1 select?
--tx2
mydb=# insert into foo (mynum) values (10);
INSERT 0 1
-- tx2 will insert next row with id 5 in foo table
-- Shouldn't insert of tx2 broke data snapshot visible for tx1?
--tx1
mydb=# update foo set mynum = 20 where id < 100;
UPDATE 4
-- Shouldn't here appear serialization fail or at least on tx1 commit?
--tx2
mydb=# commit;
COMMIT
--tx1
mydb=# commit;
COMMIT
-- tx1 Commit is OK - no any error
-- implicit tx
mydb=# select * from foo;
id | mynum
----+-------
1 | 20
2 | 20
3 | 20
4 | 20
5 | 10
(5 rows)
考虑到 PostgreSQL 文档,我想知道为什么它会这样
"To guarantee true serializability PostgreSQL uses predicate locking,
which means that it keeps locks which allow it to determine when a
write would have had an impact on the result of a previous read from a
concurrent transaction, had it run first."
link: http://www.postgresql.org/docs/current/static/transaction-iso.html
SERIALIZABLE
保证并发事务有一些串行执行顺序会产生相同的结果,但这不一定与提交顺序相同。在您的情况下,依次 运行 tx1
后跟 tx2
会给您相同的结果,因此没有理由导致序列化失败。
永远不允许两个重叠的交易看到彼此的数据(MVCC 系统确保新记录被 SELECT
/UPDATE
语句忽略)。当他们应该看到彼此的数据时,就会出现序列化失败,即当任何一个序列排序都会导致第二个事务看到第一个事务写入的记录时。
这里的关键点是,只有在两个事务都读了一些东西并写了一些东西时才会发生这种情况;否则,始终可以通过将只读事务放在首位或将只写事务放在最后来序列化它们。所以你的单独插入永远不会与任何东西发生冲突(但是如果 tx2
也包含 SELECT * FROM foo
,那么你会收到一个错误)。
当涉及到更多交易时,事情就没那么简单了。有一长串示例 here 可能会更深入地了解序列化异常发生的时间和地点。
事务#1 对数据集(WHERE
子句)进行操作,第二个事务对其执行 INSERT
,适合第一个事务的子句。
如果第 2 次提交,第 1 次事务不应该失败吗?
我有以下 table(在 PostgreSQL 9.5 db 中)
CREATE TABLE public.foo (id serial PRIMARY KEY, mynum integer);
及后续数据
id | mynum
----+-------
1 | 10
2 | 10
3 | 10
4 | 10
(4 rows)
I 运行 2 并行序列化事务(2 psql
控制台):
-- both transactions
mydb=# begin;
BEGIN
mydb=# set transaction isolation level serializable;
SET
-- tx1
mydb=# select * from foo where mynum < 100;
id | mynum
----+-------
1 | 10
2 | 10
3 | 10
4 | 10
(4 rows)
--tx1: Shouldn't freeze data visible for tx1 select?
--tx2
mydb=# insert into foo (mynum) values (10);
INSERT 0 1
-- tx2 will insert next row with id 5 in foo table
-- Shouldn't insert of tx2 broke data snapshot visible for tx1?
--tx1
mydb=# update foo set mynum = 20 where id < 100;
UPDATE 4
-- Shouldn't here appear serialization fail or at least on tx1 commit?
--tx2
mydb=# commit;
COMMIT
--tx1
mydb=# commit;
COMMIT
-- tx1 Commit is OK - no any error
-- implicit tx
mydb=# select * from foo;
id | mynum
----+-------
1 | 20
2 | 20
3 | 20
4 | 20
5 | 10
(5 rows)
考虑到 PostgreSQL 文档,我想知道为什么它会这样
"To guarantee true serializability PostgreSQL uses predicate locking, which means that it keeps locks which allow it to determine when a write would have had an impact on the result of a previous read from a concurrent transaction, had it run first." link: http://www.postgresql.org/docs/current/static/transaction-iso.html
SERIALIZABLE
保证并发事务有一些串行执行顺序会产生相同的结果,但这不一定与提交顺序相同。在您的情况下,依次 运行 tx1
后跟 tx2
会给您相同的结果,因此没有理由导致序列化失败。
永远不允许两个重叠的交易看到彼此的数据(MVCC 系统确保新记录被 SELECT
/UPDATE
语句忽略)。当他们应该看到彼此的数据时,就会出现序列化失败,即当任何一个序列排序都会导致第二个事务看到第一个事务写入的记录时。
这里的关键点是,只有在两个事务都读了一些东西并写了一些东西时才会发生这种情况;否则,始终可以通过将只读事务放在首位或将只写事务放在最后来序列化它们。所以你的单独插入永远不会与任何东西发生冲突(但是如果 tx2
也包含 SELECT * FROM foo
,那么你会收到一个错误)。
当涉及到更多交易时,事情就没那么简单了。有一长串示例 here 可能会更深入地了解序列化异常发生的时间和地点。