2008年5月16日金曜日

(FreeBSD) PostgreSQLで、vacuumdb: vacuuming of database "..." failed: ERROR: out of memory DETAIL: Failed on request of size 573885600.みたいなエラー

FreeBSDでportsでインストールしたPostgreSQL。デフォルトで、毎日、dailyによってvacuumdbが実行されるようになるんです。



ところで、あのスクリプト/usr/local/etc/periodic/daily/502.pgsqlって、いまいちなんです。vacuumdbに必ず-aオプションつけてるところが。これだと、変数daily_pgsql_vacuum_argsで、データベース名を指定することができないじゃないですか。



まあそれはいいとして、dailyのログメールで、ある日気がついたのですが、vacuumdbを実行したときに、



vacuuming of database "データベース名" failed: ERROR: out of memory DETAIL: Failed on request of size 573885600.



というようなエラーが出て、vacuumdbが失敗するようになってました。



まずいじゃん!ってことで、



vacuumdb --dbname データベース --verbose --analyze --table テーブル名



のように、テーブル名を1つずつ指定していき、どのテーブルでエラーが出るのか、調べてみたら、特定のテーブルでのみ、エラーが出ることがわかりました。



ネット検索して、いろいろ試しても、問題解決できなかったので、ソースコードを確認してみることに。



ktraceつきでvacuumdbを実行してみたら、



vacuumdb CALL  recvfrom(0x3,0x8063000,0x4000,0,0,0)
vacuumdb GIO   fd 3 read 101 bytes
       "E\0\0\0dSERROR\0C53200\0Mout of memory\0DFailed on request of size 536\
        870910.\0Faset.c\0L527\0RAllocSetAlloc\0\0"



という感じで、バックエンド(postgresプロセス。昔はpostmasterという名前だったアレ)から返ってきている、生のエラーメッセージのようなものが見えました。ここに、ソースファイルの名前、行番号、関数名のようなものが含まれているじゃないですか。



  • aset.c


  • L527


  • AllocSetAlloc


aset.cというファイルは、./backend/utils/mmgrにありまして、



static void *
AllocSetAlloc(MemoryContext context, Size size)



という関数で、



chunk_size = MAXALIGN(size);
blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ;
block = (AllocBlock) malloc(blksize);
if (block == NULL)
{
    MemoryContextStats(TopMemoryContext);
    ereport(ERROR,
            (errcode(ERRCODE_OUT_OF_MEMORY),
             errmsg("out of memory"),
             errdetail("Failed on request of size %lu.",
                       (unsigned long) size)));



という感じでになっているので、ここでmallocが失敗しているんでしょう。



というわけで、vacuumdbが出しているエラーではなく、バックエンド側で出しているエラーですね。



また



512*1024*1024 = 536870912



なので、536870910ってのは、512MBくらい。



こうなると、すぐにピンとくるのは





なんてもの。



もっとも、





なんてことがあったので、やみくもにkern.maxdsiz="2G"とかやっちゃっても、別のメモリがらみのエラーがでることもありました。



あぁ、アドレス空間が4GBまでしかない32bit OSは限界だな、なんていう感じもしてきます。



でも、FreeBSD/i386は、ちょっと、限界が低すぎじゃない?って気もします。





ところで、limitで広げるのは、postgresqlプロセスのほうであり、vacuumdbにたくさんメモリを与えても、ダメっすね。



ところで、maxdsizで広げても、root権限じゃないと512MBを超えられなかったような気がするんですが、気のせい?



postgresqlって、pgsqlというユーザーで走ってますよね。



気軽にpostgresqlを再起動できない事情がありまして、今はまだ、あれこれ試すことができません。





postgresql.confで、maintenance_work_mem = 512MB とか書いてあったんですが、これを増やしても、なんかダメ。



だいたい、vacuumdbがなぜそんなメモリを消費するのか、と。



あれ、vacuumdb --fullなら実行できました。なるほど、VACUUM ANALYZEがメモリ食いのもよう。



ということは、と思ってpgadmin3で、テーブルのサイズなど見てたら、テーブルのサイズはどうやら4GBをとうに超えてましたが、それよりも驚いたことに、インデックスのサイズが600MBを超えてました。



近頃は、psqlでvacuumを実行すると



データベース=> vacuum テーブル名;
ERROR:  out of memory
DETAIL:  Failed on request of size 609512400.



なんてことになってて、600MBくらいの値。
ひょっとして、こっち、インデックスのサイズか?!



そんな巨大なインテックスって、なんか効率が悪そう。そもそも、テーブルの設計が悪いんじゃないか?



という気がしてきまして、テーブルをいくつかに分割しようかと思います。



それでも、寿命を少しのばす程度かもしれません。



それまでの間に、FreeBSD/amd64が、もっと使いやすくなっているといいんですけど。





■ つづく





2 件のコメント:

  1. だとすると、むしろmaintenance_work_memは減らした方がいいんじゃないでしょうか。
    あと、インデックスが巨大な場合はREINDEXしてやると良いと思います。

    返信削除
  2. ありがとうございます。補足しておきました。
    http://nhh.mo-blog.jp/ttt/2008/05/freebsd_postgre_e31a.html
    なおインデックスの再構成ですが、変更よりも追記が圧倒的に多い性質のテーブルのためか、あまりインデックスのサイズは小さくなりませんでした。
    だいたい、600MBが500MBに、程度でした。

    返信削除