2014年12月3日水曜日

Symfony1.4のDoctrineで大量のデータ操作をした時に Allowed memory size...のエラーになる

Symfony1.4のDoctrineで、1万件くらいのデータを取り出してforeachで1件ずつ更新しようとしたところ、Allowed memory size..のエラーがでてしまいました。

PHP Fatal error:  Allowed memory size of 536870912 bytes exhausted (tried to allocate 32 bytes)

このエラーが出た時は、php.iniのmemory_limitを変更すれば、防げるようになるのですが、php.iniを変更できなかったり、変更したくないことも多いと思います。。

今回は、php.iniを変更したくなかったので、設定以外で対応したいと思っていたところ、Doctrineにはfree()というものがあり、オブジェクトを解放できることがわかりました。

free()は、Doctrine_RecordとDoctrine_Query、Doctrine_Collectionで使うことができるみたいです。

試しにテーブルから5000件抽出し、ループでupdated_atカラムを更新するプログラムを実行してみました。

// Usersテーブルから5000件のデータを抽出
$q = Doctrine_Query::create()
     ->from('User u')
     ->limit(5000)
     ->orderBy('u.id ASC')
     ->execute();

foreach ($q as $k => $val) {
      $val->updated_at = date("Y-m-d H:i:s");
      $val->save();
      if ($k % 500 == 0){
            printf("%d : %dKB\n", $k, (memory_get_usage(true) / 1024));
      }
      $val->free();
}
$q->free();
printf("End : %dKB\n", $k, (memory_get_usage(true) / 1024));


効果があるのかを判定するために、まずは$val->free()をコメントアウトして実行してみました。

0        : 94976KB
500   : 99072KB
1000 : 102912KB
1500 : 106752KB
2000 : 111616KB
2500 : 115712KB
3000 : 119552KB
3500 : 123392KB
4000 : 127232KB
4500 : 131840KB
End    : 137728KB

実行結果からもわかるように、メモリがどんどん増えていきます。今回はこれを増えないようにしたいので、$val->free()を有効にして実行してみます。

0       : 94976KB
500   : 95488KB
1000 : 95744KB
1500 : 96000KB
2000 : 97024KB
2500 : 97024KB
3000 : 97024KB
3500 : 97024KB
4000 : 97024KB
4500 : 97536KB
End    : 99584KB

多少は増えますが、なにもしないときよりも38144KBのメモリを抑えることができましたので、大量にデータを扱うときは対策をするべきだなと思いました。

また、テーブルを結合している場合は、引数にtrueを渡してfree(true)と指定することで、リレーションのオブジェクトもすべて解放することができます。

macOSでminikubeをインストールしようとしたら書き込みエラーになった

ローカル環境でKubernetesを使えるようにしようと環境構築中にエラーが 下記が今回のエラーで書き込みできない感じのメッセージが出ています。  $ brew install minikube Updating Homebrew... Error: The following ...