2,135,044,521 행 테이블에서 인덱스 최적화


10

큰 테이블에 I / O 문제가 있습니다.

일반 통계

이 표에는 다음과 같은 주요 특징이 있습니다.

  • 환경 : Azure SQL Database (계층은 P4 Premium (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)

exce count

실행 계획은 다음과 같습니다. 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

위의 @ 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 쿼리를 사용하면 파티션을 제거 할 수 없습니다. 하루 동안의 데이터 만 검색하더라도 파티션 기능에 정의 된 모든 파티션에 대해 최소한 몇 개의 논리적 읽기를 얻게됩니다. 각 파티션 내에서 인덱스 검색을 수행하므로 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.