こんにちは!データベースの世界へようこそ。
普段、何気なく書いているSQL。「とりあえず動けばいいや」と思いがちですが、データ量が増えてくると急にクエリが遅くなって「あれ、なんで?」と焦ること、ありますよね。
今日は、PostgreSQLが裏側でどうやってデータをくっつけているのか、その中でも最もシンプルで、かつ奥が深い「ネステッドループ結合(Nested Loop Join)」についてお話しします。
専門用語だらけの教科書は一旦置いておいて、身近な例えでイメージを掴んでみましょう!
—
ネステッドループ結合って、何をしているの?
ネステッドループ結合を一言で言うと、「名簿と住所録を照らし合わせる作業」です。
想像してみてください。あなたは今、10人の友人に手紙を出そうとしています。
1. まず、手元にある「友人リスト(外側のテーブル)」から一人目の名前を見る。
2. 次に、分厚い「住所録(内側のテーブル)」を最初から最後までめくって、その人の住所を探す。
3. 見つかったら手紙を書き、次の人の名前を見て、また最初から住所録をめくる……。
これがネステッドループ結合の動きそのものです。
「外側のループ」で一人ずつ取り出し、そのたびに「内側のループ」を全探索する。まさに二重の繰り返し(Nested Loop)ですよね。
なぜこの手法が選ばれるの?
「えっ、住所録を毎回最初からめくるの? 非効率じゃない?」と思いましたか?
鋭いですね!でも、この手法が選ばれるのにはちゃんと理由があるんです。
- データがとにかく小さいとき:
相手が10人なら、住所録をペラペラめくるより、毎回探したほうが早いです。わざわざ住所録を「あいうえお順」に並べ替えたりする準備コストをかけるほうが時間がもったいないですから。
- 内側のテーブルに「インデックス(索引)」があるとき:
これが重要です。もし住所録に「あいうえお順のインデックス」がついていたらどうでしょう? 毎回最初からめくらなくても、パッとそのページを開けますよね。この状態なら、ネステッドループは爆速になります。
こんなときは要注意!「性能劣化」の罠
このネステッドループくん、実はちょっと「空気が読めない」ところがあるんです。
特に注意が必要なのは、「内側のテーブルが巨大で、かつインデックスがない(あるいは効いていない)とき」です。
先ほどの住所録の例で言えば、100万人分のデータがある住所録を、インデックスなしで毎回最初から探すようなもの。一人の住所を探すだけでヘトヘトになってしまい、それを10回、100回と繰り返せば……クエリはいつまで経っても終わりません。
データベースが「ネステッドループでいいや!」と判断したものの、実際にはインデックスがうまく使えず、CPUやメモリを無駄に消費して大惨事になる。これが、よくある「クエリ遅延」の正体の一つです。
エンジニアからのアドバイス
初心者の方に覚えておいてほしいのは、「データベースは、データ量を見て一番効率的な方法を自分で選んでいる」ということです。
もし、ある日突然クエリが遅くなったら、PostgreSQLが「ネステッドループ」を選んでいないか調べてみてください(`EXPLAIN`コマンドを使えば確認できますよ!)。
もし選ばれていて、かつ遅いなら:
1. 「内側のテーブルにインデックスを貼る」
2. 「そもそも、結合するデータ量が多すぎてこの手法に向いていない(別の結合手法が必要)」
という可能性を疑ってみてください。
—
技術の仕組みを知ると、データベースは単なる「データの入れ物」から「賢い助手」に変わります。焦らず、少しずつ仲良くなっていきましょうね。
また次回の記事では、ネステッドループが苦手な場面で活躍する「ハッシュ結合」について解説します。お楽しみに!
コメント