PG的事务
为了保证事务的ACID特性,rdbms必须要实现并发控制。pg和oracle、mysql(innodb)数据库都使用MVCC来实现并发控制。MVCC通过数据变化时不断生成新版本对象和可查询一定范围的老版本对象来实现并发,MVCC保存数据在某个时间点的快照,读数时选择一个版本进行读取。
oracle、mysql都通过undo来记录老版本对象,pg没有undo,而是在DML时在直接将历史数据写在原表上(update会创建新行,delete标记行),并在表中记录额外的列xmin,xmax来记录事务号,通过对比事务号和一些其他信息来实现mvcc机制。
在众多关系型数据库中,pg的事务机制非常有特色,了解pg的事务机制是了解pg数据库运行原理的关键。
事务隔离级别
一般关系型数据库都可以设置多个不同的事务隔离级别。在不步的事务隔离级别下,事务并发行为有所不同
设置事务隔离级别
pg支持设置4种事务隔离级别(实际上只会生效有3个)
{ SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
事务隔离级别参数
default_transaction_isolation
:设置全局事务的默认隔离级别
transaction_isolation
:设置当前会话的事务隔离级别
默认隔离级别read committed
修改全局事务的默认隔离级别
直接修改default_transaction_isolation
参数,然后reload
即可
修改完成后,每个新的事物都会使用default_transaction_isolation
隔离级别
postgres=# alter system set default_transaction_isolation to 'serializable';
ALTER SYSTEM
postgres=# select pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
postgres=# show transaction_isolation;
transaction_isolation
-----------------------
serializable
设置当前会话的隔离级别
注意参数transaction_isolation
只是展示当前会话的隔离级别,这个参数是不可以直接修改的
lzldb=# alter system set transaction_isolation to 'REPEATABLE READ';
ERROR: parameter "transaction_isolation" cannot be changed
通过SET SESSION
修改会话的隔离级别,例如:
lzldb=# SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET
lzldb=# show transaction_isolation ;
-[ RECORD 1 ]---------+----------------
transaction_isolation | repeatable read
设置事务的隔离级别
pg可以指定事务本身的隔离级别
可通过在开启事务时设置事务的隔离级别,例如:
lzldb=# BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
lzldb=# start TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION
或者开启事务后set transaction
lzldb=# begin;
BEGIN
lzldb=*# set transaction ISOLATION LEVEL REPEATABLE READ;
SET
ANSI92的事务隔离级别
在ANSI SQL-92事务隔离级别标准中包含4种隔离级别:
Serializable(可串行化或称可序列号)
系统中所有的事务串行化执行,事务之间互不影响。
以串行地方式逐个执行,能避免所有数据不一致情况。
早期以排他锁来控制并发事务,串行化执行方式会导致事务排队,系统的并发量大幅下,不过ANSI 92后出现更多序列化实现方法,并发性和性能均有较大提升。
Repeatable read(可重复读)
一个事务一旦开始,事务过程中所读取的所有数据不允许被其他事务修改。可重复读是mysql的默认隔离级别。
注意, 在ANSI SQL中可重复读级别可发生幻读,但是pg的可重复读不会发生幻读
Read Committed(已提交读)
一个事务能读取到其他事务提交过的数据。
事务在处理过程中如果重复读取某一个数据,而且这个数据恰好被其他事务修改并提交了, 那么当前重复读取数据的事务就会出现同一个数据前后不同的情况。 已提交读是oracle、pg的默认隔离级别
在这个隔离级别会发生“不可重复读”和”幻读“的场景。
Read Uncommitted(未提交读)
一个事务能读取到其他事务修改过,但是还没有提交的(Uncommitted)的数据。
数据被其他事务修改过,但还没有提交,就存在着回滚的可能性,这时候读取这些“未提交” 数据的情况就是“脏读”。
在这个隔离级别会发生“脏读”场景。
pg没有未提交读这个隔离级别,设置未提交读会被当做已提交读
标准的一致性读和隔离级别矩阵
事务隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
未提交读 | 可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
序列化 | 不可能 | 不可能 | 不可能 |
pg的一致性度和隔离级别矩阵
事务隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
未提交读 | 不可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 不可能 |
序列化 | 不可能 | 不可能 | 不可能 |