简介
Greenplum 数据库利用多版本并发控制(MVCC)来维护数据的一致性和管理对数据的并发访问。事务快照被用来控制哪些数据对一个特定的SQL语句是可见的。当一个事务读取数据时,数据库会选择一个特定的版本。这可以防止SQL语句查看由修改相同数据行的并发事务产生的不一致的数据,提供事务隔离 (transaction isolation)。MVCC允许数据库即使在繁重的活动中也能提供高并发性。
事务快照保持了一个数据库服务器的一致性,但没有提供跨多segments的一致性保证。
Greenplum 通过利用分布式快照扩展了快照模型,分布式快照在所有segments上同步一个快照。[1]当在Greenplum中运行一个语句时,coordinator会生成一个分布式快照,然后将其与查询一起发送给各segment。当每个segment收到分布式快照时,它会创建一个本地快照,将local transaction ID (xid)映射到分布式 xid。这就是Greenplum维护整个集群数据一致性的方式。
隔离级别和事务快照
Greenplum 提供两个级别的事务隔离:READ COMMITTED和REPEATABLE READ。
READ COMMITTED是默认的。当一个事务使用READ COMMITTED时,一个SELECT查询在查询开始运行时看到的是数据库的快照。如果其他事务在语句之间提交变化,在同一事务中运行的后续SELECT可能看到不同的数据。
在REPEATABLE READ中,单个事务中的所有语句只能看到事务中运行的第一个查询或数据修改语句(INSERT、UPDATE或DELETE)之前提交的行。单个事务中的后续SELECT语句总是看到相同的数据,也就是说,它们不会看到在REPEATABLE READ事务中提交的其他事务所做的更改。
所有交易都存储一个快照,由以下字段定义:
-
xmin - 仍处于活动状态的最早的xid。在这之前的所有事务都保证会被提交或中止。
-
xmax - 第一个未分配的xid。所有 xids >= to xmax 都还没有开始,对快照来说是看不见的。
-
xip_list - 在快照发生时活跃xid。
每个元组都有一个xmin,被设置为INSERT或UPDATE事务的xid。 一个元组 的xmax由UPDATE或DELETE语句的xid设置。
快照导出
一个事务可以导出它正在使用的快照。只要该事务保持活跃,其他事务可以导入快照,保证他们看到的数据库状态与原始事务相同。[2]
快照是用pg_export_snapshot()导出的,用SET TRANSACTION SNAPSHOT导入。
让我们来看看一个基本的例子。
我们在表foo中插入10行,然后在REPEATABLE READ中开始一个事务,并导出快照。
会话1:
postgres=# create table foo(a int); insert into foo select generate_series(1,10);
postgres=# BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
postgres=# select pg_export_snapshot();
pg_export_snapshot
---------------------
00000005-0000009F-1
postgres=# select count(*) from foo;
count
-------
10
第二个会话出现了,插入另外10行。
postgres=# insert into foo select generate_series(11,20);
INSERT 0 10
postgres=# select count(*) from foo;
count
-------
20
因为会话1的事务隔离级别是REPEATABLE READ,它不能看到额外插入的10行。
会话1:
postgres=# select count(*) from foo;
count
-------
10
然后我们可以为会话2设置TRANSACTION SNAPSHOT,得到与会话1相同的数据库状态。
会话2:
postgres=# BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
BEGIN
postgres=# SET TRANSACTION SNAPSHOT '00000005-0000009F-1';
SET
postgres=# select count(*) from foo;
count
-------
10
在END之后,我们又看到了所有的20行。
会话2:
postgres=# END;