Quantcast
Channel: CodeSection,代码区,数据库(综合) - CodeSec
Viewing all articles
Browse latest Browse all 6262

Greenplum的MVCC多版本控制的简单介绍(主要涉及cmin,cmax,xmin,xmax说明)

$
0
0

熟悉Greenplum数据库的朋友应该都知道,GP底层是使用PostgreSQL数据库来实行MPP架构的,而对于事务控制这一块,也是使用PostgreSQL的多版本控制MVCC,实现了读写分离,显然就会提高数据库每秒查询的性能。

在Read Commit事务隔离级别时,查询请求只读取查询请求之前已经提交的事务的数据更改,对当前版本的数据并不影响;

而DML语句,会操作当前版本。因此做到了读写分离的目的,提高数据库并发能力。

我们先来回顾一下PostgreSQL里面的MVCC多版本控制。

在PostgreSQL中,每一个事务都会得到一个被称作为 XID 的事务ID。这里说的事务不仅仅是被 BEGIN - COMMIT 包裹的一组语句,还包括单条的insert、update或者delete语句。当一个事务开始时,PostgreSQL递增XID,然后把它赋给这个事务。PostgreSQL还在系统里的每一行记录上都存储了事务相关的信息,这被用来判断某一行记录对于当前事务是否可见。举个例子,当你插入一行记录时,PostgreSQL会把当前事务的XID存储在这一行中并称之为 xmin。只有那些已提交的而且xmin比当前事务的XID小的记录对当前事务才是可见的。这意味着,你可以开始一个新事务然后插入一行记录,直到你提交( COMMIT )之前,你插入的这行记录对其他事务永远都是不可见的。等到提交以后,其他后创建的新事务就可以看到这行新记录了,因为他们满足了 xmin < XID 条件,而且创建那一行记录的事务也已经完成。

对于 DELETE 和 UPDATE 来说,机制也是类似的,但不同的是对于它们PostgreSQL使用叫做 xmax 的值来判断数据的可见性。这幅图展示了在两个并发的插入/读取数据的事务中,MVCC在事务隔离方面是怎么起作用的。

PostgreSQL使用xmin,xmax,cmin,cmax等标记来实现多版本,他们的含义为:

xmin:在创建记录(tuple)时,记录此时的事务id,后面每次update也会更新。

xmax: 在更新或删除tuple或者lock时,记录此时的事务id;如果记录没有被删除,那么此时为0。

cmin:插入该元组的命令在插入事务中的命令标识(从0开始累加)

cmax:删除该元组的命令在插入事务中的命令标识(从0开始累加)

但是对于Greenplum数据库来说,它毕竟是基于多个postgres实例来实现MPP架构的数据库,所以上面的标记的值可能与单个postgres有区别。下面我们示例中会说明。

#装载数据,非并行,如果并行加载数据的话,可以考虑使用gpfdist或gpload等方式

zhangyun_db=# COPY test_mvcc from '/home/gpadmin/mvcc.txt' with delimiter as '|' null as '';

COPY 4

zhangyun_db=# select * from test_mvcc ;

id | name

----+-----------

4 | Hadoop

3 | Greenplum

2 | Hive

1 | Spark

(4 rows)

zhangyun_db=# select t.*, t.xmin, t.xmax, t.cmin, t.cmax from test_mvcc t;

id | name | xmin | xmax | cmin | cmax

----+------------+--------+------+------+------

8 | Flink | 449908 | 0 | 0 | 0

4 | Hadoop | 449906 | 0 | 0 | 0

5 | HBase | 449775 | 0 | 0 | 0

7 | PostgreSQL | 457913 | 0 | 0 | 0

2 | Hive | 449910 | 0 | 0 | 0

3 | Greenplum | 449909 | 0 | 0 | 0

6 | HAWQ | 449899 | 0 | 0 | 0

1 | Spark | 449905 | 0 | 0 | 0

(8 rows)

从上图可以看出,8条记录的xmin是不一样的(如果是PostgreSQL数据库的话,这里应该是一样的,因为这些数据是通过同一个事务copy创建的)。

另外xmax都为0,说明数据自从导入后就没有被删除。

下面我们来演示在Greenplum数据中执行update的情况:

请打开两个linux终端A和B,方便数据比对和查看。

首先在终端A执行,但不提交:

zhangyun_db=# begin;

BEGIN

zhangyun_db=# update test_mvcc set name = 'Hive On Spark' where id = 2;

UPDATE 1

终端B查看:

zhangyun_db=# select t.*, t.xmin, t.xmax, t.cmin, t.cmax from test_mvcc t;

id | name | xmin | xmax | cmin | cmax

----+------------+--------+--------+------+------

4 | Hadoop | 449906 | 0 | 0 | 0

7 | PostgreSQL | 457913 | 0 | 0 | 0

6 | HAWQ | 449899 | 0 | 0 | 0

2 | Hive | 449910 | 450412 | 0 | 0

1 | Spark | 449905 | 0 | 0 | 0

8 | Flink | 449908 | 0 | 0 | 0

5 | HBase | 449775 | 0 | 0 | 0

3 | Greenplum | 449909 | 0 | 0 | 0

(8 rows)

可以看到,对于id为2的数据行的xmax发生了变化,但是数据本身是没有变化的,因为终端A的事务还没有提交。

接着,我们在终端A执行提交动作,如下:

zhangyun_db=# commit;

COMMIT

同时在终端B再查看:

zhangyun_db=# select t.*, t.xmin, t.xmax, t.cmin, t.cmax from test_mvcc t;

id | name | xmin | xmax | cmin | cmax

----+---------------+--------+------+------+------

2 | Hive On Spark | 450412 | 0 | 0 | 0

6 | HAWQ | 449899 | 0 | 0 | 0

5 | HBase | 449775 | 0 | 0 | 0

7 | PostgreSQL | 457913 | 0 | 0 | 0

4 | Hadoop | 449906 | 0 | 0 | 0

3 | Greenplum | 449909 | 0 | 0 | 0

8 | Flink | 449908 | 0 | 0 | 0

1 | Spark | 449905 | 0 | 0 | 0

(8 rows)

可以看到id为2的记录,其xmin已经变化了。

根据上面的结果,不知道大家有没有发现,对于Greenplum来说,更新或者删除都没有修改cmin和cmax的值。

在PostgreSQL中,cmin和cmax用于判断同一个事务内的其他命令导致的行版本变更是否可见。如果一个事务内的所有命令严格顺序执行,那么每个命令总能看到之前该事务内的所有变更,不需要使用命令标识。然而一个事务内存在命令交替执行的情况,比如使用游标进行查询。Fetch游标时看到的是声明游标时的数据快照而不是Fetch执行时,即声明游标后对数据的变更对该游标不可见。

这一块的内容,后续抽时间分析源码再写一篇文章进行分析。


Viewing all articles
Browse latest Browse all 6262

Trending Articles