PHP
Yii

Yii frameworkのlazy loadingとeager loading

More than 5 years have passed since last update.

Post has many Comment というYii公式ガイドによく登場するモデルを例に記載してみます。


lazy loading(default)

$posts = Post::findAll();

foreach($posts as $post){
foreach($post->comments as $comment){
echo $comment->content;
}
}

最初にpostsがSELECTされ、$post毎にcommentsがSELECTされる。

SELECT回数は(1+post数)となり、いわゆるn+1問題が起きる。

(パフォーマンス上のボトルネックとして顕在化してきて初めて発覚するケースが多いかも。)

使い方によっては省メモリ。


eager loading

イーガーローディングはtogetherオプションにより、2つの手法を使い分けられるようになっている。


together => trueの場合

$posts = Post::findAll(['with'=>['comments' => ['together' => true]]]);

foreach($posts as $post){
foreach($post->comments as $comment){
echo $comment->content;
}
}

最初のSELECTでpostsとcommentsのオブジェクトが全てロードされる。

SELECTは1回だけ。

DBから返却されるデータセットとしては、postsが各コメントにくっついてくるため、

余分なデータがアプリケーションに返却され、冗長かつ無駄が多いが、

クエリ回数が少なくなる等、メリットがある。

Yiiでpostsオブジェクトまで冗長に生成はしてないはず。


together => falseの場合

$posts = Post::findAll(['with'=>['comments' => ['together' => false]]]);

foreach($posts as $post){
foreach($post->comments as $comment){
echo $comment->content;
}
}

最初にpostsがSELECTされ、次にpostsのidに一致する全てのcommentsがSELECTされる。

SELECT回数は2回。オブジェクトは全てロードされる。

DBからは必要なデータセットのみアプリケーションに返却されるためその点は効率的だが、クエリ回数が多くなる。

SQLがFROM comments WHERE comments.post_id IN (1,2,3,4,5,6,7,………)

みたいになるため、

postsの件数によってはIN句にIDが大変な数指定されることがあるので、ちょっと怖い。


おわりに

ここらへんの基本的なところは、

公式ガイドに書いてあるけど、

togetherでこのあたりを使い分けられるのはいいなーと思ったのと、

具体的にどう動くの?というのがなかったのでちょっと書いてみました。

どれがいいかは公式ガイドに記載のある通り、


絶対的な答えはありません。