パフォーマンスの調整が必要なクエリまたはストアド プロシージャがある場合、最初に何を試しますか?
29 に答える
これは、最適化について私に尋ねてくる人に私がいつも渡す便利でダンディーなリストです。
私たちは主に Sybase を使用していますが、ほとんどのアドバイスは全体に当てはまります。
たとえば、SQL Serverには多くのパフォーマンス監視/チューニングビットが付属していますが、そのようなものがない場合(そしておそらく持っている場合でも)、次のことを検討します...
私が見た問題の 99% は、結合にあまりにも多くのテーブルを配置することが原因です。これを修正するには、結合の半分を (いくつかのテーブルで) 実行し、結果を一時テーブルにキャッシュします。次に、その一時テーブルに結合する残りのクエリを実行します。
クエリ最適化チェックリスト
- 基になるテーブルで UPDATE STATISTICS を実行します
- 多くのシステムは、スケジュールされた毎週のジョブとしてこれを実行します
- 基になるテーブルからレコードを削除します (場合によっては、削除されたレコードをアーカイブします)
- これを 1 日 1 回または 1 週間に 1 回自動的に実行することを検討してください。
- インデックスの再構築
- テーブルの再構築 (bcp データのアウト/イン)
- データベースのダンプ/リロード (抜本的ですが、破損を修正する可能性があります)
- より適切な新しいインデックスを構築する
- DBCC を実行して、データベースに破損の可能性があるかどうかを確認します
- ロック/デッドロック
- データベースで他のプロセスが実行されていないことを確認する
- 特にDBCC
- 行レベルまたはページ レベルのロックを使用していますか?
- クエリを開始する前にテーブルを排他的にロックする
- すべてのプロセスが同じ順序でテーブルにアクセスしていることを確認します
- データベースで他のプロセスが実行されていないことを確認する
- 指標は適切に使用されていますか?
- 結合は、両方の式がまったく同じデータ型である場合にのみインデックスを使用します
- インデックスは、インデックスの最初のフィールドがクエリで一致した場合にのみ使用されます
- クラスター化されたインデックスは適切な場所で使用されていますか?
- 範囲データ
- value1 と value2 の間の WHERE フィールド
- 小さな結合は適切な結合です
- デフォルトでは、オプティマイザは一度に 4 つのテーブルのみを考慮します。
- これは、4 つを超えるテーブルとの結合では、最適でないクエリ プランを選択する可能性が高いことを意味します。
- 結合を分割する
- 結合を解除できますか?
- 外部キーを一時テーブルに事前選択する
- 半分の結合を行い、結果を一時テーブルに入れる
- 適切な種類の一時テーブルを使用していますか?
#temp
@table
テーブルは、大量 (数千行) の変数よりもはるかに優れたパフォーマンスを発揮する場合があります。
- サマリー テーブルの維持
- 基になるテーブルでトリガーを使用して構築する
- 毎日/毎時/などを構築します。
- アドホックなビルド
- 段階的にビルドするか、分解/再構築します
- SET SHOWPLAN ON を使用してクエリ プランを確認する
- SET STATS IO ON で実際に何が起こっているかを確認する
- プラグマを使用してインデックスを強制します: (index: myindex)
- SET FORCEPLAN ON を使用してテーブルの順序を強制する
- パラメータ スニッフィング:
- ストアド プロシージャを 2 つに分割する
- proc1 から proc2 を呼び出す
- @parameter が proc1 によって変更された場合、オプティマイザーが proc2 でインデックスを選択できるようにします
- ハードウェアを改善できますか?
- 何時に走っていますか?もっと静かな時間はありますか?
- Replication Server (またはその他のノンストップ プロセス) は実行されていますか? 中断できますか?それを実行します。毎時?
- 頭の中でクエリを実行するための最適なパスをよく考えてください。
- クエリプランを確認してください - 常に。
- STATS をオンにすると、IO と CPU の両方のパフォーマンスを調べることができます。必ずしもクエリ時間ではなく、これらの数値を下げることに重点を置いてください (他のアクティビティやキャッシュなどの影響を受ける可能性があるため)。
- 演算子に多数の行が入ってくるが、出てくる数は少ないことを確認してください。通常、インデックスは、入ってくる行の数を制限することで役立ちます (これにより、ディスクの読み取りが節約されます)。
- 最初に最大のコスト サブツリーに注目します。そのサブツリーを変更すると、多くの場合、クエリ プラン全体が変更される可能性があります。
- 私が見た一般的な問題は次のとおりです。
- 結合が多数ある場合、Sql Server は結合を拡張して WHERE 句を適用することを選択することがあります。通常、これは WHERE 条件を JOIN 句に移動するか、条件がインライン化された派生テーブルに移動することで修正できます。ビューも同じ問題を引き起こす可能性があります。
- 次善の結合 (LOOP、HASH、MERGE)。私の経験則では、一番上の行が一番下の行に比べて行数が非常に少ない場合は LOOP 結合を使用し、セットがほぼ同じで順序付けされている場合は MERGE を使用し、それ以外の場合は HASH を使用します。結合ヒントを追加すると、理論をテストできます。
- パラメータのスニッフィング。最初に非現実的な値を使用してストアド プロシージャを実行した場合 (テスト用など)、キャッシュされたクエリ プランは運用環境の値に対して最適ではない可能性があります。WITH RECOMPILE を再度実行すると、これが確認されます。一部のストアド プロシージャ、特にさまざまなサイズの範囲を扱うもの (たとえば、今日と昨日の間のすべての日付 - これには INDEX SEEK が必要です - または、昨年と今年の間のすべての日付 - これは INDEX SCAN を使用したほうがよいでしょう) ) 毎回 WITH RECOMPILE を実行する必要がある場合があります。
- 不正なインデント...さて、Sql Server にはこれに関する問題はありませんが、書式を修正するまでクエリを理解することは不可能です。
少し話題から外れていますが、これらの問題を管理している場合は...
高レベルで影響力があります。
- 高IO環境の場合、ディスクがRAID10またはRAID0 + 1、あるいはRAID1とRAID0のネストされた実装用であることを確認してください。
- 1500K未満のドライブは使用しないでください。
- ディスクがデータベースにのみ使用されていることを確認してください。IEログなしOSなし。
- 自動拡大または同様の機能をオフにします。予想されるすべてのストレージをデータベースで使用できるようにします。必ずしも現在使用されているものとは限りません。
- タイプクエリのスキーマとインデックスを設計します。
- ログタイプテーブル(挿入のみ)であり、DBに存在する必要がある場合は、インデックスを作成しないでください。
- レポートの割り当てを行う場合(複雑な選択で多くの結合がある場合)、スタースキーマまたはスノーフレークスキーマを使用してデータウェアハウスを作成することを検討する必要があります。
- パフォーマンスと引き換えにデータを複製することを恐れないでください!
CREATE INDEX
WHERE
andJOIN
句に使用できるインデックスがあることを確認してください。これにより、データアクセスが大幅に高速化されます。
ご使用の環境がデータマートまたはウェアハウスである場合、考えられるほとんどすべてのクエリに対してインデックスが豊富にあるはずです。
トランザクション環境では、インデックスのメンテナンスによってリソースが引き下げられないように、インデックスの数を減らし、それらの定義をより戦略的にする必要があります。INSERT, UPDATE,
(インデックスのメンテナンスとは、操作と同様に、基になるテーブルの変更を反映するためにインデックスのリーフを変更する必要がある場合DELETE
です。)
また、インデックス内のフィールドの順序に注意してください。フィールドの選択性が高い(カーディナリティが高い)ほど、インデックスの早い段階で表示されます。たとえば、中古車を検索しているとします。
SELECT i.make, i.model, i.price
FROM dbo.inventory i
WHERE i.color = 'red'
AND i.price BETWEEN 15000 AND 18000
価格は一般的にカーディナリティが高くなります。利用できる色は数十色しかないかもしれませんが、おそらく数千の異なる提示価格があります。
これらのインデックスの選択肢のうちidx01
、クエリを満たすためのより高速なパスを提供します。
CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)
これは、色の選択よりも価格を満たせる車が少なく、クエリエンジンに分析するデータがはるかに少ないためです。
私は、一方のクエリ(firstname、lastname)ともう一方の(lastname、firstname)のクエリを高速化するために、フィールドの順序のみが異なる2つの非常によく似たインデックスを持っていることを知っています。
ここで MySQL を想定すると、EXPLAIN を使用してクエリで何が起こっているかを調べ、インデックスが可能な限り効率的に使用されていることを確認し、ファイルの並べ替えを排除しようとします。High Performance MySQL: Optimization, Backups, Replication, and Moreは、MySQL Performance Blogと同様に、このトピックに関する優れた書籍です。
@Terrapinには、言及する価値のあるisnullとcoalesceの間にいくつかの違いがあります(ANSI準拠以外に、これは私にとって大きなものです)。
SQL Server では、WHERE 句で OR を使用すると、パフォーマンスが大幅に向上することがあります。OR を使用する代わりに、2 つの選択を実行して結合します。1000 倍の速度でも同じ結果が得られます。
where句を見てください-インデックスの使用を確認し、愚かなことは何も行われていないことを確認してください
where SomeComplicatedFunctionOf(table.Column) = @param --silly
私は通常、結合から始めます。一度に 1 つずつクエリからそれぞれをノックアウトし、クエリを再実行して、問題がある特定の結合があるかどうかを判断します。
すべての一時テーブルで、一意の制約 (適切な場合) を追加して、インデックスと主キー (ほとんどの場合) を作成するのが好きです。
declare @temp table(
RowID int not null identity(1,1) primary key,
SomeUniqueColumn varchar(25) not null,
SomeNotUniqueColumn varchar(50) null,
unique(SomeUniqueColumn)
)
ここで MySQL を仮定すると、EXPLAIN を使用してクエリで何が起こっているかを調べ、インデックスが可能な限り効率的に使用されていることを確認します...
SQL Server では、実行計画によって同じことが得られます。つまり、どのインデックスがヒットしているかなどがわかります。
それ自体は必ずしも SQL パフォーマンスの秘訣ではありませんが、間違いなく関連しています。
可能な場合は memcached を使用することをお勧めします。プリコンパイルされたデータをデータベースから取得するのではなく、メモリから直接取得する方がはるかに高速です。memcached が組み込まれた MySQL のフレーバーもあります (サードパーティ製)。
インデックスの長さはできるだけ短くしてください。これにより、DB はファイル システムから一度により多くのキーを読み取ることができるため、結合が高速化されます。これはすべての DB で機能すると思いますが、MySQL に固有の推奨事項であることはわかっています。
私は常にバインド変数を使用することを習慣にしています。RDBMS が SQL ステートメントをキャッシュしない場合、バインド変数が役に立たない可能性があります。しかし、バインド変数を使用しない場合、RDBMS はクエリ実行プランと解析された SQL ステートメントを再利用する機会がありません。大幅に節約できます: http://www.akadia.com/services/ora_bind_variables.html。私は主に Oracle を使用していますが、Microsoft SQL Server もほとんど同じように機能します。
私の経験では、バインド変数を使用しているかどうかがわからない場合は、おそらく使用していません。アプリケーション言語がそれらをサポートしていない場合は、サポートしている言語を見つけてください。場合によっては、クエリ B のバインド変数を使用してクエリ A を修正できます。
その後、DBA に相談して、RDBMS に最も問題を引き起こしている原因を突き止めます。「このクエリが遅いのはなぜですか?」と尋ねるべきではないことに注意してください。それは、医師に虫垂を取り除くように頼むようなものです。確かにクエリに問題がある可能性がありますが、他の何かがうまくいかない可能性もあります。開発者として、私たちはコード行の観点から考える傾向があります。回線が遅い場合は、その回線を修正します。しかし、RDBMS は非常に複雑なシステムであり、遅いクエリは、はるかに大きな問題の兆候である可能性があります。
あまりにも多くの SQL チューニングのヒントがカーゴ カルト アイドルです。ほとんどの場合、問題は使用する構文とは無関係であるか、ほとんど関係がないため、通常は、できるだけクリーンな構文を使用することをお勧めします。次に、(クエリではなく) データベースを調整する方法を検討することができます。それが失敗した場合にのみ、構文を調整してください。
他のパフォーマンス チューニングと同様に、常に意味のある統計を収集してください。調整しているユーザー エクスペリエンスでない限り、ウォールクロック タイムを使用しないでください。代わりに、CPU 時間、フェッチされた行、ディスクから読み取られたブロックなどを調べます。多くの場合、人々は間違ったことを最適化します。
WITH (NoLock) を使用してクエリを実行することは、私の場所ではほぼ標準的な操作です。数十ギガバイトのテーブルでクエリを実行しているのを見つけた人は、連れ出されて撃たれます。
最初のステップ: クエリ実行プランを見てください!
TableScan - > 悪い
NestedLoop -> まあ警告
NestedLoop の背後にある TableScan -> DOOM!
統計情報を設定 IO オン
統計情報を設定 時間をオン
SET NOCOUNT ON
実際にを使用する必要がない限り、通常、ストアドプロシージャ内の最初の行@@ROWCOUNT
。
私は探しています:
- CURSORループを展開し、セットベースのUPDATE/INSERTステートメントに変換します。
- 次のようなアプリケーションコードを探してください。
- 大量のレコードセットを返すSPを呼び出します。
- 次に、アプリケーションで、各レコードを調べ、パラメーターを使用してSPを呼び出してレコードを更新します。
- これを、1つのトランザクションですべての作業を行うSPに変換します。
- 多くの文字列操作を行うSP。データが正しく構造化/正規化されていないことの証拠です。
- 車輪の再発明を行うSP。
- 1分以内に何をしようとしているのか理解できないSPはありますか?
SQL Serverでは、nolockディレクティブを使用します。これにより、selectコマンドを待たずに完了することができます。通常、他のトランザクションが終了します。
SELECT * FROM Orders (nolock) where UserName = 'momma'
フィルター処理した clm でテーブルにインデックスを付けます
- すべてのテーブルにプレフィックス dbo を付けます。再コンパイルを防ぐため。
- クエリ プランを表示し、テーブル/インデックス スキャンを探します。
- 2005 年に、欠落しているインデックスがないか管理ビューを精査します。
不要なカーソルを削除します。
使うのが好き
isnull(SomeColThatMayBeNull, '')
以上
coalesce(SomeColThatMayBeNull, '')
合体が提供する複数の引数のサポートが必要ない場合。
http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx
多くの行が関数を呼び出す Sprocs の関数呼び出しを削除します。
私の同僚は、関数呼び出し (例としてユーザー ID から lastlogindate を取得する) を使用して、非常に幅広いレコードセットを返しました。
最適化を担当し、sproc 内の関数呼び出しを関数のコードに置き換えました。多くの sproc の実行時間を 20 秒以上から 1 秒未満に短縮しました。
私は常に最初にSQLプロファイラー(ネストレベルが多いストアドプロシージャの場合)またはクエリ実行プランナー(ネストのない少数のSQLステートメントの場合)に移動します。90%の確率で、これら2つのツールのいずれかを使用して問題をすぐに見つけることができます。
システム プロシージャはすべて "sp_" で始まるため、ストアド プロシージャ名の前に "sp_" を付けないでください。SQL Server は、プロシージャが呼び出されたときにプロシージャを見つけるために、よりハードな検索を行う必要があります。
set transaction isolation level read uncommitted
トランザクションの整合性が絶対に必要でない場合のデッド ロックを防止します (通常はそうです)。