DoctrineのN+1問題の解決策
code:php
$qb = $this->entityManager->createQueryBuilder();
$Customers = $qb
->from(Customer::class, 'c')
->select('c')
->addSelect('p') // この行を追加
->leftJoin('c.Pref', 'p') // この行を追加
->getQuery()
->getResult();
foreach ($Customers as $Customer) {
dump($Customer->getPref()->getName());
}
これはSQLのjoinのコストと、ぐるぐるSQLのコストを比較している これ、かなり最悪だなmrsekut.icon
「qbの利用時にgetPrefするなら」、上のようにjoinを書いておくと速くなる
しないなら不要
つまり、ここでの$Customersが、どのように使用されるかによって、qbを組み立てる箇所を修正する必要がある
これはServerだけでは収まらず、twig上でのアクセスでも考慮しないといけない
例えば、getPrefしないのに、2行を書いた場合と書かなかった場合では、後者の方が速かったりするのだろうか #?? 知らんけど当然そうだろうなmrsekut.icon
そうでなきゃ、全てのqbに全tableをjoinするように書いときゃ良い、になる
コードとしてはクソ汚くなるけど、ええやろ
qbを組み立てる際にその2行が何故書かれているのか意図がわかりにくい
コメントが必須になってしまう
Symfonyの設計が悪いのか
Doctrineの設計が悪いのか
ORMが悪いのか
Lazy Loadなのが悪いのか
単に書き方が問題なのか
どの辺にトレードオフがあるのか理解していない #?? 説明があった
defaultでLAZYである
必要になった時に遅延読み込みする
しかし、N+1問題に遭遇する
SQLで全部のデータを取ってくるのでアクセス回数は減って速い
が、必要以上にデータを取ってくる可能性も高いのでその分のコストはアリうる
しかしこれをEntity定義時に指定しないといけないの渋すぎない #?? query発行する際に指定できるべきだと思うけどmrsekut.icon
他に思いつく解決法
SQL内で全てのpref->nameまで取得してしまう方が良い
これはこれで重そうな気もするが、ぐるぐるSQLよりはマシだったりするんだろうか