本文介绍GrrenPlum的MVCC架构。GreenPlum作为分布式数据库,与传统事务型数据库系统通过锁机制来控制并发访问的机制不同, GPDB使用多版本控制(Multiversion Concurrency Control/MVCC)来保证数据一致性。MVCC意味着在查询数据库时,看到的只是数据的快照,可以确保当前的事务不会看到其他事务在相同记录上的修改。据此为数据库的每个事务提供事务隔离。
MVCC以避免给数据库事务显式锁定的方式,最大化减少锁争用以确保多用户环境下的性能。在并发控制方面,使用MVCC而不是使用锁机制的最大优势是, MVCC对查询(读)的锁与写的锁不存在冲突,并且读与写之间从不互相阻塞。
数据库中常见的并发技术有:
MVCC特点:
用来跟踪数据库中某个时间点所有事务运行的状态。Greenplum中快照的实现方式是通过在每个数据对象(如行、页、表)上维护多个版本的数据,并每个版本都有一个时间戳。快照只是一个逻辑概念,实际上并没有真正的数据副本。每个事务只是通过时间戳来确定自己所能看到的数据版本,而不是真正地复制一份数据。这样可以节省存储空间,并提高性能。
比如在Read Committed隔离级别下,当前查询可以读取到已提交的事务,但是不能读取到未提交的事务,此时查询开始时生成的快照,使用快照判断事务状态,只读取已提交事务。
创建测试表插入测试数据
--创建测试表
create table test_mvcc(
id int,
name varchar(100)
);
--插入测试数据
insert into test_mvcc
select gs,'name-'||gs from generate_series(1, 10000) gs;
--
select * from test_mvcc;
隐藏字段解读:
select xmin,xmax,ctid,tm.* from test_mvcc tm where id=1;
--结果为:
xmin xmax ctid id name
1823425 0 (0,1) 1 name-1
字段解释:
结果解读:
表明该行数据是由1823425事务创建,xmax=0说明没有被删除,不存在的事务。
实验一:删除操作:
在session-1窗口下开启事务,执行删除操作:
begin;
delete from test_mvcc tm where id=1;
在session-1窗口下查询对应数据:
select xmin,xmax,ctid,tm.* from test_mvcc tm where id=1;
--结果为
xmin xmax ctid id name
1823425 1823755 (0,1) 1 name-1
因为Read Committed隔离级别,未提交事务可见,该行数据仍然存在。但是可以发现xmax字段值更新,表明1823755事务正在删除改行数据,但是未提交或者已经回滚。
实验二:更新操作:
查看初始状况:
select xmin,xmax,ctid,tm.* from test_mvcc tm where id=2;
--结果
xmin xmax ctid id name
1823066 0 (0,1) 2 name-2
在session-1窗口下开启事务,执行更新操作:
begin;
update test_mvcc set name = 'test-2' where id=2;
select xmin,xmax,ctid,tm.* from test_mvcc tm where id=2;
--执行结果
xmin xmax ctid id name
1823460 0 (3,212) 2 test-2
同一事务中,可以看数据的xmin变化,并且name已经发生变化。虽然当前事务未提交,但是该事务中可以看到数据变化。
在session-2窗口下执行查询:
select xmin,xmax,ctid,tm.* from test_mvcc tm where id=2;
--查询结果
xmin xmax ctid id name
1823066 1823460 (0,1) 2 name-2
该行数据的name未发生变化,但是xmax变为1823460事务id,表明1823460事务在修改数据但是未提交或者已回滚。
并发读一定不会冲突,但是在MVCC架构中如何处理并发写/更新呢?
事务并发写时,如果发现事务快照中存在未提交事务,则等待事务完成时在写入。
由于MVCC使用快照的方式实现了多版本的高并发,在实验二中可以看到更新操作时实际上创建了一个新的元组,所以在大量写操作过后,需要清理旧的元组。可以通过VACUUM执行操作,避免表膨胀的问题。
清理数据的内容: