Postgresql ERROR: database is not accepting commands to avoid wraparound data loss in database

ERROR:  database is not accepting commands to avoid wraparound data loss in database "mydb"

HINT:  Stop the postmaster and vacuum that database in single-user mode.

You might also need to commit or roll back old prepared transactions

可以看到是一些临时表的age比较大,应该是由于事物没有及时提交,影响到了数据库autovacuum这些表.

mydb=> select b.nspname,a.relname,a.relfrozenxid,age(a.relfrozenxid) from pg_class a, pg_namespace b where a.relnamespace=b.oid and a.relkind='r' order by a.relfrozenxid::text::int8 limit 10;

   nspname   |             relname              | relfrozenxid |    age     

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

 pg_temp_51  | avcp_shower_online_log_tmp       |   1185660983 | 1903192163

 pg_temp_161 | tmp_avcp_mobile_phone_data       |   2811680917 |  277172229

 pg_temp_464 | avcp_shower_online_log_tmp       |   2854092324 |  234760822

mydb=> select datname,age(datfrozenxid) from pg_database where datname=current_database();

  datname  |    age     

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

 avcp_work | 2146485849

这里由于我进入单用户模式进行过vacuum,所以最大age表的age不等于数据库的age了,vacuum之前是相等的.

通过以下语句可以查找出age年龄大于vacuum_freeze_table_age的表:

 select datname,age(datfrozenxid) from pg_database where datname not in ('postgres','template0','template1') and age(datfrozenxid)>(select setting::int from pg_settings where name='vacuum_freeze_table_age')order by age(datfrozenxid) desc;

xidWarnLimit = xidStopLimit - 10000000 //如果xid上限达到xidWarnLimit,数据库会发出warning,让你进行vacuum

 xidStopLimit = xidWrapLimit - 1000000 //如果xid上限达到xidStopLimit,数据库会不允许任何会话申请事物号,必须进入single mode,进行vacuum freeze

vi src/include/access/transam.h   //MaxTransactionId在这里定义最大值为2^32

#define MaxTransactionId                        ((TransactionId) 0xFFFFFFFF)

 xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1) //xid是一个32位值,所以2^32右移一位

 xidVacLimit = oldest_datfrozenxid + autovacuum_freeze_max_age //如果xid达到autovacuum_freeze_max_age这个值,那么就算没开启自动vacuum,数据库也会进行vacuum

从以下代码可以看到报错原因:

src/backend/access/transam/varsup.c

         if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit))

        {

                /*

                 * For safety's sake, we release XidGenLock while sending signals,

                 * warnings, etc.  This is not so much because we care about

                 * preserving concurrency in this situation, as to avoid any

                 * possibility of deadlock while doing get_database_name(). First,

                 * copy all the shared values we'll need in this path.

                 */

                TransactionId xidWarnLimit = ShmemVariableCache->xidWarnLimit;

                TransactionId xidStopLimit = ShmemVariableCache->xidStopLimit;

                TransactionId xidWrapLimit = ShmemVariableCache->xidWrapLimit;

                Oid                     oldest_datoid = ShmemVariableCache->oldestXidDB;



                LWLockRelease(XidGenLock);



                /*

                 * To avoid swamping the postmaster with signals, we issue the autovac

                 * request only once per 64K transaction starts.  This still gives

                 * plenty of chances before we get into real trouble.

                 */

                if (IsUnderPostmaster && (xid % 65536) == 0)

                        SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);



                if (IsUnderPostmaster &&

                        TransactionIdFollowsOrEquals(xid, xidStopLimit))

                {

                        char       *oldest_datname = get_database_name(oldest_datoid);



                        /* complain even if that DB has disappeared */

                        if (oldest_datname)

                                ereport(ERROR,

                                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),

                                                 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",

                                                                oldest_datname),

                                                 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"

                                                                 "You might also need to commit or roll back old prepared transactions.")));

                        else

                                ereport(ERROR,

                                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),

                                                 errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",

                                                                oldest_datoid),

                                                 errhint("Stop the postmaster and vacuum that database in single-user mode.\n"

                                                                 "You might also need to commit or roll back old prepared transactions.")));

                }

                else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))

                {

                        char       *oldest_datname = get_database_name(oldest_datoid);



                        /* complain even if that DB has disappeared */

                        if (oldest_datname)

                                ereport(WARNING,

                                                (errmsg("database \"%s\" must be vacuumed within %u transactions",

                                                                oldest_datname,

                                                                xidWrapLimit - xid),

                                                 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"

                                                                 "You might also need to commit or roll back old prepared transactions.")));

                        else

                                ereport(WARNING,

                                                (errmsg("database with OID %u must be vacuumed within %u transactions",

                                                                oldest_datoid,

                                                                xidWrapLimit - xid),

                                                 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"

                                                                 "You might also need to commit or roll back old prepared transactions.")));

                }

解决方法:

postgres --single -D $PGDATA mydb

如果mydb比较大,可能会很慢,可以找出最大年龄的表vaccum,如下:

vacuum freeze tablename;

如果数据库不大,可以直接vacuum freeze;做整个库的vacuum.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值