X(Twitter) Zenn GitHub RSS 共有

パフォーマンスの良いSQLを書く上で最低限考える事

作成日時:2025-06-20
更新日時:2025-06-20

パフォーマンスの良いSQLを書く上で最低限考える事。

  1. 最少・最少・最短・最速の原則
  2. SQLを書いたら実行計画をとる

単語

記事中にある単語は次の意味を持つ。

1.最少・最少・最短・最速の原則

SQLを書くときはパフォーマンスを上げるため、下記の原則を順守する。

  1. DBから取得するデータ量は最少でなければならない
  2. DBにSQLを発行する回数は最少でなければならない
  3. トランザクションは最短でなければならない
  4. SQLそのものが最速でなければならない

1-1.DBから取得するデータ量は最少でなければならない

1KBのデータと1GBのデータ、全量取得する時間が速いのはどちらか。無論、前者である。
故にパフォーマンスを向上させるため、SQLで取得するデータ量は最少にしなければならない。
必要最小限を取る。

具体的には下記の対応を行う。

この原則を守らない場合、パフォーマンスの低下とネットワークコストの増大を招く。
特にクラウドにおいては、クラウドのコスト増大に繋がりかねない。

1-2.DBにSQLを発行する回数は最少でなければならない

例えば、100個の荷物をA地点からB地点に運ぶとする。
1個ずつ運んで100往復するのと、100個まとめて運んで1往復するのはどちらが速いか。

AP-DB間には物理的距離/各種ネットワーク機器が存在する。
そこにオーバーヘッドが生じるため、やり取りする回数は最少の方が良い。

100件のレコードを登録するために、100回INSERT文を発行するなどしてはならない。

1-3.トランザクションは最短でなければならない

仮にロックをした場合、他トランザクションはそのトランザクションが終わるまで”待ち”が発生しうる。
また、デッドロックが発生しかねない。
故にトランザクションが長いとパフォーマンスの低下に繋がるかねない。
他トランザクションと処理が被った場合、データ不整合の可能性も高まる。

1-4.SQLそのものが最速でなければならない

表題通り。
SQLそのものを速くすべきである。

どう書けば速くなるかは本記事では記載しないが、
いま書いたSQLが十分早いかどうかは、実行計画を確認する。

参照:2.SQLを書いたら実行計画をとる

1-5.原則を無視してもよい場合

これらの原則は必ずしも守らなければならない、というものではない。
**納得できる理由があるならば、**守らなくてもよい。
守らなければならないかどうかは、要件やドメイン、背景/文脈に依存する。
「納得できる理由」は下記など。

「1.DBから取得するデータ量は最少でなければならない」の場合

ORMの仕様で不要なカラムも取得している。
※ORMの内部で「SELECT * FROM tbl WHERE primary_key = ?」しているときなど。

しかし、1レコードしか取得しないため、不要なカラム数を取得しても影響は少ない。
また、可読性や変更容易性などを考えると、現状のORMをそのまま使用する方が良い。

「2.DBにSQLを発行する回数は最少でなければならない」の場合

100件の受注とそれに紐づく各10件の受注明細を登録する。
受注テーブルと受注明細テーブルのそれぞれにバルクインサートするので、2件のINSERT文で済む。

しかし、登録データにはDBの制約上登録できないものがあり、かつ事前チェックでは確認不可である。
顧客からは「登録できるものはそのまま登録してほしい」という要望が出た。
受注単位で整合性を取るため、受注1件とそれに紐づく受注明細を登録することになった。
つまり「トランザクション開始⇒受注登録⇒受注明細登録⇒コミット」の流れを100件分行った。

発行回数最少の原則に反しているが、要件なのでよい。

2.SQLを書いたら実行計画をとる

実行計画(またはアクセスプラン)とは、SQLを受け取ったDBが実際にどうSSD/HDD/メモリからデータを取得するかの計画である。
これを読むことにより、SQLが想定通りの最速の動きをするかを確認できる。

SQLが実行される流れ

製品によって異なるが、DBがSQLを受け取ってからデータを返す流れは下記の順序である。

  1. パーサ:SQL文を解析する。
    • SQL文が文法的に正しいか
    • 指定したテーブルやカラムが存在するか
    • ユーザにそのテーブルの閲覧権限があるか
    • etc…
  2. プランナ + 統計情報(カタログ):SQLと統計情報から実行計画を立てる
    • 統計情報とはDBに関する情報が格納されている
      • テーブル構造
      • インデックスの有無
      • インデックスのデータの偏り
      • etc…
    • 複数パターンの実行計画を建て、コストが良い実行計画をエグゼキュータに渡す
  3. エグゼキュータ:実行計画に基づいて、データを取得する。

実際のデータの取得方法はプランナの出す実行計画によるので、
いくら自分が最速のSQLを書いた気になっても、プランナの出した実行計画は最遅のものになっている可能性がある。
故にSQLを書いたら実行計画を読み、想定通りの取得方法になっているかを確認する。

取得方法

製品によるが、大体はクエリの先頭にEXPLAINを付与して実行する。

-- 実行計画取得
EXPLAIN SELECT * FROM tbl;
-- 実際にクエリを発行して計測
EXPLAIN ANALYZE SELECT * FROM tbl;

確認内容

表示される実行計画の内容は製品によるので、各自調べてほしい。

最低限、下記は確認する。

注意

統計情報は常に最新であるとは限らない。
下記は統計情報が最新では無いゆえに、遅い実行計画が選定される例。

  1. テーブルAにレコードが1件だけ登録された
  2. 統計情報が更新され、テーブルAはレコードが1件だけと記録された
  3. テーブルAを検索
    • レコードが1件だけなので、インデックスを見るまでもない。よってフルスキャンを行う。
  4. テーブルAにレコードが1億件登録された
  5. テーブルAを検索
    • 統計情報が更新されていないので、レコードが1件だけと認識
    • フルスキャンを行う実行計画が選定される
    • 1億件全部見るので、遅いクエリとなる

上記の様に、誤った実行計画が選定されうる。
開発環境では最適な実行計画でも、本番では最悪な実行計画になりうる。

実行計画をとる際は、想定される本番データと同じ量/偏りのデータを登録した上でとる。

想定通りの実行計画を出す方法

主に下記など。

その他個人的メモ