GraphQLのN+1問題に向き合う ~問題提起編~

はじめに

GraphQLを扱うデメリットとしてN+1問題が挙げられる。
なぜGraphQLだとN+1問題が起こってしまうのか考える。

そもそもN+1問題とは

そもそもN+1問題とはなにか。
N件のデータと、取得したそれぞれのデータに紐付くデータを取得する際に、DBへN+1回のアクセスが生じる問題のことをN+1問題という。

<例> ここに一つのブログシステムがある。扱うDBはRDBであり、ブログ投稿記事テーブル、ユーザテーブルの二つのテーブルでデータを管理する。 ブログ投稿記事一覧ページを表示する際に10件のブログ投稿記事を取得し、それぞれの記事の作者であるユーザも取得したい。このとき、ブログ記事はユーザのIDを持っており、そのユーザのIDを基にユーザの情報を取得する。

このような構成の場合、ブログ投稿記事一覧を取得するためのアクセス(1回)と、取得したそれぞれのブログ投稿記事に紐付くユーザを取得するためのアクセス(N回)が生じる。解消方法としては、JOIN句で一回のアクセスで取得してしまう、もしくは取得したブログ投稿記事が持つユーザIDを持つユーザだけを取得するSQLを作成して二回のアクセスで取得する。
以下の記事がわかりやすいと思う。

qiita.com

なぜGraphQLで問題視されるのか

上記の方法で取得すればなんの問題もないはずだが、なぜGraphQLで問題視されるのか。
気になって調べてみると二つの理由があった。

紐付いたリゾルバー切り出してしまった時の問題

こちらに関しては柔軟にしたばかりに、N+1問題が起きてしまったパターン。
どういうことかというと、GraphQLではリクエスト次第ではユーザ情報を含めないようレスポンスを要求することも可能なので、フィールド指定でユーザの情報が呼ばれた時にだけ、ユーザを取得するためのリゾルバが呼ばれるように切り出すことがある。確かにこの切り分けは便利で要求された時のみDBアクセスが走るのでパフォーマンスが良い。
だけども要求された時には毎回、内部でリゾルバーが呼び出されてN+1問題が生じるという話。

毎回網羅的に取得するようにしてしまったときの問題

かといって毎回、JOINなりEager Loadingするとフィールドが要求されていないにときにも、毎回紐付くデータまで取得してしまう。
要求されていないデータまで取得されてしまうことでパフォーマンスの劣化も考えられるという話。

解決策

DataLoaderを使うといいらしい。。。
そのうちまとめよう。