大きなテーブルでI/Oの問題があります。
このテーブルには次の主な特徴があります。
これはテーブルの実装です:
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オペレーションを削減する方法を教えてください。多分より良いインデックスを追加することによって?
投稿されたクエリプランは、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のパフォーマンスはこのクエリの影響をほとんど受けません。
ソリューションをテストするために、Entity Frameworkを単純なSqlCommandに置き換えました。その結果、パフォーマンスが大幅に向上しました。
クエリプランはSSMSと同じになり、論理的な読み取りと書き込みは実行ごとに最大8に減少します。
また、パーティションの範囲を月次から日次に変更した後、パフォーマンスが大幅に低下する理由も説明しています。パーティションの削除がないため、スキャンするパーティションが多くなりました。
ORMによって生成されたデータ型を変更できる場合は、このクエリの_PAGEIOLATCH_SH
_待機を減らすことができる場合があります。テーブルの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