2,135,044,521行テーブルのインデックスを最適化する


10

大きなテーブルでI / Oの問題があります。

一般的な統計

このテーブルには次の主な特徴があります。

  • 環境:Azure SQLデータベース(階層はP4プレミアム(500 DTU))
  • 行:2,135,044,521
  • 使用済みパーティション1,275
  • クラスター化およびパーティション化されたインデックス

型番

これはテーブルの実装です:

CREATE TABLE [data].[DemoUnitData](
    [UnitID] [bigint] NOT NULL,
    [Timestamp] [datetime] NOT NULL,
    [Value1] [decimal](18, 2) NULL,
    [Value2] [decimal](18, 2) NULL,
    [Value3] [decimal](18, 2) NULL,
    CONSTRAINT [PK_DemoUnitData] PRIMARY KEY CLUSTERED 
    (
        [UnitID] ASC,
        [Timestamp] ASC
    )
)
GO

ALTER TABLE [data].[DemoUnitData] WITH NOCHECK ADD CONSTRAINT [FK_DemoUnitData_Unit] FOREIGN KEY([UnitID])
REFERENCES [model].[Unit] ([ID])
GO

ALTER TABLE [data].[DemoUnitData] CHECK CONSTRAINT [FK_DemoUnitData_Unit]
GO

パーティショニングはこれに関連しています:

CREATE PARTITION SCHEME [DailyPartitionSchema] AS PARTITION [DailyPartitionFunction] ALL TO ([PRIMARY])

CREATE PARTITION FUNCTION [DailyPartitionFunction] (datetime) AS RANGE RIGHT
FOR VALUES (N'2017-07-25T00:00:00.000', N'2017-07-26T00:00:00.000', N'2017-07-27T00:00:00.000', ... )

サービスの質

インデックスと統計は、インクリメンタルな再構築/再編成/更新によって毎晩適切に維持されていると思います。

これらは、最も頻繁に使用されるインデックスパーティションの現在のインデックス統計です。

パーティション統計

これらは、最も頻繁に使用されるパーティションの現在の統計プロパティです。

統計学

問題

テーブルに対して高い頻度で単純なクエリを実行します。

SELECT [UnitID]
    ,[Timestamp]
    ,[Value1]
    ,[Value2]
    ,[Value3]
FROM [data].[DemoUnitData]
WHERE [UnitID] = 8877 AND [Timestamp] >= '2018-03-01' AND [Timestamp] < '2018-03-13'
OPTION (MAXDOP 1)

超過カウント

実行計画は次のようになります。https//www.brentozar.com/pastetheplan/?id = rJvI_4TtG

私の問題は、これらのクエリが非常に大量のI / O操作を生成し、その結果、PAGEIOLATCH_SH待機のボトルネックが発生することです。

トップ待機

質問

私は、PAGEIOLATCH_SH待機がよく最適化されていないインデックスに関連していることが多いことを読みました。I / Oオペレーションを削減する方法を教えてください。多分より良いインデックスを追加することによって?


回答1-@ S4V1Nからのコメントに関連

投稿されたクエリプランは、SSMSで実行したクエリからのものです。あなたのコメントの後、私はサーバーの履歴についていくつかの調査を行います。サービスから実行された実際のクエリは少し異なります(EntityFramework関連)。

(@p__linq__0 bigint,@p__linq__1 datetime2(7),@p__linq__2 datetime2(7)) 

SELECT 1 AS [C1], [Extent1] 
   .[Timestamp] AS [Timestamp], [Extent1] 
   .[Value1] AS [Value1], [Extent1] 
   .[Value2] AS [Value2], [Extent1] 
   .[Value3] AS [Value3]  
FROM [data].[DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2) OPTION (MAXDOP 1) 

また、計画は異なって見えます:

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

または

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

そして、ここに見られるように、DBパフォーマンスはこのクエリの影響をほとんど受けません。

トップSQL

回答2-@Joe Obbishからの回答に関連

ソリューションをテストするために、Entity Frameworkを単純なSqlCommandに置き換えました。その結果、パフォーマンスが大幅に向上しました。

クエリプランはSSMSと同じになり、論理的な読み取りと書き込みは実行ごとに最大8に減少します。

全体的なI / O負荷がほぼ0に低下します。 I / Oドロップ

また、パーティションの範囲を月次から日次に変更した後にパフォーマンスが大幅に低下する理由も説明しています。パーティションの削除がないため、スキャンするパーティションが多くなりました。


2
実行プランを見ると、そのクエリはそれほど問題になっているようには見えません。必要なパーティションのみをスキャンし、読み取り量が少なく、代わりにpageiolatch_sh待機(sos_sched ..)が報告されていません。とにかく、物理的な読み取りがなかったので、これは理解できます。それらの累積的な待機、または特定の時間にわたって行われますか?たぶん問題は、結局のところ他のクエリです。
S4V1N 2018年

上記の@ S4V1Nに詳細な回答を投稿しました
Steffen Mangold

回答:


7

PAGEIOLATCH_SHORMによって生成されたデータ型を変更できる場合は、このクエリの待機を減らすことができる場合があります。Timestampテーブルの列のデータタイプはですDATETIMEが、パラメータ@p__linq__1@p__linq__2データタイプはDATETIME2(7)です。その違いが、ORMクエリのクエリプランが、ハードフィルターされた検索フィルターを使用して投稿した最初のクエリプランよりもはるかに複雑である理由です。XMLでもこれに関するヒントを得ることができます。

<ScalarOperator ScalarString="GetRangeWithMismatchedTypes([@p__linq__1],NULL,(22))">

現状のまま、ORMクエリでは、パーティションを削除することはできません。1日分のデータを検索している場合でも、パーティション関数で定義されているすべてのパーティションについて少なくとも数回の論理読み取りが行われます。各パーティション内でインデックスシークを取得するため、SQL Serverが次のパーティションに移動するのに時間がかかりませんが、そのIOがすべて追加されている可能性があります。

確かに簡単な再現を行いました。パーティション関数内で定義された11のパーティションがあります。このクエリの場合:

DECLARE @p__linq__0 bigint = 2000;
DECLARE @p__linq__1 datetime2(7) = '20180103';
DECLARE @p__linq__2 datetime2(7) = '20180104';

SELECT 1 AS [C1]
, [Extent1].[Timestamp] AS [Timestamp]
, [Extent1].[Value1] AS [Value1]
FROM [DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2)
OPTION (MAXDOP 1) ;

IOは次のようになります。

テーブル「DemoUnitData」。スキャンカウント11、論理読み取り40

データ型を修正すると:

DECLARE @p__linq__0 bigint = 2000;
DECLARE @p__linq__1 datetime = '20180103';
DECLARE @p__linq__2 datetime = '20180104';

SELECT 1 AS [C1]
, [Extent1].[Timestamp] AS [Timestamp]
, [Extent1].[Value1] AS [Value1]
FROM [DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2)
OPTION (MAXDOP 1) ;

パーティションが削除された結果、IOが減少します。

テーブル「DemoUnitData」。スキャンカウント2、論理読み取り8

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