ttt

getttyent

(FreeBSD) PostgreSQLで、vacuumdb: vacuuming of datab

2008-05-15 23:59:00 | デジタル・インターネット

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 コメント

コメント日が  古い順  |   新しい順
だとすると、むしろmaintenance_work_memは減らし... (iakio)
2008-05-16 01:26:56
だとすると、むしろmaintenance_work_memは減らした方がいいんじゃないでしょうか。
あと、インデックスが巨大な場合はREINDEXしてやると良いと思います。
返信する
ありがとうございます。補足しておきました。 (本人)
2008-05-17 02:03:15
ありがとうございます。補足しておきました。
http://nhh.mo-blog.jp/ttt/2008/05/freebsd_postgre_e31a.html

なおインデックスの再構成ですが、変更よりも追記が圧倒的に多い性質のテーブルのためか、あまりインデックスのサイズは小さくなりませんでした。
だいたい、600MBが500MBに、程度でした。
返信する

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。