変更要約: 初版
5.3チューニングとキャパシティ設計
性能目的で意図的に冗長化する非正規化、大規模テーブルを分割するパーティショニング、SQL文自体を見直すSQLチューニング、そして性能劣化の真因を切り分けるボトルネック診断とスロークエリの原因特定を学びます。
データベース設計者・DBAにとって、性能問題への対処は「インデックスを張る」だけでは終わりません。テーブル構造そのもの(非正規化・パーティショニング)を見直す判断、SQL文の書き方を変える判断、そして「本当のボトルネックがどこにあるか」を診断する判断まで含めて初めて、持続的なチューニングができます。この節ではそうした設計・診断の判断力を養います。
5.3.1非正規化とパーティショニング
- 非正規化=正規化によって分解されたテーブルを、参照性能を優先する目的で意図的に結合・冗長化する設計判断。例えば注文明細に商品名を都度JOINで取得する代わりに、注文明細テーブル自体に商品名を冗長に持たせれば結合コストを削減できる。ただし、商品名を後から変更した際に複数箇所を同時に更新しないと不整合が生じるという更新時異常のリスクを引き受けることになる。
- パーティショニング=1つの論理テーブルを、特定のキー(日付・地域等)に基づいて物理的に複数の領域(パーティション)へ分割して格納する技術。レンジパーティショニング(日付範囲等の区間で分割)・ハッシュパーティショニング(ハッシュ値で均等に分割)等の方式がある。クエリの条件が特定パーティションに絞り込める場合、該当パーティションだけを読めばよく(パーティションプルーニング)、テーブル全体を走査するより高速になる。加えて、古いパーティション単位でのアーカイブ削除も容易になる。
5.3.2SQLチューニングとボトルネック診断
- SQLチューニング=実行計画・インデックス構成を変えずとも、SQL文の書き方自体を見直すことで性能を改善する手法。例えば
SELECT *で不要な列まで取得している場合に必要な列だけへ絞る、相関副問合せをJOINへ書き換える、OR条件で複数のインデックスが使えない場合にUNIONへ分解する、といった書き換えが該当する。 - ボトルネック診断=性能劣化の原因が、CPU(複雑な演算・大量の結合処理)・メモリ(ソート/ハッシュ結合のバッファ不足によるディスクスワップ)・ディスクI/O(インデックス未整備による大量の物理読み込み)・ロック待ち(同時実行制御による排他)のどこにあるかを、実測データに基づいて切り分けるプロセス。原因を誤って特定すると、的外れな対策(例:ロック待ちが原因なのにインデックスを増やす)に時間を浪費する。
「非正規化=参照性能を優先し冗長化を許容、更新時異常のリスクを引き受ける」「パーティショニング=パーティションプルーニングで該当領域のみ読む」が最頻出です。ボトルネック診断はCPU・メモリ・ディスクI/O・ロック待ちのどれが原因かを実測で切り分ける姿勢を必ず問われます。
あるSaaS事業者のDBAが、月次レポート生成バッチの実行時間が徐々に伸び続け、ついにサービス影響が出るまでになったという相談を受けたとします。まず実行中のクエリの待機イベントを実測すると、CPU使用率もディスクI/O使用率も低いまま、ロック待ち時間だけが突出して長いことが分かりました。これは「インデックスが足りない」「メモリが不足している」といった典型的な性能劣化とは異なり、同時実行制御(排他ロック)が真因であると切り分けられます。原因をさらに追うと、レポート生成バッチが対象月の全注文行に対して長時間の読み取りロックを保持したまま集計しており、同時刻に走る通常の注文処理(INSERT/UPDATE)と競合していました。ここで安易に「インデックスを増やそう」という対策に飛びつくと、ロック待ちというボトルネックには効果がなく貴重な時間を浪費します。DBAは実測に基づき、テーブル構造の見直しとして、注文テーブルを月単位のレンジパーティショニングへ変更する設計を採用しました。これにより、レポート生成バッチは対象月のパーティションのみをロックすればよくなり(パーティションプルーニングでロック範囲自体も縮小)、他の月のパーティションに対する通常の注文処理とは競合しなくなりました。加えて、レポート集計SQL自体も相関副問合せをJOINへ書き換えるSQLチューニングを行い、全体の処理時間を短縮しました。この事例のように、まず実測でボトルネックの所在(CPU/メモリ/ディスクI/O/ロック待ち)を切り分け、その真因に応じて非正規化・パーティショニング・SQLチューニングのいずれか(または組み合わせ)を選ぶのが、持続可能な性能設計の判断です。
| 手法 | 狙い | 引き受けるリスク/コスト |
|---|---|---|
| 非正規化 | 結合コストを減らし参照を高速化 | 更新時異常(複数箇所の同時更新が必要) |
| パーティショニング | パーティションプルーニングで読み取り範囲・ロック範囲を縮小 | パーティションキー設計を誤ると効果が出ない |
| SQLチューニング | 書き方の見直しで無駄な処理を削減 | 根本原因(構造・ロック)が別にあると効果が限定的 |
ひっかけ: 「性能劣化の対策はまずインデックスを増やすことである」は誤りです——ロック待ちやメモリ不足が真因の場合、インデックス追加は効果がなく時間を浪費します。また「非正規化は正規化に反するため絶対に避けるべきだ」も誤り=非正規化は参照性能を優先する目的で更新時異常のリスクを意図的に引き受ける正当な設計判断であり、要件次第では採用すべきです。
5.3.3この節のまとめ
- 非正規化は参照性能優先で冗長化を許容し更新時異常のリスクを引き受ける設計判断。パーティショニングは分割によりパーティションプルーニングで読み取り/ロック範囲を縮小
- SQLチューニングは実行計画/インデックスを変えずSQL文の書き方自体を見直す手法
- ボトルネック診断はCPU・メモリ・ディスクI/O・ロック待ちのどれが真因かを実測で切り分けてから対策を選ぶ
進捗の記録にはログインが必要です。
理解度チェック
(軽い確認用)Q1. 月次レポート生成バッチの実行時間が徐々に伸びている。実測するとCPU使用率・ディスクI/O使用率はいずれも低いままで、ロック待ち時間だけが突出して長いことが判明した。この状況で採るべき対策として最も妥当なものはどれか。
Q2. 注文明細テーブルで商品名を都度JOINで取得する代わりに、注文明細テーブル自体に商品名を冗長に持たせる設計変更を検討している。この非正規化を採用する際に必ず引き受けなければならないリスクはどれか。
Q3. 実行計画やインデックス構成を変更する余地がない状況で、`SELECT *`により不要な列まで取得しているクエリと、複数のインデックスが使えなくなる`OR`条件を含むクエリがある。性能改善のために最初に検討すべきアプローチとして最も適切なものはどれか。

