image.png

問題

FuelPHPで大量のデータを総当たりで処理しようとするとメモリが足りずエラーになることがある。ギガを超える量のメモリを割り当てても動作しない。調べてみた。

$query = DB::select('*')->from('member_data')->as_object()->execute();

データの件数は12万件くらい。ORMはオーバーヘッドが大きいのでクエリビルダーで書いたけど、そんなレベルの話ではないみたい。

Fatal Error!

ErrorException [ Fatal Error ]:
Allowed memory size of 134217728 bytes exhausted (tried to allocate 4194312 bytes)
COREPATH/classes/database/pdo/connection.php @ line 300

エラーの内容は上記のとおり。メモリーを使い切ってしまっている。

// Convert the result into an array, as PDOStatement::rowCount is not reliable
if ($as_object === false)
{
    $result = $result->fetchAll(\PDO::FETCH_ASSOC);
}
elseif (is_string($as_object))
{
    $result = $result->fetchAll(\PDO::FETCH_CLASS, $as_object);
}
else
{
    $result = $result->fetchAll(\PDO::FETCH_CLASS, 'stdClass');
}

https://github.com/fuel/core/blob/1.8.0.4/classes/database/pdo/connection.php#L286-L298
FuelPHPのデータアクセスの仕組みはこんなふうになっている。fetchAllで全てのデータを一気に取り込んで配列化する。メモリを贅沢に使ってパワーで回す感じ?

メモリさえあればFuelPHPは軽快に動作するみたいだけど、たとえばコンバート処理とか、数十万件を超える規模のデータをバッチで回すようなケースだと、いくらメモリを足しても限度がある。取得するフィールドを絞り込めば動作するかもしれないけど、それも程度問題だ。データは今後も増え続けるから、知らない間にまた動作しなくなる可能性は十分にある。

解決方法

下記のように書き換えてみた。

$db = Database_Connection::instance()->connection();
$result = $db->query('select * from member_data');
while($row = $result->fetch(PDO::FETCH_OBJ))
{
    echo sprintf('ID:%s 名前:%s<br />',$row->id,$row->name);
}

結果セットだけ取り寄せて1件ずつ順番に処理するので、これならメモリを食わない。PDOとか生SQLではこういう書き方を普通にするけど、FuelPHPのコア内ではこういう記述は一ヶ所もない。

SQLサーバが物理的に異なるロケーションにある場合とか、何十万件もクエリを打つのはそれはそれで重いかもしれない。ポインターを進めながら1万件ずつくらいデータを取ってくる処理に書き換えてもいいんだけど、レスポンスを気にしない変換バッチだし面倒なので今回はやらなかった。

$db = Database_Connection::instance()->connection();
$query = DB::select('*')->from('member_data')->compile();
$result = $db->query($query);
while($row = $result->fetch(PDO::FETCH_OBJ))
{
    echo sprintf('ID:%s 名前:%s<br />',$row->id,$row->name);
}

クエリビルダーでFuelっぽく書きたいなら、上記のようにcompile()を使う。

https://github.com/fuel/core/blob/1.9/develop/classes/database/pdo/result.php
開発中のv1.9ではこんな処理が加わっている。このファイル自体がv1.8の時点では存在しない。どうしたいのかまだよく分からないけど、もしかしたら1件ずつの取得が普通にできるようになるのかも?Fuel的には推奨しない気もするけど、アプローチが増えるのは嬉しい。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.