お気に入りのパフォーマンスチューニングトリック[終了]


126

パフォーマンスの調整が必要なクエリまたはストアドプロシージャがある場合、最初に試すことは何ですか。



これは建設的ではなく、Googleで検索できることに同意しますが、なぜ118 uvなのですか?:)
FLICKER 2016年

回答:


114

これは、最適化について尋ねる人にいつも私が与えるものの便利なリストです。
私たちは主にSybaseを使用していますが、ほとんどのアドバイスは全体に適用されます。

たとえば、SQL Serverには多数のパフォーマンスモニタリング/チューニングビットが付属していますが、そのようなものがない場合は(そうした場合でも)、次のことを検討します...

私が見た問題の99%は、結合に入れたテーブルが多すぎることが原因です。これに対する修正は、結合の半分を(いくつかのテーブルで)実行し、結果を一時テーブルにキャッシュすることです。次に、その一時テーブルで結合する残りのクエリを実行します。

クエリ最適化チェックリスト

  • 基になるテーブルでUPDATE STATISTICSを実行します。
    • 多くのシステムはこれをスケジュールされた毎週のジョブとして実行します
  • 基になるテーブルからレコードを削除します(削除されたレコードをアーカイブする可能性があります)
    • これを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(または他のノンストッププロセス)は実行されていますか?一時停止できますか?それを実行します。毎時?

2
どのビットを参照していますか?
AJ。

2
これはクールなものですが、いくつかの主張についていくつかの参考資料が必要です。たとえば、オプティマイズが結合で一度に4つのテーブルのみを考慮すると聞いたことはありません。これがどのように正しいのか理解できません。特にそのためのいくつかの参考資料を提供できますか?あなたがこれをどこで手に入れているのかを見たいです。
SheldonH 2016年

19
  1. 頭の中でクエリを実行するための最適なパスについてかなり良いアイデアを持っています。
  2. クエリプランを確認します-常に。
  3. STATSをオンにして、IOとCPUの両方のパフォーマンスを確認できるようにします。必ずしもクエリ時間ではなく、それらの数値を下げることに焦点を当てます(他のアクティビティ、キャッシュなどの影響を受ける可能性があるため)。
  4. オペレーターに入力される行が多数あり、出力される行が少ないことを確認します。通常、インデックスは、入ってくる行の数を制限することで役立ちます(ディスクの読み取りを節約します)。
  5. 最初に最大コストのサブツリーに焦点を当てます。そのサブツリーを変更すると、クエリプラン全体が変更されることがよくあります。
  6. 私が見た一般的な問題は次のとおりです。
    • 多数の結合がある場合、SQL Serverは結合を拡張してWHERE句を適用することを選択する場合があります。通常、これを修正するには、WHERE条件をJOIN句、または条件がインライン化された派生テーブルに移動します。ビューは同じ問題を引き起こす可能性があります。
    • 次善の結合(LOOP vs HASH vs MERGE)。私の経験則では、一番上の行が一番下の行に比べて行が非常に少ない場合はLOOP結合を使用し、セットがほぼ等しく順序付けされている場合はMERGEを使用し、その他の場合はすべてHASHを使用します。結合ヒントを追加すると、理論をテストできます。
    • パラメータスニッフィング。最初に非現実的な値で(たとえば、テストのために)ストアドプロシージャを実行した場合、キャッシュされたクエリプランは本番環境の値に対して最適ではない可能性があります。WITH RECOMPILEで再度実行すると、これを確認する必要があります。一部のストアドプロシージャ、特にさまざまなサイズの範囲を処理するストアドプロシージャ(たとえば、今日から昨日までのすべての日付-INDEX SEEKを伴う-または、昨年から今年までのすべての日付-INDEX SCANの方が適しています) )毎回WITH RECOMPILEで実行する必要がある場合があります。
    • 不正なインデント...さて、SQL Serverにはこの問題はありませんが、書式を修正するまでクエリを理解することは不可能だと思います。

1
悪いインデントを含めるための+1。書式設定が重要です!:)
mwigdahl

18

トピックからわずかに外れていますが、これらの問題を制御できる場合...
高レベルおよび高影響。

  • 高IO環境では、ディスクがRAID 10またはRAID 0 + 1のいずれか、またはRAID 1とRAID 0のネストされた実装用であることを確認してください。
  • 1500K未満のドライブは使用しないでください。
  • ディスクがデータベースにのみ使用されていることを確認してください。IEはOSをロギングしません。
  • 自動拡大または同様の機能をオフにします。予想されるすべてのストレージをデータベースが使用できるようにします。必ずしも現在使用されているものとは限りません。
  • タイプクエリのスキーマとインデックスを設計します。
  • ログタイプテーブル(挿入のみ)であり、DBに存在する必要がある場合は、インデックスを作成しないでください。
  • レポートの割り当てを行う場合(多数の結合を伴う複雑な選択)、スタースキーマまたはスノーフレークスキーマを使用したデータウェアハウスの作成を検討する必要があります。
  • パフォーマンスと引き換えにデータを複製することを恐れないでください!

8

CREATE INDEX

WHEREand JOIN句で使用できるインデックスがあることを確認します。これにより、データアクセスが大幅に高速化されます。

環境がデータマートまたはウェアハウスである場合、考えられるほとんどすべてのクエリに対してインデックスが豊富である必要があります。

ではトランザクション環境そのインデックスのメンテナンスがリソースを下にドラッグしないように、インデックスの数は下とその定義より戦略的でなければなりません。(インデックスのメンテナンスとは、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)

これは、色の選択よりも価格ポイントを満たす自動車の数が少なくなり、クエリエンジンが分析するデータがはるかに少なくなるためです。

私は、クエリを高速化するために、フィールドの順序のみが異なる2つの非常に類似したインデックス(firstname、lastname)と(lastname、firstname)をもう1つ持つことが知られています。


6

私が最近学んだトリックは、SQL Serverがupdateステートメントでフィールドだけでなくローカル変数も更新できることです。

UPDATE table
SET @variable = column = @variable + otherColumn

またはより読みやすいバージョン:

UPDATE table
SET
    @variable = @variable + otherColumn,
    column = @variable

これを使用して、再帰的な計算を実装するときに複雑なカーソル/結合を置き換え、パフォーマンスも大幅に向上しました。

パフォーマンスを大幅に改善した詳細とサンプルコードを以下に示します 。 aspx


5

ここでMySQLを想定し、EXPLAINを使用してクエリで何が行われているのかを調べ、インデックスが可能な限り効率的に使用されていることを確認し、ファイルの並べ替えを排除します。高性能MySQL:最適化、バックアップ、レプリケーションなどは、MySQLパフォーマンスブログと同様に、このトピックに関する優れた書籍です。


3
これはMySQLに適していますが、質問には「sqlserver」というタグが付けられました。それでも、それを行うのは良いことです。SSMSで行うのと同様のことは、「推定実行プランの表示」と「実際の実行プランを含める」を使用することです。巨大なテーブルスキャンを排除してクラスター化インデックスシークを使用できる場合は、最適なパフォーマンスに向かっています。
eksortso 2009年


3

SQL Serverでは、where句でORを使用すると、パフォーマンスが向上する場合があります。ORを使用する代わりに、2つの選択を行ってそれらを結合します。1000xの速度で同じ結果が得られます。


この原因不明の動作を確認しました。
Esen

2

where句を見てください-インデックスの使用を確認してください/愚かなことが何も行われていないことを確認してください

where SomeComplicatedFunctionOf(table.Column) = @param --silly

2

通常は、結合から始めます。1つずつクエリから除外し、クエリを再実行して、問題のある特定の結合があるかどうかを確認します。


2

すべての一時テーブルで、一意の制約(適切な場合)を追加して、インデックスと主キー(ほとんどの場合)を作成します。

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)

2

常にバインド変数を使用することを習慣にしています。RDBMSがSQLステートメントをキャッシュしない場合、バインド変数が役に立たない可能性があります。ただし、バインド変数を使用しない場合、RDBMSはクエリ実行プランと解析済みSQLステートメントを再利用する機会がありません。大幅な節約になる可能性があります:http : //www.akadia.com/services/ora_bind_variables.html。私は主にOracleを使用していますが、Microsoft SQL Serverはほとんど同じように動作します。

私の経験では、バインド変数を使用しているかどうかわからない場合は、おそらく使用していません。アプリケーション言語がそれらをサポートしていない場合は、サポートしている言語を見つけてください。クエリBのバインド変数を使用して、クエリAを修正できる場合があります。

その後、私はDBAと話し合って、RDBMSに最も苦痛を与えている原因を突き止めます。「なぜこのクエリは遅いのですか?」と尋ねるべきではないことに注意してください。それはあなたの医者にあなたに虫垂を取り出すように頼むようなものです。確かにクエリに問題がある可能性がありますが、何か他の問題が発生している可能性もあります。開発者として、私たちはコード行の観点から考える傾向があります。ラインが遅い場合は、そのラインを修正します。しかし、RDBMSは非常に複雑なシステムであり、遅いクエリははるかに大きな問題の症状である可能性があります。

あまりにも多くのSQLチューニングのヒントはカーゴカルトアイドルです。ほとんどの場合、問題は使用する構文とは無関係または最小限に関連しているので、通常、できるだけクリーンな構文を使用するのが最善です。次に、データベース(クエリではない)を調整する方法を検討します。それが失敗したときにのみ構文を微調整します。

パフォーマンスチューニングと同様に、常に意味のある統計を収集します。調整しているユーザーエクスペリエンスでない限り、ウォールクロック時間を使用しないでください。代わりに、CPU時間、フェッチされた行、ディスクから読み取られたブロックなどを確認します。多くの場合、人々は間違ったことを最適化します。


2

最初のステップ:クエリ実行プランを見てください! TableScan-
>
NestedLoopの不良-> Meh警告
TableScanがNestedLoopの背後にある-> DOOM!

SET STATISTICS IO ON
SET STATISTICS TIME ON


2

WITH(NoLock)を使用してクエリを実行することは、私の場所ではかなり標準的な操作です。数十ギガバイトのテーブルで実行中のクエリをキャッチせずにだれでも、取り出して撃たれます。


2
これは、常用ではなく、慎重に使用する必要があります。ロックは悪ではなく、誤解されています。

2

NOT INクエリを可能であればLEFT OUTER JOINSに変換します。たとえば、Table2の外部キーで使用されていないTable1のすべての行を検索する場合は、次のようにします。

SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
    SELECT Table1ID
    FROM Table2)

しかし、これによりはるかに優れたパフォーマンスが得られます。

SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null

1

@ DavidM

ここでMySQLを想定し、EXPLAINを使用してクエリで何が起こっているかを調べ、インデックスが可能な限り効率的に使用されていることを確認します...

SQL Serverでは、実行プランによって同じことが実現されます。これにより、ヒットしているインデックスなどがわかります。


1

フィルターに使用するclmでテーブルにインデックスを付けます


1

必ずしもSQLパフォーマンスのトリック自体ではありませんが、確実に関連しています。

プリコンパイル済みデータをデータベースから取得するのではなく、メモリから直接フェッチするだけの方がはるかに高速であるため、可能な場合はmemcachedを使用することをお勧めします。また、memcachedが組み込まれたMySQLのフレーバー(サードパーティ)もあります。


1

インデックスの長さをできるだけ短くしてください。これにより、DBはファイルシステムから一度により多くのキーを読み取ることができるため、結合が高速化されます。これはすべてのDBで機能すると思いますが、MySQLの特定の推奨事項であることは知っています。


1

私は注意します:

  • CURSORループを展開し、セットベースのUPDATE / INSERTステートメントに変換します。
  • 次のようなアプリケーションコードを探します。
    • 大量のレコードセットを返すSPを呼び出し、
    • 次に、アプリケーションで、各レコードを調べ、レコードを更新するパラメーターを指定してSPを呼び出します。
    • これを、1つのトランザクションですべての作業を行うSPに変換します。
  • 多くの文字列操作を行うすべてのSP。データが正しく構造化/正規化されていないことの証拠です。
  • ホイールを再発明するすべてのSP。
  • 1分以内に何をしようとしているのか理解できないSPです!

1
SET NOCOUNT ON

通常、実際にを使用する必要がない限り、ストアドプロシージャ内の最初の行@@ROWCOUNT


2
@@ ROWCOUNTはとにかく設定されます。NOCOUNTは、「影響を受けるxx行」ステートメントを無効にします。
Sklivvz

これにより、パフォーマンスにかなりの違いが出ますか?
JohnFx 2009年

ええ、SQLステートメントが実行されるたびにカウントが自動的に計算されるわけではありません。クエリをベンチマークするのは簡単で、違いがあるかどうかはわかりません。
トラビス

いずれにしても、SQL Serverでカウントが追跡されます。表示されるパフォーマンスの違いは、カウントがネットワークを介してフロントエンドに移動する必要があるためです。単一のSELECTを実行している場合、それほど大きな違いはありません。100000挿入のループがある場合、ネットワーク上で非常に多くなります。
トムH

1

SQL Serverでは、nolockディレクティブを使用します。これにより、待機せずにselectコマンドを完了することができます。通常、他のトランザクションは完了します。

SELECT * FROM Orders (nolock) where UserName = 'momma'

3
NOLOCKは、正しい結果を気にしないクエリ専用です
Mark Sowul '19年

1

必要のない場所でカーソルを削除します。


ええ、カーソルは呪いです!;)
Sklivvz

8
ああ。そのような無資格を捨てないでください。カーソルは銃のようなものです。彼らはそれ自体で悪いわけではありません。人々が本当に悪いことをするだけです。
JohnFx 2009年

1

多くの行が関数を呼び出すSprocsの関数呼び出しを削除します。

私の同僚は、関数呼び出し(例としてuseridからlastlogindateを取得)を使用して、非常に広いレコードセットを返しました。

最適化を任されて、sprocの関数呼び出しを関数のコードに置き換えました。多くのsprocの実行時間を20秒以上から1秒未満に減らしました。


0
  • すべてのテーブルの前にdboを付けます。再コンパイルを防ぐため。
  • クエリプランを表示し、テーブル/インデックススキャンを探します。
  • 2005年には、不足しているインデックスの管理ビューを調べます。


0

システムプロシージャはすべて "sp_"で始まるため、ストアドプロシージャ名の前に "sp_"を付けないでください。SQLServerは、呼び出されたときにプロシージャを見つけるために検索をより困難にする必要があります。


1
これを実際にベンチマークしましたか?SQL Serverが適切な処理を行っている場合(ハッシュアルゴリズムを使用してStored Procを検索する場合)、違いはありません。実際、SQL Server がそれ行わなかった場合、システムパフォーマンスは悪臭を放つように思われます(おそらく、独自のprocを呼び出すため)。
ジョンスタウファー、

1
これは時期尚早の最適化のバケツに入ると思います。人々の混乱を避けることはおそらく良い習慣ですが、最適化のヒントとして... D-
JohnFx

0

ダーティリード -

set transaction isolation level read uncommitted

トランザクションの整合性が絶対に必要ではないデッドロックを防止します(通常はそうです)


1
はい、しかし、これは見つけるのが非常に難しい奇妙なバグにつながる可能性があります。
Grant Johnson、

0

私は常にSQLプロファイラー(ネストレベルの多いストアドプロシージャの場合)またはクエリ実行プランナー(ネストのないSQLステートメントの場合)に最初に行きます。この2つのツールのいずれかを使用すると、90%の確率で問題をすぐに見つけることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.