2

いくつかのテーブルを持つSQLServer2005データベースがあります。テーブルの1つは、複数のデバイスのタイムスタンプとメッセージカウンターを格納するために使用され、次の列があります。

CREATE TABLE [dbo].[Timestamps] (
[Id] [uniqueidentifier] NOT NULL,
[MessageCounter] [bigint] NULL,
[TimeReceived] [bigint] NULL,
[DeviceTime] [bigint] NULL,
[DeviceId] [int] NULL
)

Idは一意の主キー(Guid.Comb)であり、と列の両方にインデックスがDeviceIdありMessageCounterます。

私がやりたいのはMessageCounter、特定のデバイスの最後に挿入された行(最大の行)を見つけることです。

奇妙なのは、デバイス番号のクエリです。4(および1番を除く他のすべてのデバイス)はほぼ瞬時に戻ります。

select top 1 * 
   from "Timestamps"
   where DeviceId = 4
   order by MessageCounter desc

しかし、デバイス番号に対する同じクエリ。1は完了するのに永遠にかかります:

select top 1 * 
   from "Timestamps"
   where DeviceId = 1 /* this is the only line changed */
   order by MessageCounter desc

最も奇妙なことは、デバイス1の行数がデバイス4よりもはるかに少ないことです。

select count(*) from "Timestamps" where DeviceId = 4
(returns 1,839,210)

select count(*) from "Timestamps" where DeviceId = 1
(returns 323,276).

誰かが私が間違っている可能性があることの手がかりを持っていますか?

[編集]

両方のクエリの実行プランから、デバイス1(下の図)がインデックススキャンではるかに多くの行を作成していることがはっきりとわかります。

デバイス4(上)とデバイス1(下)の実行プランhttp://img295.imageshack.us/img295/5784/execplans.png

違いは、実行プラン図でインデックススキャンノードにカーソルを合わせると次のようになります。

Device 4 Actual Number of Rows: 1

Device 1 Actual Number of Rows: approx. 6,500,000

select count(*)私のクエリはデバイス1に対して約300,000行を返すため、6,500,000行は非常に奇妙な数です。

4

6 に答える 6

2

統計が最新であると確信していますか?UPDATESTATISTICSを使用します。

UPDATE STATISTICS dbo.Timestamps

クエリをどのように実行していますか?ストアドプロシージャを使用している場合は、パラメータのスニッフィングに問題がある可能性がありますか?

于 2010-07-05T15:34:46.903 に答える
2

にインデックスを作成してみてください(DeviceId, MessageCounter DESC)

また、次のクエリを試してください。

select * 
   from "Timestamps"
   where DeviceId = 1
   and MessageCounter = (SELECT MAX(MessageCounter) FROM "Timestamps" WHERE DeviceID = 1)

推測:パフォーマンスの違いは、がDeviceId = 1より多くのページに分散していることが原因である可能性がありますDeviceId = 4。並べ替えることで、一番上の行だけを選択したとしても、一致するすべてのページを浚渫しているのではないかと思います。

于 2010-07-05T15:37:05.127 に答える
1

降順でレコードを注文するMessageCounterと、最初のレコードを見つける前に6,500,000を耕す必要がDeviceId=4あるのに対し、他DeviceIdのレコードの方がはるかに優れているため、これが発生しているに違いないと思います。

DeviceId=4述語は、実行プランのFilterオペレーターまで機能しないと思います。

の複合インデックスはDeviceId, MessageCounterこれを解決します。しかし、DeviceId=4新しいデータが記録されなくなったレガシーデバイスを備えたデバイスはありますか?その場合、DeviceId = 4レコードを独自のテーブルに抽出し、パーティションビューを使用して、そのデバイスでのクエリが無関係なレコードの負荷をスキャンしないようにすることができる場合があります。

以下修正済み

また、クラスター化されたインデックスとしてGuid.Combを選択する理由は何ですか?上のクラスター化されたインデックスはDeviceId, MessageCounter、断片化とホットスポットの回避という点で同様の特性を持っていると思いますが、より便利です。

于 2010-07-06T09:16:07.870 に答える
1

実行プランの図は、使用されているインデックスを示していないため、あまり役に立ちません。

最も役立つ情報は、次のクエリから得られます

select DeviceId, max(MessageCounter) from "Timestamps" group by DeviceId

デバイス2〜4のMessageCounterは比較的高い数値だと思います。MessageCounterは比較的小さい数値です。

その場合、SQLサーバーはどのようにクエリを実行しますか。

サーバーは、MessageCounterインデックスを高い数値から低い数値に読み取ります。サーバーは、すべての行について、デバイスIDを比較するために、カスタードインデックスにネストされたシークを作成します。

デバイス2〜4の場合、サーバーがデバイス2〜4のMessageCounterインデックスで行を検出するため、これはすぐに終了します。デバイス1の場合、サーバーがデバイス1の最初の行を見つける前に、サーバーは600万を超えるシーク操作を必要とします。

deviceidインデックスを読み取り、custeredインデックスを検索する方が高速です。これは、323kがシークした後に停止するはずです。さらに悪い。

デバイスIDとMessageCounterの両方を含むインデックスが必要です(Marcelo Cantosが指摘したように)。

于 2010-07-06T12:43:02.793 に答える
0

私が最初に考えたのは、これはパラメーターのスニッフィングが原因である可能性があるということでした。基本的に、SQL Serverはクエリが初めて実行されるときに計画を立てますが、そのクエリは通常のワークロードを表すものではありませんでした。http://www.sqlshare.com/solve-parameter-sniffing-by-using-local-variables_531.aspxを参照してください

統計に関するアドバイスは良いですが、これら両方のクエリのクエリプランを確認する必要があると思います。これはクエリアナライザで実行できます。[実行]ボタンの右側にある約3つのボタンです。両方のクエリの計画の違いを確認してみてください...

于 2010-07-05T15:46:15.963 に答える
0

それらのクエリは、投稿したとおりにSQLServerに送信されますか

select top 1 * 
   from "Timestamps"
   where DeviceId = 4
   order by MessageCounter desc

またはNHibernateはパラメーター化されたクエリを使用しましたか?(where deviceid = @deviceidまたはそのようなもの)??

それはそれを説明するかもしれません-SQLServerはDeviceId=4のパラメータ化されたクエリを取得し、そのパラメータ値に対して機能する実行計画を考え出しますが、次の実行時に、DeviceId = 1の場合、つまずき、どういうわけか最初のクエリは、その2番目のケースには最適ではありません。

これらの2つのクエリを逆の順序で実行してみてください。最初にDeviceId=1で、次にDeviceId = 4-で、同じ結果が得られますか?

于 2010-07-05T16:49:37.007 に答える