sql >> データベース >  >> RDS >> Database

インデックスチューニングへのアプローチ–パート2

    前回の投稿では、クエリを調整するとき、特に新しいインデックスを追加したり、既存のインデックスを変更したりする必要があることに気付いたときに、実行するプロセスの概要を説明し始めました。この時点までに、問題のあるクエリ、必要なインデックス、テーブルに現在存在するインデックス、およびそれらのインデックスが使用されているかどうかを特定しました。そのデータを取得したら、プロセスの次のステップに進むことができます。

    ステップ5:インデックスを使用するもの

    インデックスが使用される(または使用されない)頻度を確認するだけでなく、どのクエリを使用するかを知ることも有益です。 特にインデックスを別のインデックスとマージする場合は、インデックスを使用します。幸い、Jonathan Kehayiasは、特定のインデックスを使用しているプラ​​ンを特定するのに役立つクエリをすでに作成しています。彼のバージョンはプランキャッシュに使用できます。情報が一時的なものであるという唯一の課題があるため、特定のインデックスを使用するすべてのクエリをキャプチャできるとは限りません。クエリストアはそれを支援します–クエリストアの計画から同じ情報を取得するように彼のクエリを変更しました:

      SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
      DECLARE @IndexName AS NVARCHAR(128) = N'[IX_Sales_OrderLines_AllocatedStockItems]',
              @lb AS nchar(1) = N'[', @rb AS nchar(1) = N']';
     
      -- Make sure the name passed is appropriately quoted
      IF (LEFT(@IndexName, 1) <> @lb AND RIGHT(@IndexName, 1) <> @rb) SET @IndexName = QUOTENAME(@IndexName);
     
      --Handle the case where the left or right was quoted manually but not the opposite side
      IF LEFT(@IndexName, 1)  <> @lb SET @IndexName = @rb + @IndexName;
      IF RIGHT(@IndexName, 1) <> @rb SET @IndexName = @IndexName + @rb;
     
      ;WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')   
      SELECT
        stmt.value('(@StatementText)[1]', 'varchar(max)') AS SQL_Text,
        obj.value('(@Database)[1]', 'varchar(128)') AS DatabaseName,
        obj.value('(@Schema)[1]', 'varchar(128)') AS SchemaName,
        obj.value('(@Table)[1]', 'varchar(128)') AS TableName,
        obj.value('(@Index)[1]', 'varchar(128)') AS IndexName,
        obj.value('(@IndexKind)[1]', 'varchar(128)') AS IndexKind,
        query_plan
      FROM 	
      (
        SELECT query_plan
        FROM
        (
          SELECT TRY_CONVERT(XML, [qsp].[query_plan]) AS [query_plan]
          FROM sys.query_store_plan [qsp]
        ) tp
      ) AS tab (query_plan)
      CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
      CROSS APPLY stmt.nodes('.//IndexScan/Object[@Index=sql:variable("@IndexName")]') AS idx(obj)
      OPTION(MAXDOP 1, RECOMPILE);

    これは、レビューしているインデックスの数とそれらを使用するクエリの数によっては、うさぎの穴の奥深くにいることができるもう1つのポイントであることに注意してください。可能であれば、を理解するだけでなく、(クエリストアまたはプランキャッシュからの)実行回数も考慮します。 クエリはインデックスを使用しますが、そのクエリが実行される頻度。ここで、インデックスの調整が芸術になります。とんでもない量のデータを収集できますが、分析する時間が無限にないため、レビューするクエリの数について判断を下す必要があります。

    ステップ6:テスト

    最も簡単な方法では、インデックスのテストとは、問題のあるクエリを取得し、計画とパフォーマンスのデータ(期間、IO、CPUなど)をキャプチャしてから、インデックスを作成し、クエリを再実行して同じ情報をキャプチャすることを意味します。パフォーマンスが向上した場合は、問題ありません。

    それほど単純なことはめったにありません。

    まず、テストしたいインデックスのバリエーションが少なくとも2つ、場合によってはそれ以上あることがよくあります。ベースラインから始めて、すべてのインデックスバリエーションを作成し、プランキャッシュをクリアして、SQLServerが何を選択するかを確認します。次に、ロールスルーしてヒントを使用して各インデックスを強制し、実行ごとの計画とパフォーマンスのメトリックをキャプチャします。注:これは、すべてのインデックスに十分なディスク容量があることを前提としています。そうでない場合は、一度に1つずつ作成して、テストします。最後に、数値を比較します。新しいインデックスを追加するだけであれば、ほぼ完了です。ただし、インデックスを変更したり、カップルをマージしたりすると、複雑になる可能性があります。

    理想的な世界では、既存のインデックスを変更すると、現在のインデックスを使用して計画とパフォーマンスメトリックを取得する最も頻繁で重要なクエリが見つかります(これはクエリストアで簡単です)。次に、インデックスを変更し、それらすべてのクエリを再度実行して、プランの形状やパフォーマンスに大幅な変更がないかどうかを確認します。

    2つのインデックスをマージする場合、同じことを行いますが、いずれかのインデックスを使用するすべてのクエリを使用して、マージされたインデックスで再テストします。

    テーブルに複数のインデックスを追加/変更/マージする場合は、関連するすべてのクエリとそのプランとメトリックを取得し、インデックスを変更してから、すべての情報を再度取得して比較する必要があります。そこにあるさまざまなクエリの数によっては、これには非常に時間がかかる場合があります。これはアートフォームであり、実際にテストする必要のあるクエリの数を決定する必要があります。これは、実行の頻度、クエリの重要性/関連性、および利用可能/割り当てられた時間の関数です。

    最後に、テーブルにインデックスを追加し、既存のインデックスを削除しない場合、INSERT、DELETE、および場合によってはUPDATEのオーバーヘッドが追加されます。この変更のパフォーマンステストは可能ですが、テスト環境と、負荷テストを実行し、期間、IO、およびCPUに関連する変更前と変更後のメトリックをキャプチャする機能が必要です。

    たくさんの友達がいるので、インデックスの調整が簡単だと最初に考えたのは皮肉なことです。必ずしも単純ではないかもしれませんが、それは可能です。それは勤勉さとすべてを追跡することの問題です。

    ステップ7:実装

    新しいインデックスを可能な限り精査した後、本番環境の準備が整いました。私は、インデックスの変更を低リスク、特に新しいものと見なしていることを認めます。問題がある場合は、すぐに削除して、元の状態に戻すことができます。変更/マージ/ドロップのシナリオでは、すべてをスクリプト化する必要があるため、必要に応じてインデックスを変更および再作成して、インデックスをリセットできます。インデックスを削除するのではなく、最初に無効にすることを常にお勧めします。そうすれば、定義について心配する必要がなくなります。インデックスを追加し直す必要がある場合は、単に再構築するだけです。

    概要

    インデックスを追加および/または統合する方法は異なる場合があります。クエリチューニングと同様に、完璧なプロセスはありません。インデックスチューニングに不慣れな人にとって、これはレビューする項目のスターターと重要な考慮事項を提供することを願っています。ある程度のオーバーヘッドを追加せずにインデックスを追加することは不可能です。これもまた、芸術の出番です。インデックスのメリットが変更のコストを上回っているかどうかを判断する必要があります。

    インデックスの調整は永続的で反復的なプロセスです。コードが変更され、新しいテーブルまたは機能が追加され、テーブル内のデータが変更されるため、これまでに完了したとは思いません。キンバリーには2つの投稿があります(https://www.sqlskills.com/blogs/kimberly/spring-cleaning-your-indexes-part-i/とhttps://www.sqlskills.com/blogs/kimberly/spring-cleaning- your-indexes-part-ii /)は、インデックスのクリーンアップについて説明しています。これで、始めるのに最適な時間になりました。そして最後に、誰かが「テーブルにはいくつのインデックスが必要ですか?」と尋ねるたびに。 「できるだけ多くの質問に答えるのに必要な数が最も少ない」というような答えをします。マジックナンバーはありません—インデックスがゼロのテーブルを見たことがあり、100を超えるテーブルを見たことがあります(カウントが高いテーブルを見たことがあると思います)。ゼロも100も適切ではありませんが、「正しい」数値は、利用可能なデータと経験を使用して把握する必要がある数値です。


    1. PostgreSQL11.1で既存の列をIdentityとして変更するにはどうすればよいですか?

    2. 複数のレコードを挿入してID値を取得するにはどうすればよいですか?

    3. MySQLデータベースで低速クエリ(低速クエリログ)のログを有効にする

    4. ListをJavaからOracleプロシージャに渡す方法は?