加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

转:postgresql 表年龄的一些理解

(2015-02-22 15:02:45)
标签:

股票

一直对postgresql的年龄问题有点模糊,这里记录下关于pg数据库年龄的相关问题,可能理解的不到位,欢迎指正。
1.由于pg没有像oracle、mysql那样的undo来实现多版本并发控制(mvcc),而是当dml时在表上创建新行,并在每行中用额外的列(xmin,xmax)来记录事务号(分别为插入或回滚时的事务号和update的事务号),以此实现多版本并发控制,当前事务只能看到比表上xmin事务号小的记录,txid的最大值为32bit位,即2^32为4294967296(40亿),当数据库的事务号到达最大值后事务号就用尽了,此时需要重新归零使用。

2.事务号用尽后就会归零,任何原来表上的数据的xmin均大于当前事务号,造成看不到以前的数据现象。当然数据库系统不会让这种情况发生,当数据库的年龄到达20亿时就要采取措施了,数据库中的表就需要清理事务号(使用vacuum freeze),以此来降低数据库表的年龄。降低数据库的年龄是auto vacuum 进程在表的年龄到达阀值后自动进行的,也可以vacuum freeze命令手动执行。auto vacuum 操作也有可能会进行部分行freeze而不是全表freeze。

3.数据库集群的各个数据库使用一个事务号系统,因此每个库的事务活动都会使集群的事务号推高。
4.数据库的年龄是指数据库中表的年龄,可以查询select datname,datfrozenxid from pg_database。

5.另外需要明确的是年龄是指当前系统事务号减去事务发生时的事务号的差值,因此在早期插入的数据行,随着事务号的分配,年龄会越来越大,另外还有个frozenxid的概念,即冻结事务号(pg_class的relfrozenxid自段记录此值),要分清表上数据行的xid和这个frozenxid是不同的,常规的行版本(也就是数据行上的xmin、xmax)会和当前事务号比较来决定数据是否可见,而frozenxid不参与事务号比较 ,如下:

5.1.数据库表的行版本(xmin、xmax):
会话1:
postgres=# begin;
BEGIN
postgres=# select txid_current();
 txid_current 
--------------
      4084684
(1 row)

postgres=# insert into t values(1,'mcl');
INSERT 0 1
postgres=# insert into t values(2,'mcl2');
INSERT 0 1
postgres=# select xmin,xmax,* from t;
  xmin   | xmax | id | name 
---------+------+----+------
 4084684 |    0 |  1 | mcl
 4084684 |    0 |  2 | mcl2
(2 rows)

不提交,然后开启会话2:

postgres=# select txid_current();
 txid_current 
--------------
      4084685
(1 row)

postgres=# select xmin,xmax,* from t;
 xmin | xmax | id | name 
------+------+----+------
(0 rows)

由于会话2的当前txid是4084685,而此时t表的数据的事务号都为4084684 ,小于事务4084685,因此查询t表时认为那两条数据是未来的,还未提交的,对会话2来说是不可见的。

5.2.数据表的年龄。
数据表的年龄是指产生事务时的事务号离当前事务号的距离,又分行版本的年龄和冻结(frozen)年龄
postgres=# select xmin,xmax,age(xmin),txid_current(),* from t;
  xmin   | xmax | age | txid_current | id | name 
---------+------+-----+--------------+----+------
 4084684 |    0 |   2 |      4084686 |  1 | mcl
 4084684 |    0 |   2 |      4084686 |  2 | mcl2
(2 rows)

postgres=# select xmin,xmax,age(xmin),txid_current(),* from t;
  xmin   | xmax | age | txid_current | id | name 
---------+------+-----+--------------+----+------
 4084684 |    0 |   3 |      4084687 |  1 | mcl
 4084684 |    0 |   3 |      4084687 |  2 | mcl2
(2 rows)

postgres=# select relfrozenxid,age(relfrozenxid) from pg_class where relname='t';
 relfrozenxid | age 
--------------+-----
      4084602 |  93
(1 row)

每次查询txid_current()都会增加一个事务号,另外增删改查也会增加系统的事务号,因此从上可以看出,随着数据库的增删改查等事务活动,会使txid_current的值一直增加,表数据的行版本age就越来越大,从pg_class查询的relfrozenxid是上次进行行版本冻结的事务号,小于此版本号的数据均为可见的,没有事务信息的。
下面进行freeze,降低表年龄。
postgres=# vacuum FREEZE t;
VACUUM
postgres=#  select xmin,xmax,age(xmin),txid_current(),* from t;
 xmin | xmax |    age     | txid_current | id |    name    
------+------+------------+--------------+----+------------
    2 |    0 | 2147483647 |      4084695 |  1 | mcl
    2 |    0 | 2147483647 |      4084695 |  2 | postgresql
(2 rows)

postgres=#  select relfrozenxid,age(relfrozenxid) from pg_class where relname='t';
 relfrozenxid | age 
--------------+-----
      4084695 |   1
(1 row)

以上执行了vacuum freeze t后,表数据的xmin变为2,age为2147483647,而relfrozenxid为4084695 ,age为1,为什么xmin的年龄显示为20亿? 这点容易让人迷惑,其实一旦进行全表freeze操作,现有的所有行都变为冻结状态,所以显示的xmin=2和age为2147483647代表行已经冻结过了,已经进行了提交,数据肯定可见,不再需要进行行版本比较,当数据库表的年龄到达20亿时就一定要进行vacuum freeze了,如果不进行vacuum freeze,数据库将无法继续提供服务。问题是,为什么要到20亿进行vacuum freeze呢?
下面是我的一些理解postgresql把40亿的事务号分成了两段,前20亿和后20亿,20亿个事务可以包含很多条数据,只要保证表的最早插入的数据不再需要前镜像了,就可以清除(或冻结)事务信息,同理,过了20亿后,又有大量数据插入,快到40亿时要把20-40亿事务之间的数据的行版本清除(或冻结),这样才事务号才可以归零重用。 (这段为我自己的理解,可能有误)。

另外命令行执行vacuum freeze是全表freeze,数据库表的现有行版本都算冻结了,即肯定是提交的、可见的,不再进行行版本比较,而后续插入数据的行版本仍然是有版本信息的,mvcc仍然需要进行行版本比较。

通常执行vacuum freeze这个操作是数据库系统的auto vacuum进程自动执行的,执行频率是有以下几个参数决定的:5.3.vacuum_freeze_table_age表示当表的年龄到达此阀值后将进行全表vacuum freeze,第一次操作会写大量的wal日志,过程会很慢,产生的日志量与表的大小差不多,因此如果此值太小将造成较大的io压力,但第二次vacuum freeze就小的多了,原因是不需要进行全表vacuum freeze了,只需要修改pg_class的relforzenxid,此值代表什么时候进行的vacuum freeze。
vacuum_freeze_table_age的默认值是1亿5千万,到达此年龄后将自动进行全表vacuum freeze:
postgres=# show vacuum_freeze_table_age;
 vacuum_freeze_table_age 
-------------------------
 150000000
(1 row)

5.4.autovacuum_freeze_max_age,此值代表当数据表的年龄到达此值时,不论是否开启auto vacuum,都将强制开启vacuum来降低数据库的年龄。
postgres=# show autovacuum_freeze_max_age ;
 autovacuum_freeze_max_age 
---------------------------
 200000000
(1 row)
此参数是指当数据库的年龄到达2亿后,需要强制vacuum freeze,而不管是否开启了auto vacuum参数。

5.5.vacuum_freeze_min_age,此值控制多旧的记录不被vacuum freeze,此值越大事务信息保留的时间越久,反之越短。行版本的年龄小于此值将不进行全表扫描,仅对特定行进行frozen,如果表从来没有被全表vacuum freeze过,且行版本大于vacuum_freeze_table_age-vacuum_freeze_min_age就会进行全表vacuum freeze
postgres=# show vacuum_freeze_min_age;
 vacuum_freeze_min_age 
-----------------------
 50000000
(1 row)

通过以上三个参数,pg数据库可以定期的清理表的事务信息,从而不至于当事务到达40亿而重新归零时原来表的数据行还有事务信息而造成mvcc紊乱。

参考:

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有