PostgreSQL的事务也是基于MVCC实现的。支持3种隔离级别,读已提交,可重复读和可串行化。但用户设置读未提交并不会报错而是被映射到读已提交,这是不违反SQL规约的。PostgreSQL的可重复读相当于Oracle的可串行化,即SI。而PostgreSQL的可串行化则是真正的可串行化即Serializable Snapshot Isolation (SSI),这一特性是从9.1开始加进来的。所以,在并发事务隔离的处理上,可以说PostgreSQL走在了包括Oracle,SQL Server在内的商业数据库的前头。下面看看详细测试结果。
1. 测试环境
OS:Windows7
DBMS:PostgreSQL 9.2.4
2. 测试结果
读已提交:
OK(**):基于更新前的数据,即看到是查询时的快照
等待(*): 如果先行的SQL提交,则基于更新后的数据,否则基于原来的数据
注:黄色代表和Oracle的读已提交不同的地方
可重复读/可串行化:
OK(***):基于更新前的数据,查询看到的是事务开始时的快照
等待(*): 如果先行的SQL提交,则基于更新后的数据,否则基于原来的数据
等待(**):如果先行的SQL提交,则报更新冲突的错误
注:黄色代表和读已提交不同的地方
3. PostgreSQL 的 串行化(SSI)和可重复读(SI)的区别
PostgreSQL的串行化(SSI)级别下,比可重复读(SI)多了是否能串行化的检查,如果在某个时点(SQL语句执行或提交时),发现串行化冲突,则终止事务并报错。还是用之前的例子来说明。
SSI下,2个事务按下面的顺序交替执行,在下面的第6步,PostgreSQL检出串行化冲突,因而报错并取消事务2。这样先提交的事务1就不违反可串行化原则。
1) 事务1:select * from tb1;
2) 事务2:select * from tb1;
3) 事务1:insert into tb1 values(5,'a');
4) 事务2:insert into tb1 values(6,'a');
5) 事务1:commit;
6) 事务2:commit;//报错
在可重复读(SI)时,上面的6)是能执行成功的。
*)上面的6)如果执行成功就不符合真正的串行化。假设2个事务按照 事务1->事务2 的顺序串行执行, 在2)中事务2应该能看到3)事务1插入的数据,这是不可能的;反之 事务2->事务1 亦然。
*)详细测试方法请参考:
并发事务下各数据库外部表现实测之一(SQL Server篇)
http://blog.chinaunix.net/uid-20726500-id-3883105.html
1. 测试环境
OS:Windows7
DBMS:PostgreSQL 9.2.4
2. 测试结果
读已提交:
先执行SQL\后执行SQL |
同一行查询 | 非同行查询 | 整表查询 | 同一行插入 | 非同行插入 | 同一行更新 | 非同行更新 | 整表更新 | 同一行删除 | 非同行删除 | 整表删除 |
单行查询 | OK | OK | OK | 主键冲突 | OK | OK | OK | OK | OK | OK | OK |
整表查询 | OK | OK | OK | 主键冲突 | OK | OK | OK | OK | OK | OK | OK |
插入 | OK(**) |
OK | OK(**) |
等待(*) | OK | OK(**) | OK | OK(**) | OK(**) | OK | OK(**) |
单行更新 | OK(**) |
OK | OK(**) |
等待(*) | OK | 等待(*) | OK | 等待(*) | 等待(*) | OK | 等待(*) |
整表更新 | OK(**) |
OK | OK(**) |
等待(*) | OK | 等待(*) | OK | 等待(*) | 等待(*) | OK | 等待(*) |
单行删除 | OK(**) |
OK | OK(**) |
等待(*) | OK | 等待(*) | OK | 等待(*) | 等待(*) | OK | 等待(*) |
整表删除 | OK(**) |
OK | OK(**) |
等待(*) | OK | 等待(*) | OK | 等待(*) | 等待(*) | OK | 等待(*) |
等待(*): 如果先行的SQL提交,则基于更新后的数据,否则基于原来的数据
注:黄色代表和Oracle的读已提交不同的地方
可重复读/可串行化:
先执行SQL\后执行SQL |
同一行查询 | 非同行查询 | 整表查询 | 同一行插入 | 非同行插入 | 同一行更新 | 非同行更新 | 整表更新 | 同一行删除 | 非同行删除 | 整表删除 |
单行查询 | OK | OK | OK | 主键冲突 | OK | OK | OK | OK | OK | OK | OK |
整表查询 | OK | OK | OK | 主键冲突 | OK | OK | OK | OK | OK | OK | OK |
插入 | OK(***) |
OK | OK(***) |
等待(*) | OK | OK(***) | OK | OK(***) | OK(***) | OK | OK(***) |
单行更新 |
OK(***) |
OK | OK(***) |
等待(*) | OK | 等待(**) | OK | 等待(**) | 等待(**) | OK | 等待(**) |
整表更新 |
OK(***) |
OK |
OK(***) |
等待(*) | OK | 等待(**) | OK | 等待(**) | 等待(**) | OK | 等待(**) |
单行删除 |
OK(***) |
OK | OK(***) |
等待(*) | OK | 等待(**) | OK | 等待(**) | 等待(**) | OK | 等待(**) |
整表删除 | OK(***) |
OK | OK(***) |
等待(*) | OK | 等待(**) | OK | 等待(**) | 等待(**) | OK | 等待(**) |
等待(*): 如果先行的SQL提交,则基于更新后的数据,否则基于原来的数据
等待(**):如果先行的SQL提交,则报更新冲突的错误
注:黄色代表和读已提交不同的地方
3. PostgreSQL 的 串行化(SSI)和可重复读(SI)的区别
PostgreSQL的串行化(SSI)级别下,比可重复读(SI)多了是否能串行化的检查,如果在某个时点(SQL语句执行或提交时),发现串行化冲突,则终止事务并报错。还是用之前的例子来说明。
SSI下,2个事务按下面的顺序交替执行,在下面的第6步,PostgreSQL检出串行化冲突,因而报错并取消事务2。这样先提交的事务1就不违反可串行化原则。
1) 事务1:select * from tb1;
2) 事务2:select * from tb1;
3) 事务1:insert into tb1 values(5,'a');
4) 事务2:insert into tb1 values(6,'a');
5) 事务1:commit;
6) 事务2:commit;//报错
在可重复读(SI)时,上面的6)是能执行成功的。
*)上面的6)如果执行成功就不符合真正的串行化。假设2个事务按照 事务1->事务2 的顺序串行执行, 在2)中事务2应该能看到3)事务1插入的数据,这是不可能的;反之 事务2->事务1 亦然。
*)详细测试方法请参考:
并发事务下各数据库外部表现实测之一(SQL Server篇)
http://blog.chinaunix.net/uid-20726500-id-3883105.html