SQL実践入門第5章を読んだ

ループ依存症

問題を細かく分割し、レコードという単位にまで落とし込んだ後に、1レコードに対する処理を繰り返すことで問題を解こうとする心理的態度のこと

ループ依存症のコード

  • カード明細を表示するために1行ずつ明細にアクセスするSELECT文をループさせる
  • バッチ処理で大量データを処理するため、1行ずつレコードをフェッチしてホスト言語側で処理を行い、また1行ずつテーブルを更新する(めちゃくちゃやったことあるわ...非同期の実装だったから問題なかったか?)

SQLは意図的にループを追放している

  • SQLにはループがない
  • 意識的に言語設計から排除している
  • SQLは最初に作られた時からループを無くそうという発想で作られている

ループ依存症のSQLのデメリット

  • パフォーマンスが悪い

    • 特にSQLの実行プロセスに置いてSQL文のパース(構文解析)と実行計画生成、評価は比較的遅い処理である。中でもパースは原則としてデータベースがSQLを受け取るたびに実行せざるを得ないため、細かいSQLを積み重ねるループ依存症SQLにおいてはオーバーヘッドに占める割合が高くなりがち
  • 並列分散がやりにくい

    • ループ1回あたりの処理を極めて単純化している関係上、リソースを分散した上での並列処理による最適化が受けられない

      • CPUのマルチコアによる並列処理を利用できない
    • ストレージの分散効率が悪い

      • ループ依存症SQLは単純なものが多く、必然的に1回のSQL文がアクセスするデータ量も少なくなり、I/Oを並列化しにくい
  • データベースの進化による恩恵を受けられない

    • DBMSベンダーの努力は基本的に「大規模データを扱う複雑なSQL文」を速くすることが中心となっている
    • 遅いループ依存SQLを(深い考えなしに)スケールアップしたところで、物理リソースがボトルネックになっているわけではないため全く速くならないこともしばしば
      • チューニングポテンシャルがほとんどない、ループ依存症SQLの本当に怖いのはこの点

ループ依存症のSQLのメリット

SQL文が単純極まりないため実行計画もまた単純なものになる。そのために3つのメリットが生まれる

  • 実行計画が安定する

    • オプティマイザには使うインデックスを変えるくらいの自由しか許されていないので、本番運用中に実行計画が変わってスローダウンするというトラブルからほぼ自由になれる
  • 処理時間の見積もり精度が(相対的には)高い

    • 1回あたり実行時間×実行回数で(参考程度にしかならないが)処理時間を測れる
      • そもそも高速なSQLの処理時間はちょっとした条件に違いですぐに数倍〜数百倍の違いが出てしまうので、それを積み上げるような見積もりの精度が高いとは言えないが
    • 複数行を一度に処理するSQLはどんな実行計画が選ばれるかによってパフォーマンスが違うので、騎乗で処理時間を見積もるのが難しい。それに比べてループ依存症SQLの見積もりの方がまだマシ、という程度
  • トランザクション制御が容易

    • 特定のループ回数ごとにコミット処理を行うなど、トランザクションの粒度を細かく設定することができる

ループ依存症のSQLを速くする方法はあるか

  • 複数行を1度に処理するSQLに変える

    • 即アプリケーション改修を意味する...
  • 個々のSQLを速くする

    • ただし、ループ依存症SQLはもうすでに十分単純なため、改善箇所がない場合も
  • 処理を多重化する

    • 最も望みのある選択肢
    • CPUやディスクといったリソースに余裕が当て、処理をうまく分割できるキーがあれば、ループそのものを多重化することによってパフォーマンスを線形に近い形でスケールさせられる可能性あり
    • 物理リソースがすでにいっぱいいっぱいだとこの方法は採用できない
  • ループ依存症SQLはチューニングポテンシャルに乏しい

  • トレードオフを考えて、ループ依存症のSQLと複数行を一度に処理するSQLのどちらを採用するか判断する必要がある

SQLでループをどう表現するか

CASE式とウィンドウ関数を使おう