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

sys.partitionsのパフォーマンス

    sys.partitionsは2つの結果セット(行ストアと列ストア)のUNION ALLのようであり、私のクエリのほとんどはsysrowsetの2つのスキャンになります。探している行がrowstoreであることがわかっている場合、sys.partitionsのクエリに適用できるフィルターはありますか?

    この質問はJakeManskeによって#sqlhelpに投稿され、ErikDarlingによって私の注意を引きました。

    sys.partitionsでパフォーマンスの問題が発生したことを覚えていません。 。私の最初の考え(Joey D'Antoniによってエコーされた)は、data_compressionのフィルターでした。 列すべき 冗長スキャンを回避し、クエリの実行時間を約半分に短縮します。ただし、この述語はプッシュダウンされないため、少し解凍する必要があります。

    sys.partitionsが遅いのはなぜですか?

    sys.partitionsの定義を見ると 、それは基本的にジェイクが説明したものです– UNION ALL THREE を使用して、すべての列ストアおよび行ストアパーティションの sys.sysrowsetsへの明示的な参照 (ここに省略されたソース):

    CREATE VIEW sys.partitions AS
        WITH partitions_columnstore(...cols...)
        AS
        (
          SELECT ...cols...,
            cmprlevel AS data_compression ...
          	FROM sys.sysrowsets rs OUTER APPLY OpenRowset(TABLE ALUCOUNT, rs.rowsetid, 0, 0, 0) ct
    -------- *** ^^^^^^^^^^^^^^ ***
    		LEFT JOIN sys.syspalvalues cl ...
    		WHERE ... sysconv(bit, rs.status & 0x00010000) = 1 -- Consider only columnstore base indexes
        ),
        partitions_rowstore(...cols...)
        AS
        (
          SELECT ...cols...,
            cmprlevel AS data_compression ...
    	FROM sys.sysrowsets rs 
    -------- *** ^^^^^^^^^^^^^^ ***
    		LEFT JOIN sys.syspalvalues cl ...
    		WHERE ... sysconv(bit, rs.status & 0x00010000) = 0 -- Ignore columnstore base indexes and orphaned rows.
        )
        SELECT ...cols...
        from partitions_rowstore p OUTER APPLY OpenRowset(TABLE ALUCOUNT, p.partition_id, 0, 0, p.object_id) ct
        union all
        SELECT ...cols...
        FROM partitions_columnstore as P1
        LEFT JOIN 
          (SELECT ...cols...
           FROM sys.sysrowsets rs OUTER APPLY OpenRowset(TABLE ALUCOUNT, rs.rowsetid, 0, 0, 0) ct
    ------- *** ^^^^^^^^^^^^^^ ***
          ) ...

    このビューは、おそらく下位互換性の懸念のために、一緒に石畳になっているようです。特にsys.sysrowsetsのみを参照するように、より効率的に書き直すことができます。 およびTABLE ALUCOUNT オブジェクトを一度。しかし、あなたや私が今それについてできることはあまりありません。

    cmprlevel sys.sysrowsetsから取得 (列参照のエイリアスプレフィックスが役立ちます)。 OUTER APPLYの前に、そこにある列に対する述語が論理的に発生することを期待します。 スキャンの1つを防ぐことができますが、それは起こりません。次の簡単なクエリを実行します:

    SELECT * 
      FROM sys.partitions AS p
      INNER JOIN sys.objects AS o 
      ON p.object_id = o.object_id
      WHERE o.is_ms_shipped = 0;

    データベースに列ストアインデックスがある場合、次のプランが生成されます(クリックして拡大):

    列ストアインデックスが存在するsys.partitionsの計画

    そして、ない場合は次の計画(クリックして拡大):

    列ストアインデックスが存在しないsys.partitionsの計画

    これらは同じ推定計画ですが、SentryOne Plan Explorerは、実行時に操作がスキップされたときに強調表示できます。これは後者の場合の3回目のスキャンで発生しますが、その実行時スキャン数をさらに減らす方法があるかどうかはわかりません。 2番目のスキャンは、クエリがゼロ行を返した場合でも発生します。

    ジェイクの場合、彼はたくさんを持っています オブジェクトの数が多いため、このスキャンを2回実行しても、目立ち、苦痛を伴い、1回は多すぎます。そして正直なところ、TABLE ALUCOUNTかどうかはわかりません 、内部の文書化されていないループバック呼び出しも、これらの大きなオブジェクトのいくつかを複数回スキャンする必要があります。

    ソースを振り返って、計画の形を強制することができるビューに渡すことができる他の述語があるかどうか疑問に思いましたが、実際に影響を与える可能性のあるものはないと思います。

    別のビューは機能しますか?

    ただし、まったく別のビューを試すこともできます。両方のsys.sysrowsetsへの参照を含む他のビューを探しました およびALUCOUNT 、リストに表示されるものは複数ありますが、有望なのは2つだけです:sys.internal_partitions およびsys.system_internals_partitions

    sys.internal_partitions

    sys.internal_partitionsを試しました 最初:

    SELECT *
      FROM sys.internal_partitions AS p
      INNER JOIN sys.objects AS o 
      ON p.object_id = o.object_id
      WHERE o.is_ms_shipped = 0;

    しかし、計画はそれほど良くはありませんでした(クリックして拡大):

    sys.internal_partitionsの計画

    sys.sysrowsetsに対するスキャンは2つだけです。 今回は、クエリが目的の行の生成に近づかないため、スキャンはとにかく無関係です。列ストア関連のオブジェクトの行のみが表示されます(ドキュメントに記載されています)。

    sys.system_internals_partitions

    sys.system_internals_partitionsを試してみましょう 。サポートされていないため(ここの警告を参照)、これについては少し警戒していますが、しばらくお待ちください:

    SELECT *
      FROM sys.system_internals_partitions AS p
      INNER JOIN sys.objects AS o 
      ON p.object_id = o.object_id
      WHERE o.is_ms_shipped = 0;

    列ストアインデックスを使用するデータベースでは、sys.sysschobjsに対するスキャンがあります。 、ただし現在は1つのみ sys.sysrowsetsに対してスキャンします (クリックして拡大):

    列ストアインデックスが存在するsys.system_internals_partitionsの計画

    列ストアインデックスを使用せずにデータベースで同じクエリを実行すると、計画はさらに単純になり、sys.sysschobjsに対してシークが行われます。 (クリックして拡大):

    列ストアインデックスが存在しない場合のsys.system_internals_partitionsの計画

    ただし、これはかなりではありません 列ストアインデックスからのアーティファクトも含まれているため、私たちが求めているもの、または少なくともJakeが求めていたものではありません。これらのフィルターを追加すると、実際の出力は以前のはるかに高価なクエリと一致するようになります。

    SELECT *
      FROM sys.system_internals_partitions AS p
      INNER JOIN sys.objects AS o ON p.object_id = o.object_id
      WHERE o.is_ms_shipped = 0
        AND p.is_columnstore = 0
        AND p.is_orphaned = 0;

    ボーナスとして、sys.sysschobjsに対するスキャン 列ストアオブジェクトを使用するデータベースでもシークになっています。私たちのほとんどはその違いに気付かないでしょうが、ジェイクのようなシナリオにいる場合は、(クリックして拡大):

    追加のフィルターを使用した、sys.system_internals_partitionsのより簡単な計画

    sys.system_internals_partitions sys.partitionsとは異なる列のセットを公開します (完全に異なるものもあれば、新しい名前を持つものもあります)。したがって、ダウンストリームで出力を消費する場合は、それらに合わせて調整する必要があります。また、行ストア、メモリ最適化、列ストアのインデックス全体で必要なすべての情報が返されることを検証する必要があります。これらの厄介なヒープを忘れないでください。そして最後に、sを除外する準備をします internals 何度も何度も。

    結論

    前述したように、このシステムビューは公式にはサポートされていないため、その機能はいつでも変更される可能性があります。また、Dedicated Administrator Connection(DAC)の下に移動したり、製品から完全に削除したりすることもできます。 sys.partitionsの場合は、このアプローチを自由に使用してください はうまく機能していませんが、バックアップ計画があることを確認してください。また、念のため、SQL Serverの将来のバージョンのテストを開始するとき、またはアップグレードした後に、回帰テストとして文書化されていることを確認してください。


    1. MySQL:カンマ区切りのリストを複数の行に分割

    2. テーブル行の編集GUIを使用してSQLServerテーブルに行を挿入する方法-SQLServer/TSQLチュートリアルパート101

    3. HikariCP:Oracle11gのmaxLifetimeを設定するために考慮すべきデータベースレベルのタイムアウト

    4. テーブル値関数によって返される列を検索する(T-SQLの例)