LoginSignup
2
2

More than 5 years have passed since last update.

Yii frameworkのlazy loadingとeager loading

Posted at

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でこのあたりを使い分けられるのはいいなーと思ったのと、
具体的にどう動くの?というのがなかったのでちょっと書いてみました。

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

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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2