Tối ưu hóa chỉ mục trên bảng hàng 2.135.044.521


10

Tôi có một vấn đề I / O với một bảng lớn.

Thống kê chung

Bảng có các đặc điểm chính sau:

  • môi trường: Cơ sở dữ liệu Azure SQL (tầng là P4 Premium (500 DTU))
  • hàng: 2.135.044.521
  • 1.275 phân vùng đã sử dụng
  • chỉ mục phân cụm và phân vùng

Mô hình

Đây là bảng thực hiện:

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

Việc phân vùng có liên quan đến điều này:

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', ... )

Chất lượng dịch vụ

Tôi nghĩ rằng các chỉ số và số liệu thống kê được duy trì tốt mỗi đêm bằng cách xây dựng lại / sắp xếp lại / cập nhật gia tăng.

Đây là các số liệu thống kê chỉ mục hiện tại của các phân vùng chỉ mục được sử dụng nhiều nhất:

Chỉ số phân vùng

Đây là các thuộc tính thống kê hiện tại của các phân vùng được sử dụng nhiều nhất:

Số liệu thống kê

Vấn đề

Tôi chạy một truy vấn đơn giản trên một tần số cao so với bảng.

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)

số đếm

Kế hoạch thực hiện trông như thế này: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG

Vấn đề của tôi là các truy vấn này tạo ra số lượng hoạt động I / O cực kỳ cao dẫn đến tắc nghẽn PAGEIOLATCH_SHchờ đợi.

chờ đợi hàng đầu

Câu hỏi

Tôi đã đọc rằng PAGEIOLATCH_SHchờ đợi thường liên quan đến các chỉ mục không được tối ưu hóa tốt. Bạn có đề xuất gì cho tôi về cách giảm hoạt động I / O không? Có lẽ bằng cách thêm một chỉ số tốt hơn?


Câu trả lời 1 - liên quan đến nhận xét từ @ S4V1N

Kế hoạch truy vấn được đăng là từ một truy vấn tôi đã thực hiện trong SSMS. Sau khi nhận xét của bạn, tôi làm một số nghiên cứu về lịch sử máy chủ. Truy vấn tình cờ xuất phát từ dịch vụ có vẻ hơi khác (liên quan đến 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) 

Ngoài ra, kế hoạch có vẻ khác nhau:

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

hoặc là

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

Và như bạn có thể thấy ở đây, hiệu suất DB của chúng tôi hầu như không bị ảnh hưởng bởi truy vấn này.

SQL hàng đầu

Câu trả lời 2 - liên quan đến câu trả lời từ @Joe Obbish

Để thử nghiệm giải pháp, tôi đã thay thế Entity Framework bằng một SqlCommand đơn giản. Kết quả là một hiệu suất tuyệt vời tăng!

Kế hoạch truy vấn bây giờ giống như trong SSMS và số lần đọc và ghi logic giảm xuống ~ 8 mỗi lần thực hiện.

Tổng tải I / O giảm xuống gần 0! Tôi thả

Nó cũng giải thích lý do tại sao tôi giảm hiệu suất lớn sau khi tôi thay đổi phạm vi phân vùng từ hàng tháng sang hàng ngày. Việc thiếu loại bỏ phân vùng dẫn đến nhiều phân vùng để quét.


2
Nhìn vào kế hoạch thực hiện, truy vấn đó dường như không có vấn đề gì cả. Nó chỉ quét các phân vùng cần thiết với số lượng đọc thấp và nó đã không báo cáo pageiolatch_sh chờ đợi (sos_sched ..). Điều này là dễ hiểu bởi vì dù sao bạn cũng không có đọc vật lý. Là những chờ đợi tích lũy, hoặc chiếm trong một khoảng thời gian nhất định? Có lẽ vấn đề là một số truy vấn khác sau khi tất cả.
S4V1N

Tôi đã đăng câu trả lời chi tiết cho bạn @ S4V1N ở trên
Steffen Mangold

Câu trả lời:


7

Bạn có thể giảm PAGEIOLATCH_SHchờ đợi cho truy vấn này nếu bạn có thể thay đổi các loại dữ liệu được tạo bởi ORM. Các Timestampcột trong bảng của bạn có một kiểu dữ liệu DATETIMEnhưng các thông số @p__linq__1@p__linq__2có kiểu dữ liệu của DATETIME2(7). Sự khác biệt đó là lý do tại sao kế hoạch truy vấn cho các truy vấn ORM phức tạp hơn nhiều so với kế hoạch truy vấn đầu tiên mà bạn đã đăng có các bộ lọc tìm kiếm được mã hóa cứng. Bạn cũng có thể nhận được một gợi ý về điều này trong XML:

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

Như vậy, với truy vấn ORM, bạn không thể loại bỏ phân vùng. Bạn sẽ nhận được ít nhất một vài lần đọc logic cho mọi phân vùng được xác định trong chức năng phân vùng, ngay cả khi bạn chỉ tìm kiếm một ngày dữ liệu. Trong mỗi phân vùng, bạn nhận được một chỉ mục tìm kiếm để SQL Server không mất nhiều thời gian để chuyển sang phân vùng tiếp theo, nhưng có lẽ tất cả các IO đó đều được thêm vào.

Tôi đã làm một sinh sản đơn giản để chắc chắn. Có 11 phân vùng được định nghĩa trong chức năng phân vùng. Đối với truy vấn này:

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) ;

Đây là những gì IO trông giống như:

Bảng 'DemoUnitData'. Quét số 11, đọc logic 40

Khi tôi sửa các kiểu dữ liệu:

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 bị giảm do loại bỏ phân vùng:

Bảng 'DemoUnitData'. Quét số 2, đọc logic 8

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.