Оптимізуйте індекс у таблиці 2,135,044,521 рядків


10

У мене є проблема вводу / виводу з великою таблицею.

Загальна статистика

Таблиця має такі основні характеристики:

  • середовище: база даних SQL Azure (рівень 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)

перевищує кількість

План виконання виглядає приблизно так: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG

Моя проблема полягає в тому, що ці запити призводять до надзвичайно великої кількості операцій вводу / виводу, що призводить до вузького місця PAGEIOLATCH_SHочікування.

верх чекає

Питання

Я читав, що PAGEIOLATCH_SHочікування часто пов’язані з недостатньо оптимізованими індексами. Чи є у мене якісь рекомендації щодо зменшення операцій вводу / виводу? Може, додавши кращий індекс?


Відповідь 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

І, як ви бачите тут, на цей запит навряд чи впливає наша продуктивність.

Топ SQL

Відповідь 2 - пов'язана з відповіддю від @Joe Obbish

Для тестування рішення я замінив Entity Framework простим SqlCommand. Результатом було дивовижне підвищення продуктивності!

План запитів тепер такий же, як у SSMS, і логічне зчитування та записування падає до ~ 8 за виконання.

Загальне падіння навантаження вводу / виводу майже до 0! Крапля вводу / виводу

Це також пояснює, чому я отримую великий падіння продуктивності після зміни діапазону розділів із щомісячного на щоденний. Відсутність усунення розділів призвело до сканування більшої кількості розділів.


2
Дивлячись на план виконання, цей запит зовсім не видається проблематичним. Він просканував лише необхідні розділи з низькою кількістю прочитаних, і він не повідомив, що pageiolatch_sh очікує (sos_sched ..). Що зрозуміло, оскільки у вас жодного разу не було фізичних читань. Це кумулятивні очікування чи за певний час зайняті? Можливо, проблема - це ще якийсь запит.
S4V1N

Я детально відповів вам на @ @ S4V1N вище
Steffen Mangold

Відповіді:


7

Ви можете зменшити PAGEIOLATCH_SHочікування на цей запит, якщо ви зможете змінити типи даних, згенеровані ORM. У 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.