看例子:
开两个终端来对比:
在终端A:
[pgsql@localhost bin]$ ./psql psql (9.1.2) Type "help" for help. pgsql=# begin; BEGIN pgsql=# select xmin,xmax,cmin,cmax,* from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+-----------+---- 1878 | 0 | 0 | 0 | 1 | 1 1884 | 0 | 0 | 0 | 999888777 | 2 (2 rows) pgsql=# insert into tab01 values(3,'3'); INSERT 0 1 pgsql=# insert into tab01 values(4,'4'); INSERT 0 1 pgsql=# select xmin,xmax,cmin,cmax,* from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+-----------+---- 1878 | 0 | 0 | 0 | 1 | 1 1884 | 0 | 0 | 0 | 999888777 | 2 1885 | 0 | 0 | 0 | 3 | 3 1885 | 0 | 1 | 1 | 4 | 4 (4 rows) pgsql=#
此时的终端B:
[pgsql@localhost bin]$ ./psql psql (9.1.2) Type "help" for help. pgsql=# select xmin,xmax,cmin,cmax, * from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+-----------+---- 1878 | 0 | 0 | 0 | 1 | 1 1884 | 0 | 0 | 0 | 999888777 | 2 (2 rows) pgsql=#
然后再在终端A进行提交:
pgsql=# commit; COMMIT pgsql=# select xmin,xmax,cmin,cmax,* from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+-----------+---- 1878 | 0 | 0 | 0 | 1 | 1 1884 | 0 | 0 | 0 | 999888777 | 2 1885 | 0 | 0 | 0 | 3 | 3 1885 | 0 | 1 | 1 | 4 | 4 (4 rows) pgsql=#
此时,再在终端B进行观察:
pgsql=# select xmin,xmax,cmin,cmax, * from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+-----------+---- 1878 | 0 | 0 | 0 | 1 | 1 1884 | 0 | 0 | 0 | 999888777 | 2 1885 | 0 | 0 | 0 | 3 | 3 1885 | 0 | 1 | 1 | 4 | 4 (4 rows) pgsql=#
继续研究cmin是咋回事:
pgsql=# begin; BEGIN pgsql=# insert into tab01(id,cd) values(generate_series(5,6),'xx'); INSERT 0 2 pgsql=# select xmin,xmax,cmin,cmax,* from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+-----------+---- 1878 | 0 | 0 | 0 | 1 | 1 1884 | 0 | 0 | 0 | 999888777 | 2 1885 | 0 | 0 | 0 | 3 | 3 1885 | 0 | 1 | 1 | 4 | 4 1886 | 0 | 0 | 0 | 5 | xx 1886 | 0 | 0 | 0 | 6 | xx (6 rows) pgsql=#
可以说cmin可理解为一个事务里,执行了几次sql命令的顺序。
那么cmax呢?在前面的基础上继续执行,居然没有看到区别:
pgsql=# update tab01 set id=2 where cd = '2'; UPDATE 1 pgsql=# select xmin,xmax,cmin,cmax,* from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+----+---- 1878 | 0 | 0 | 0 | 1 | 1 1885 | 0 | 0 | 0 | 3 | 3 1885 | 0 | 1 | 1 | 4 | 4 1886 | 0 | 0 | 0 | 5 | xx 1886 | 0 | 0 | 0 | 6 | xx 1886 | 0 | 1 | 1 | 2 | 2 (6 rows) pgsql=# commit; COMMIT pgsql=# select xmin,xmax,cmin,cmax,* from tab01; xmin | xmax | cmin | cmax | id | cd ------+------+------+------+----+---- 1878 | 0 | 0 | 0 | 1 | 1 1885 | 0 | 0 | 0 | 3 | 3 1885 | 0 | 1 | 1 | 4 | 4 1886 | 0 | 0 | 0 | 5 | xx 1886 | 0 | 0 | 0 | 6 | xx 1886 | 0 | 1 | 1 | 2 | 2 (6 rows) pgsql=#
经过反复折腾,终于发现,其实 cmin和 cmax就是一个东西:
看源代码:
/* ---------------- * heap_getsysattr * * Fetch the value of a system attribute for a tuple. * * This is a support routine for the heap_getattr macro. The macro * has already determined that the attnum refers to a system attribute. * ---------------- */ Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) { Datum result; Assert(tup); /* Currently, no sys attribute ever reads as NULL. */ *isnull = false; switch (attnum) { case SelfItemPointerAttributeNumber: /* pass-by-reference datatype */ result = PointerGetDatum(&(tup->t_self)); break; case ObjectIdAttributeNumber: result = ObjectIdGetDatum(HeapTupleGetOid(tup)); break; case MinTransactionIdAttributeNumber: result = TransactionIdGetDatum(HeapTupleHeaderGetXmin(tup->t_data)); break; case MaxTransactionIdAttributeNumber: result = TransactionIdGetDatum(HeapTupleHeaderGetXmax(tup->t_data)); break; case MinCommandIdAttributeNumber: case MaxCommandIdAttributeNumber: /* * cmin and cmax are now both aliases for the same field, which * can in fact also be a combo command id. XXX perhaps we should * return the "real" cmin or cmax if possible, that is if we are * inside the originating transaction? */ result = CommandIdGetDatum(HeapTupleHeaderGetRawCommandId(tup->t_data)); break; case TableOidAttributeNumber: result = ObjectIdGetDatum(tup->t_tableOid); break; default: elog(ERROR, "invalid attnum: %d", attnum); result = 0; /* keep compiler quiet */ break; } return result; }