【要約】【DB崩壊の罠】「とりあえずN+1問題」を解消しようとして巨大JOINを組み、メモリを食い潰した話 [Qiita_Trend] | Summary by TechDistill
> Source: Qiita_Trend
Execute Primary Source
// Problem
ECサイトの開発者が、注文履歴一覧画面の実装においてN+1問題を解消しようとした際に直面した問題である。
- ・N+1回避のため、全関連テーブルをLEFT JOINで結合した巨大なSQLを作成した。
- ・1対多のリレーションが重なり、デカルト積により取得行数が爆発的に増加した。
- ・重複データの転送量増大とORMの処理負荷により、JavaのOutOfMemoryErrorを引き起こした。
// Approach
開発チームは、単一の巨大なクエリに頼るのではなく、クエリを適切に分割して取得する手法を採用した。
- ・IN句による分割:親テーブルからIDを取得し、そのリストを用いて子テーブルをIN句で一括取得する。
- ・バッチ処理:データ量が多い場合は、IDリストを100件ずつ分割して段階的に取得する。
// Result
データの取得手法を「巨大JOIN」から「IN句による分割取得」へ変更したことで、以下の改善が見られた。
- ・SQL発行回数は1回から数回へ増えたが、N+1問題は回避された。
- ・デカルト積によるデータ爆発が解消され、ネットワーク転送量が劇的に削減された。
- ・メモリ消費とGC負荷が抑えられ、OOMによるシステムダウンを防ぐことが可能となった。
Senior Engineer Insight
> 「SQL発行回数の削減」という単一指標に固執する危険性を説いている。大規模システムでは、通信遅延だけでなく転送量とメモリ消費のトレードオフを考慮すべきだ。JOINによるデータ爆発は、リレーションが深まるほど指数関数的にリスクが高まる。実戦では、IN句による分割取得を標準とし、スケーラビリティを確保することが不可欠だ。