2016-04-25 32 views
1

データベースのすべてのレコードを処理する必要があるデータベース管理タスクがいくつかあります。これは、CakePHPのの3.x ORMで、私はこのような何かを行うことができることを私の理解であり、それは今まで一度にメモリ内の1つのレコードを持っているでしょう:「レコードの読み取りごとにメモリ使用量が増加する

$records = TableRegistry::get('Whatever')->find(); 
foreach ($records as $record) { 
    // do some processing 
} 

しかし、これが最終的にクラッシュしますメモリ不足の例外です。 memory_get_peak_usageのロギングを少し追加しましたが、foreachループ内で発生したロギング以外は何もない場合でも、繰り返しごとに増加しています。デルタはループのたびに約12Kです。

私は3.2.7を実行しています。デバッグやSQLログを有効にしているかどうかにかかわらず、結果は似ています。 gc_collect_cycles()への頻繁な呼び出しを追加すると、処理が遅くなりますが、メモリ使用量には役立ちません。

これは予定ですか、またはバグですか?前者の場合、私は別にこのコードのを行うことができますか? (明らかに、私はより小さなバッチでそれを処理することができましたが、それはエレガントな解決策ではありません)

+0

あなたはオフにしようとしたことがあり[**結果バッファリングは、**](http://book.cakephp.org/3.0/en/orm/retrieving-data -and-resultsets.html#working-with-result-sets)? – ndm

+0

@ndm、それは有望だったので、私はいくつかの簡単なテストを実行しましたが、いくつかのバッファがオフになってメモリ使用量が*高速に*増加しているように思われました。私のテストで何かが間違っていた可能性があります...他のテストがクラッシュし、 "他のバッファリングされていないクエリがアクティブな間にクエリを実行できません"と言いました。私はそれについて考える必要があり、小規模なバッチで行う必要がある大きなクエリを実行するよりも、コードへの影響が少ないソリューションがあるかどうかを確認する必要があります。 –

答えて

0

私が理解したように、オブジェクトの反復処理を開始するときにORMを使ってクエリービルドを実行すると、記録)。したがって、すべてのデータがメモリにロードされ、各エントリを1つずつ繰り返します。

メモリ使用量を制限したい場合は、limitoffsetを調べることをお勧めします。これらを使用すると、作業するサブセットを抽出して、メモリの使用を制限することができます。

+0

私は、新しいORMが、この方法で使用されるときに、レコードを一度に1つだけ取り出すと考えました。結果セットで 'toArray'を呼び出すと、すべてを一度に読み込むことができますが、それが進むにつれて増加しているという事実は、そうでないことを示すようです。 –

+0

私はあなたが[怠け者の評価](http://book.cakephp.org/3)を混乱させていると思います。0/ja/orm/query-builder.html#how-are-queries-lazily-evaluated)私はORMの専門家ではありませんが、[クエリービルダー](http://book.cakephp.org/3.0/en/orm/query-builder.html)と[コレクション](http ://book.cakephp.org/3.0/ja/core-libraries/collections.html)は、クエリの最適化に役立ちます:)また、[Advanved querying](https://www.youtube.com/watch? v = rBRy5BiCeew)ORMの能力を実証しています。あなたにも便利かもしれません:) –

+0

一度にすべてのレコードをメモリに取り込んでから一度に1つずつ移動すると、すぐにクラッシュします。 (または、私が小さい数字を読んだ場合、メモリはピーク時の使用率にすぐにジャンプし、そこには比較的変化がないように座るだけです)。メモリ使用量がクラッシュするまで、各繰り返しで安定した量だけ増加するという事実は、一度に1つのレコードしか取り出しませんが、次のレコードに行くときにはオブジェクトを破棄しません。これは私にはとても分かります。おそらく私はそれをうまく説明していないでしょう。 –

0

CakePHP 3.x ORMには、ResultSetオブジェクトのクエリキャッシングが組み込まれています。結果セットを反復処理すると、エンティティは内部配列に格納されます。これはイテレータを巻き戻してループを再開できるように行われます。

大きな結果セットを1回だけ反復し、メモリ使用量を減らしたい場合は、結果バッファリングを無効にする必要があります。

$records = TableRegistry::get('Whatever')->find()->bufferResults(false); 
foreach ($records as $record) { 
    // do some processing 
} 

バッファリングをオフにすると、エンティティが結果セットからフェッチされ、その後に参照がないはずです。

この機能に関するドキュメントは、CakePHPの本にはありませんが、それはそうでなければなりません。ここで

は、APIのリファレンスです:

https://api.cakephp.org/3.3/class-Cake.Database.Query.html#_bufferResults

関連する問題