2008年5月17日土曜日

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

前回の





についてコメントをいただきました。



  • maintenance_work_mem を減らせばよい


  • reindexすれば、インデックスのサイズが少なくなる


ありがとうございます。
それが大正解でした。





教えてもらったことをベースに、試行錯誤した結果をメモっておきます。



もうすでにテーブルを複数に分割してしまったので、前回とまったく同じ環境で実行していることにならないのですが、実行結果を検討してみて、それほど大きな違いは出ないと思われたので、そのままで行きます。



■ インデックスを作成しなおす



まず、REINDEXでインデックスを作成しなおすことで、インデックスが小さくなる、という件。



やってみました。ただ、いきなり、勘違いしておかしなことをやったよ、という例。



データベース=> reindex index インデックス名;
ERROR:  out of memory
DETAIL:  Failed on request of size 268435456.



うーん・・・out of memoryですか。



じゃあインデックスを削除して、最初から作り直せばいい・



データベース=> drop index インデックス名;
DROP INDEX



データベース=> create index インデックス名 on テーブル名 using btree (列);
ERROR:  out of memory
DETAIL:  Failed on request of size 268435456.



・・・あんたバカ?



REINDEXって、今あるインデックスを残したまま、新規にインデックスを作成し、正常にインデックス作成完了したとき、従来のインデックスを新インデックスへ、つけかえるんですよね、きっと。だから、こうなるのは、当たり前。



先に、out of memory問題を解決しておきますか。



■ VACUUM ANALYZE、REINDEX、CREATE INDEXなどでのout of memoryエラーと、maintenance_work_memの関係について



maintenance_work_memは、VACUUM ANALYZE、REINDEX、CREATE INDEXなど、たまに実行したくなる、主にメンテナンス目的の処理のとき使うメモリの量を指定するものだそうです。



http://www.postgresql.jp/document/pg826doc/html/populate.html#POPULATE-WORK-MEM



http://www.postgresql.jp/document/pg826doc/html/runtime-config-resource.html#GUC-MAINTENANCE-WORK-MEM



すっかり勘違いしていたんですが、

そういったメンテナンス処理をするためのメモリが不足したからout of memoryになった

というわけではなくて、



  1. maintenance_work_memに大きな値を指定してあった。


  2. maintenance_work_memの値まではメモリが使えるものだ、とpostgresqlが考え、どんどんメモリを確保していった


  3. あるところで、OSのリソース制限値に引っかかって、メモリ確保が失敗した


  4. postgresqlは、なんだよ~、メモリが確保できないじゃないか。じゃあ、エラーにするよ


ということがおきているみたいです。



正解のやり方は、こうなのでした。



  1. maintenance_work_memには、OSのリソース制限値未満の、そこそこの値を指定しておく。


  2. maintenance_work_memの値まではメモリが使えるものだ、とpostgresqlが考える。これは同じ。


  3. maintenance_work_memの値まで確保してしまったら、確保済みのメモリの範囲内で、なんとかやりくりして、処理を行う


  4. メモリが少なくて、処理時間が増えるかもしれないけど、一応、最後まで終了する。


扱うテーブルサイズや、インデックスサイズがmaintenance_work_memより大きくたって、どうやら、ちゃんとやりくりして、処理してくれるようです。



できれば、OSのリソース制限に引っかかってエラーになった場合は、その時点までに確保済みのメモリだけで、なんとかやりくりして、処理を継続してくれればよかったのに、と思うのですが、そこまで気の利いたことは、今のPostgreSQLはやってくれないみたいです。



■ maintenance_work_memの値の変更方法



FreeBSDのportsで普通にインストールした場合ですが、initdbした後、/usr/local/pgsql/data/postgresql.confというファイルができるので、このファイルにmaintenance_work_memを指定します。



postgresql.confのコメントをじっくりと読めばちゃんと書いてありますが、postgresql.confは、postgresqlの起動時に読み込まれて、サーバが実行中のときにファイルpostgresql.confを書き換えても、反映はされません。
そして、pg_ctl reloadすれば、postgresql.confが読み込まれて、設定した値が反映される、と書いてあります。



FreeBSDの場合は、reloadしたいときは、

/usr/local/etc/rc.d/postgresql reload

でOKです。



ただし、postgresql.confで指定できるパラメータの中には、reloadでは反映されず、restartして反映されるものもあります。change requires restartというコメントがついてますね。



maintenance_work_memは、reloadするだけで反映されます。
念のため、確認済みです。


■ maintenance_work_memの値と、OSのリソース制限



FreeBSDって、デフォルトでは、OSのリソース制限として、メモリのdataサイズが512MBまでに制限されてたりします。



この場合、maintenace_work_memは512MBを指定していいのか、っていうとダメで、メンテナンス以外の処理にもメモリは使われているわけなので、1プロセスのトータルで512MBになるように、512MBからある程度差し引いた値を、maintenance_work_memに指定しなければなりません。



topやpsでみると、



# ps auxww | grep postgres | grep -v idle
pgsql    38355 40.3 11.1 417152 377344  ??  Rs    9:49PM   0:23.21 postgres: ユーザー データベース [local] CREATE INDEX (postgres)
pgsql    57636  0.0  0.3 104744  8716  ??  Is   05PM    0:08.74 /usr/local/bin/postgres
pgsql    57638  0.0  2.6 104884 88808  ??  Ss   05PM    1:08.84 postgres: writer process    (postgres)
pgsql    57639  0.0  0.2 10976  7336  ??  Ss   05PM   43:39.05 postgres: stats collector process    (postgres)



普通に起動しているだけで、すでに100MB以上確保しているので、余裕をみて、256MBくらいまでなら、maintenance_work_memに指定しても大丈夫そうでした。



■ maintenance_work_memの値を、もっと大きくしたい



もっと大きくするには、OSのリソース制限を緩和してやればよいわけです。



csh系のシェルを使っているときは、limitで、現在の制限が見られます。



% limit
cputime      unlimited
filesize     unlimited
datasize     524288 kbytes
stacksize    65536 kbytes
coredumpsize unlimited
memoryuse    unlimited
vmemoryuse   unlimited
descriptors  7264
memorylocked unlimited
maxproc      3632
sbsize       unlimited



maintenance_work_memが制限されるのは、上記のうち、datasizeです。



なので、たとえばこれを800MBへ広げるために、「limit datasize 800m」とかやりたくなるんですが、これ、デフォルトでは不可能です。



「man setrlimit」を見ると、hard limitと、soft limit、2つの制限があるとか、あれこれ書いてあるんですが、

デフォルトのFreeBSDは、datasizeは512MBまで

と設定されちゃってます。OSが起動しちゃったら、もうこれを拡大することはできないようです。たとえroot権限でも。



じゃあどうやって広げるかっていうと、



/boot/loader.confに



kern.maxdsiz="2G"



とか書いておけば、2GBまで、datasizeを広げられるようになります。

   

参考





% limit
cputime      unlimited
filesize     unlimited
datasize     2097152 kbytes
stacksize    65536 kbytes
coredumpsize unlimited
memoryuse    unlimited
vmemoryuse   unlimited
descriptors  11095
memorylocked unlimited
maxproc      5547
sbsize       unlimited



これでいいのかと思ったのですが、このようにkern.maxdsizに大きな値を指定しても、それでもまだ512MBまでしか増やせない、って場合があります。



それでもまだ512MBに制限されている原因はどこか?っていうと、/etc/login.confでした。



default:\
        :passwd_format=md5:\
        :copyright=/etc/COPYRIGHT:\
        :welcome=/etc/motd:\
        :setenv=MAIL=/var/mail/$,BLOCKSIZE=K,FTP_PASSIVE_MODE=YES:\
        :path=/sbin /bin /usr/sbin /usr/bin /usr/games /usr/local/sbin /usr/loca
l/bin ~/bin:\
        :nologin=/var/run/nologin:\
        :cputime=unlimited:\
        :datasize=unlimited:\
        :stacksize=unlimited:\
        :memorylocked=unlimited:\
        :memoryuse=unlimited:\
        :filesize=unlimited:\
        :coredumpsize=unlimited:\
        :openfiles=unlimited:\
        :maxproc=unlimited:\
        :sbsize=unlimited:\
        :vmemoryuse=unlimited:\
        :priority=0:\
        :ignoretime@:\
        :umask=022:



なんて感じに書かれているのが普通らしいのですが、なぜか、



        :datasize=512m:\



とか書かれていたりすると、この値までに制限されるようになります。そこの値よりも大きくはできません。ただし、root権限なら、この値を超えた値に増やせます。もちろん、それでも、kern.maxdsize未満という制限はあります。



・・・で、なぜか、私がPostgreSQLを実行していたFreeBSD 6-STABLEなマシンでは、/etc/login.confで、datasize=512m と書いてありました。
cvsで確認しても、datasize=512mと書かれていた痕跡は、まったくありません。
自分で昔書き換えて、すっかり忘れていただけ?



maintenace_work_memを大きくしてみました。712MBほど、メモリを確保しています。



# ps auxww | grep postgres | grep -v idle
pgsql    63641 57.1 16.8 712308 571596  ??  Ds   11:35PM   0:08.83 postgres: ユーザー データベース [local] CREATE INDEX (postgres)





1つ余談。



man setrlimitにも書かれているんですが、setrlimitで設定されたリソース制限は、プロセスについて設定される情報だそうです。
そのため、shellのbuiltinコマンドであるlimitやulimitで値を設定しておいてから、シェルから起動されるプロセスに引き継がれるようになっている、という仕組み。



login.confで、datasize=512mに制限していても、su rootして、unlimitすれば、512m以上に広げることができます。



そのあとsu pgsqlすれば、512m以上のままです。



しかし、su - pgsqlすると、ログインしなおしたのと同等のことがおこるので、login.confが反映されて、もとの制限値に戻ってしまいます。






もう1つ余談。



アカウントpgsql用に、/etc/login.confでlogin classを定義してやって、PostgreSQLを実行するときだけdatasizeを増やす、とかやると、なんかイイんじゃない?と思ってやってみました。


vipwで、パスワードファイルを編集。login classは、5番目のフィールドなので

pgsql:*:70:70::0:0:PostgreSQL Daemon:/usr/local/pgsql:/bin/sh

となっているのを

pgsql:*:70:70:pgsql:0:0:PostgreSQL Daemon:/usr/local/pgsql:/bin/sh

に変更。

/etc/login.confを編集して、末尾に

pgsql::datasize=2000m:

とか書き加える。

cap_mkdb /etc/login.conf を実行して、/etc/login.conf.db を作成。

これでいいかと思ったら、思わぬ落とし穴。
dailyで実行される/usr/local/etc/periodic/daily/502.pgsql がエラーを起こすようになりました。vacuumdb not foundみたいな・・・

あぁ、環境変数PATHに、/usr/local/binが入ってないじゃないですか。

/etc/login.confのdefaultにて、PATHに/usr/local/binが加えられていたんですね。
まあ、このあたりもちゃんと/etc/login.confのpgsqlクラスに加えてやればいいんじゃないかと思います。



0 件のコメント:

コメントを投稿