インデックス列の非常に大きなテーブルからのSELECT TOP 1は非常に遅いですが、逆順ではありません(「desc」)


16

強力なサーバーでSQL Server 2014を実行している約1 TBの大規模データベースがあります。数年はすべてうまくいきました。約2週間前に、次のような完全なメンテナンスを行いました。すべてのソフトウェアアップデートをインストールします。すべてのインデックスを再構築し、DBファイルを圧縮します。ただし、実際の負荷が同じ場合、特定の段階でDBのCPU使用率が100%から150%増加するとは予想していませんでした。

多くのトラブルシューティングを行った後、非常に単純なクエリに絞り込みましたが、解決策が見つかりませんでした。クエリは非常に簡単です。

select top 1 EventID from EventLog with (nolock) order by EventID

常に約1.5秒かかります!ただし、「desc」を使用した同様のクエリには常に約0ミリ秒かかります。

select top 1 EventID from EventLog with (nolock) order by EventID desc

PTableには約5億行があります。データ型がbigint(Identity列)EventIDのプライマリクラスター化インデックス列(ordered ASC)です。上部のテーブルにデータを挿入する複数のスレッド(より大きなEventID)があり、下部からデータを削除する1つのスレッド(より小さなEventID)があります。

SMSSでは、2つのクエリが常に同じ実行プランを使用することを確認しました。

  • クラスター化インデックススキャン。

  • 推定および実際の行番号は両方とも1です。

  • 推定および実際の実行回数は両方とも1です。

  • 推定I / Oコストは8500です(高いようです)

  • 連続して実行した場合、クエリコストは両方で同じ50%です。

インデックス統計を更新しましたがwith fullscan、問題は続きました。インデックスを再構築しましたが、問題は半日消えたようですが、戻ってきました。

IO統計をオンにしました:

set statistics io on

次に、2つのクエリを連続して実行し、次の情報を見つけました。

(最初のクエリについては、遅いクエリ)

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

(2番目のクエリ、高速クエリの場合)

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

論理読み取りの大きな違いに注意してください。両方の場合にインデックスが使用されます。

インデックスの断片化は少し説明できますが、その影響は非常に小さいと思います。そして、問題は以前に一度も起こりませんでした。別の証拠は、次のようなクエリを実行した場合です。

select * from EventLog with (nolock) where EventID=xxxx   

xxxxをテーブル内の最小のEventIDに設定しても、クエリは常に高速です。

チェックしましたが、ロック/ブロックの問題はありません。

注:上記の問題を単純化しようとしました。「PTable」は実際には「EventLog」です。PIDですEventID

NOLOCKヒントなしで同じ結果をテストできます。

誰でも助けることができますか?

ここに画像の説明を入力してください

ここに画像の説明を入力してください

XMLでのより詳細なクエリ実行計画は次のとおりです。

https://www.brentozar.com/pastetheplan/?id=SJ3eiVnob

https://www.brentozar.com/pastetheplan/?id=r1rOjVhoZ

create tableステートメントを提供することは重要ではないと思います。これは古いデータベースであり、メンテナンスまで長い間完全に正常に実行されています。私たちは多くの調査を自分で行い、質問で提供された情報に絞り込みました。

テーブルは通常、EventID列を主キーとして作成され、これはidentitytypeの列ですbigint。現時点では、問題はインデックスの断片化にあると思います。インデックスの再構築直後、問題は半日はなくなったようです。しかし、なぜそれがそんなに早く戻ってきたのか...?

回答:


17

クラスター化インデックススキャンは、423,723の論理読み取りを示し、最初の行を返します。1926ミリ秒かかります。

ナッツ

これは、インデックスの順序で最初の行を見つけるにはかなり多くのようです。

ほとんどの場合、ゴーストクリーンアップタスクが大幅に遅れているか、停止しています。ghost_record_countクラスター化インデックスをチェックインし、sys.dm_db_index_physical_stats経時的な変化を監視する必要があります。

一定の削除アクティビティが発生しているインデックスの末尾からの順序付きスキャンでは、返される最初の「生きた」行を見つける前に、非常に多くのゴーストレコードをスキャンする必要があります。これは、追加の論理読み取りについて説明しています。Bツリーをインデックスの最小値までシークすると、ゴーストレコードがはるかに少なくなります。

別のパフォーマンスに影響する要因は、Paul Randalによる「ストレージエンジンの内部:ゴーストのクリーンアップ」で説明したように、スキャン自体がゴーストレコードの削除を担当することです。

トレースフラグ661(ゴーストクリーンアップを無効にする)がアクティブでないことを確認する必要があります。

解決策

ゴーストクリーンアッププロセスが完全に停止した場合、最も効果的な解決策は通常、SQL Serverインスタンスを再起動することです。また、SQL Serverが最新の累積更新プログラムのいずれかを実行していることを確認する必要があります。長年にわたって多くのゴーストクリーンアップバグがありました。

特定の場合:

この問題は、同じサーバー上の別のテストデータベースが原因であることが判明しました。そのテストデータベースは「データ損失」で復元され、破損しています。驚いたことに、ゴーストクリーンアッププロセスは明らかにそのデータベースに残っていました。破損したデータベースをSMSSから削除すると、問題は自動的に解決しました(長時間かかり、DBが短時間ロックアップする可能性がありました)。

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