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

SETSTATISTICSIOからの誤解を招くデータに注意してください

    私の同僚のSteveWright(ブログ| @SQL_Steve)は、彼が見た奇妙な結果について最近質問をしてくれました。最新のツールであるSQLSentryPlan Explorer PROの一部の機能をテストするために、彼は幅の広い大きなテーブルを作成し、それに対してさまざまなクエリを実行していました。あるケースでは、彼は大量のデータを返していましたが、 STATISTICS IO 読み取りがほとんど行われていないことを示していました。 #sqlhelpで何人かの人にpingを送信しましたが、この問題は誰も見たことがないようだったので、ブログに載せようと思いました。

    TL;DRバージョン

    つまり、 STATISTICS IOに頼ることができないシナリオがいくつかあることに十分注意してください。 正直に言うと。場合によっては(これは TOPを含みます および並列処理)、論理読み取りを大幅に過少報告します。これにより、そうでない場合でも、非常にI/Oに適したクエリがあると思われる可能性があります。他にも明らかなケースがあります。たとえば、スカラーのユーザー定義関数を使用して大量のI/Oを隠している場合などです。 Plan Explorerを使用すると、これらのケースがより明確になると思います。ただし、これは少し注意が必要です。

    問題のクエリ

    テーブルには3700万行、1行あたり最大250バイト、約100万ページ、非常に低い断片化(レベル0で0.42%、レベル1で15%、それ以降は0)があります。先頭のINTにクラスター化された主キーを除いて、計算列、実行中のUDF、およびインデックスはありません。 桁。 TOP を使用して、500,000行、すべての列を返す単純なクエリ およびSELECT*

    SET STATISTICS IO ON;
     
    SELECT TOP 500000 * FROM dbo.OrderHistory 
    WHERE OrderDate < (SELECT '19961029');

    (はい、私は自分のルールに違反していて、 SELECT *を使用していることに気づきました。 およびTOP ORDER BYなし 、ただし、簡単にするために、オプティマイザへの影響を最小限に抑えるように最善を尽くしています。)

    結果:

    (影響を受ける500000行)
    テーブル'OrderHistory'。スキャンカウント1、論理読み取り23、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。

    500,000行を返しますが、約10秒かかります。論理読み取り数に問題があることがすぐにわかります。基になるデータについてまだ知らなかったとしても、Management Studioのグ​​リッド結果から、メモリからのものかキャッシュからのものかを問わず、23ページを超えるデータがプルされていることがわかります。これは<のどこかに反映されるはずです。 code> STATISTICS IO 。計画を見て…

    …並列処理があり、テーブル全体をスキャンしたことがわかります。では、論理読み取りが23個しかないのはどうしてですか?

    別の「同一の」クエリ

    スティーブへの最初の質問の1つは、「並列処理を排除するとどうなりますか?」でした。だから私はそれを試してみました。元のサブクエリバージョンを使用して、 MAXDOP 1を追加しました :

    SET STATISTICS IO ON;
     
    SELECT TOP 500000 * FROM dbo.OrderHistory 
    WHERE OrderDate < (SELECT '19961029') OPTION (MAXDOP 1);

    結果と計画:

    (影響を受ける500000行)
    テーブル'OrderHistory'。スキャンカウント1、論理読み取り149589、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。

    計画は少し複雑ではなく、(明らかな理由で)並列処理がないため、 STATISTICS IO 論理的な読み取りカウントについて、はるかに信頼できる数値を示しています。

    真実は何ですか?

    これらのクエリの1つが完全な真実を伝えていないことを確認するのは難しいことではありません。 STATISTICS IO 全体像を教えてくれないかもしれませんが、たぶんトレースはそうするでしょう。 Plan Explorerで実際の実行プランを生成してランタイムメトリックを取得すると、魔法の低読み取りクエリは実際には、魔法のピクシーダストのクラウドからではなく、メモリまたはディスクからデータをプルしていることがわかります。実際、他のバージョンよりも*多くの*読み取りがあります:

    したがって、読み取りが行われていることは明らかです。読み取りが STATISTICS IOに正しく表示されていないだけです。 出力。

    問題は何ですか?

    まあ、正直に言うと、並列処理が確実に役割を果たしているという事実以外はわかりません。それはある種の競合状態のようです。 STATISTICS IO (そして、それがデータを取得する場所なので、[テーブルI / O]タブ)は非常に誤解を招く読み取り数を示しています。クエリが探しているすべてのデータを返すことは明らかであり、トレース結果から、浸透ではなく読み取りを使用していることが明らかです。 Paul White(ブログ| @SQL_Kiwi)に質問したところ、スレッド前のI / Oカウントの一部のみが合計に含まれていることを示唆しました(これはバグであることに同意します)。

    自宅でこれを試してみたい場合は、AdventureWorks(2008、2008 R2、および2012バージョンに対して再現する必要があります)と次のクエリだけが必要です。

    SET STATISTICS IO ON;
    DBCC SETCPUWEIGHT(1000) WITH NO_INFOMSGS;
    GO
     
    SELECT TOP (15000) * 
    FROM Sales.SalesOrderHeader 
    WHERE OrderDate < (SELECT '20080101');
     
    SELECT TOP (15000) * 
    FROM Sales.SalesOrderHeader 
    WHERE OrderDate < (SELECT '20080101') 
    OPTION (MAXDOP 1);
     
    DBCC SETCPUWEIGHT(1) WITH NO_INFOMSGS;

    SETCPUWEIGHTに注意してください 並列処理を同軸にするためにのみ使用されます。詳細については、PlanCostingに関するPaulWhiteのブログ投稿を参照してください。)

    結果:

    テーブル'SalesOrderHeader'。スキャンカウント1、論理読み取り4、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。
    テーブル'SalesOrderHeader'。スキャンカウント1、論理読み取り333、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。

    ポールはさらに簡単な再現を指摘しました:

    SET STATISTICS IO ON;
    GO
     
    SELECT TOP (15000) *
    FROM Production.TransactionHistory
    WHERE TransactionDate < (SELECT '20080101')
    OPTION (QUERYTRACEON 8649, MAXDOP 4);
     
    SELECT TOP (15000) *
    FROM Production.TransactionHistory AS th
    WHERE TransactionDate < (SELECT '20080101');

    結果:

    テーブル「TransactionHistory」。スキャンカウント1、論理読み取り5、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。
    表「TransactionHistory」。スキャンカウント1、論理読み取り110、物理読み取り0、先読み読み取り0、lob論理読み取り0、lob物理読み取り0、lob先読み読み取り0。

    したがって、 TOP を使用すると、これを自由に簡単に再現できるようです。 演算子と十分に低いDOP。バグを報告しました:

    • STATISTICS IOは、並列プランの論理読み取りを過少報告します

    そして、Paulは、並列処理に関連する他の2つのバグを報告しました。最初のバグは、私たちの会話の結果です。

    • ルックアップでのプッシュされた述語によるカーディナリティ推定エラー[関連ブログ投稿]
    • 並列処理とトップのパフォーマンスの低下[関連するブログ投稿]

    (懐かしいので、ここに私が数年前に指摘した他の6つの並列処理のバグがあります。)

    レッスンは何ですか?

    単一のソースを信頼することに注意してください。 STATISTICS IOだけを見る場合 このようにクエリを変更した後、継続時間の増加ではなく、読み取りの奇跡的な減少に焦点を合わせたくなるかもしれません。その時点で、クエリに多大なパフォーマンスの影響を与えたと思って、背中を軽くたたき、早めに仕事を辞めて週末を楽しむことができます。もちろん、真実から遠く離れることはできません。


    1. DATE_SUB()の例– MySQL

    2. SQL Serverでのdatetime2とdatetimeoffsetの違い:違いは何ですか?

    3. JDBCでストアドプロシージャを呼び出す方法

    4. PostgreSQL13へのアップグレード