暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL Greenplum crash 后临时表引发的BUG - 暨年龄监控的重要性

digoal 2016-03-24
580

作者

digoal

日期

2016-03-24

标签

PostgreSQL , Greenplum , 临时表 , 年龄 , xid wrap


背景

PostgreSQL 和 Greenplum 都支持临时表。

在使用临时表时,如果数据库crash,临时表不会被自动清除,这样可能会埋下隐患,隐患爆发时是非常危险的。

问题在哪呢?

因为vacuum freeze不处理其他会话创建的临时表,仅仅处理当前会话创建的临时表。

也就是说,没有被清理的临时表,可能导致数据库年龄无法下降。

但是PostgreSQL从8.4的版本开始autovacuum进程就有了自动清理未正常删除的TEMP表的功能。

并且PostgreSQL从8.4的版本开始如果将来还会继续在同一个temp schema中创建临时表的话,同样也会自动清理以前的未清除的临时表。

代码:

```
http://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=5b965bf08bfb4aa8928bafaed20e42b89de02a5c

            /*  
             * Check if it is a temp table (presumably, of some other backend's).  
             * We cannot safely process other backends' temp tables.  
             */  
            if (classForm->relpersistence == RELPERSISTENCE_TEMP)  
            {  
                    int                     backendID;

                    backendID = GetTempNamespaceBackendId(classForm->relnamespace);

                    /* We just ignore it if the owning backend is still active */  
                    if (backendID == MyBackendId || BackendIdGetProc(backendID) == NULL)  
                    {  
                            /*  
                             * We found an orphan temp table (which was probably left  
                             * behind by a crashed backend).  If it's so old as to need  
                             * vacuum for wraparound, forcibly drop it.  Otherwise just  
                             * log a complaint.  
                             */  
                            if (wraparound)  
                            {  
                                    ObjectAddress object;

                                    ereport(LOG,  
                                                    (errmsg("autovacuum: dropping orphan temp table \"%s\".\"%s\" in database \"%s\"",  
                                                             get_namespace_name(classForm->relnamespace),  
                                                                    NameStr(classForm->relname),  
                                                                    get_database_name(MyDatabaseId))));  
                                    object.classId = RelationRelationId;  
                                    object.objectId = relid;  
                                    object.objectSubId = 0;  
                                    performDeletion(&object, DROP_CASCADE, PERFORM_DELETION_INTERNAL);  
                            }  
                            else  
                            {  
                                    ereport(LOG,  
                                                    (errmsg("autovacuum: found orphan temp table \"%s\".\"%s\" in database \"%s\"",  
                                                             get_namespace_name(classForm->relnamespace),  
                                                                    NameStr(classForm->relname),  
                                                                    get_database_name(MyDatabaseId))));  
                            }  
                    }  
            }

```

而greenplum现在还使用的是8.3的版本,这个BUG还存在:

例子:

会话A:

postgres=# create temp table tmp1(id int); CREATE TABLE

不要断开会话

模拟数据库crash(-m immediate或kill -9某非数据库postmaster进程)

pg_ctl stop -m immediate waiting for server to shut down.... done server stopped

重启数据库

pg_ctl start

重启后,临时表并没有自动被清除。

postgres=# select nspname,relname,age(relfrozenxid) from pg_class a, pg_namespace b where a.relnamespace=b.oid and a.relname='tmp1'; nspname | relname | age -----------+---------+----- pg_temp_2 | tmp1 | 1 (1 row)

模拟几个事务:

```
postgres=# select txid_current();
txid_current


     1798

(1 row)

postgres=# select txid_current();
txid_current


     1799

(1 row)

postgres=# select txid_current();
txid_current


     1800

(1 row)

postgres=# select txid_current();
txid_current


     1801

(1 row)
```

这个临时表会导致数据库的年龄无法降低

postgres=# vacuum freeze; VACUUM postgres=# select nspname,relname,age(relfrozenxid) from pg_class a, pg_namespace b where a.relnamespace=b.oid and a.relname='tmp1'; nspname | relname | age -----------+---------+----- pg_temp_2 | tmp1 | 6 (1 row) postgres=# select datname,age(datfrozenxid) from pg_database where datname='postgres'; datname | age ----------+----- postgres | 6 (1 row)

如果不处理的话,久而久之,数据库可能会到达最大年龄,需要停库维护。

在修复这个BUG前的处理方法

drop table pg_temp_2.tmp1;

对于PostgreSQL 8.4以及以上的版本, 不需要太担心以上问题。

对于现在的Greenplum,可能存在的问题更复杂,比如只是某些SEGMENT上没有清理,某些清理了,则只能使用UTIL模式连接到GP的每个节点(包括master)取DROP TABLE。

但是注意不要删除当前其他会话正在使用的TEMP TABLE.

```
PGOPTIONS="-c gp_session_role=utility" psql -h xx -p xx -U xx -d xx

postgres=# select b.nspname,relname,age(relfrozenxid) from pg_class a ,pg_namespace b where a.relnamespace=b.oid and b.nspname ~ 'temp' and relkind='r';
nspname | relname | age
------------+---------+-----
pg_temp_10 | t | 388
pg_temp_7 | t | 350
pg_temp_9 | t | 508
(3 rows)

drop table xxx.xx;
```

这个隐患比长事务,2PC事务更隐蔽 , 排查时需要注意。

PostgreSQL 许愿链接

您的愿望将传达给PG kernel hacker、数据库厂商等, 帮助提高数据库产品质量和功能, 说不定下一个PG版本就有您提出的功能点. 针对非常好的提议,奖励限量版PG文化衫、纪念品、贴纸、PG热门书籍等,奖品丰富,快来许愿。开不开森.

9.9元购买3个月阿里云RDS PostgreSQL实例

PostgreSQL 解决方案集合

德哥 / digoal's github - 公益是一辈子的事.

digoal's wechat

文章转载自digoal,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论