Quantcast
Channel: PostgreSQL research
Viewing all articles
Browse latest Browse all 253

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

$
0
0

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事务更隐蔽 , 排查是需要注意。


Viewing all articles
Browse latest Browse all 253

Trending Articles