Optimalkan indeks pada tabel baris 2.135.044.521

10

Saya memiliki masalah I / O dengan meja besar.

Statistik umum

Tabel ini memiliki karakteristik utama berikut:

  • lingkungan: Azure SQL Database (tingkatnya adalah P4 Premium (500 DTUs))
  • baris: 2.135.044.521
  • 1.275 partisi yang digunakan
  • indeks berkerumun dan dipartisi

Model

Ini adalah implementasi tabel:

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

Partisi terkait dengan ini:

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

Kualitas pelayanan

Saya pikir indeks dan statistik dipelihara dengan baik setiap malam dengan penambahan membangun kembali / mengatur ulang / memperbarui.

Ini adalah statistik indeks saat ini dari partisi indeks yang paling banyak digunakan:

Statistik Pemisahan

Ini adalah properti statistik saat ini dari partisi yang paling banyak digunakan:

Statistik

Masalah

Saya menjalankan kueri sederhana pada frekuensi tinggi terhadap tabel.

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)

jumlah kelebihan

Rencana eksekusi terlihat seperti ini: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG

Masalah saya adalah bahwa kueri ini menghasilkan jumlah operasi I / O yang sangat tinggi yang mengakibatkan kemacetan PAGEIOLATCH_SHmenunggu.

menunggu top

Pertanyaan

Saya telah membaca bahwa PAGEIOLATCH_SHmenunggu seringkali terkait dengan indeks yang tidak dioptimalkan dengan baik. Apakah ada rekomendasi yang Anda miliki untuk saya bagaimana mengurangi operasi I / O? Mungkin dengan menambahkan indeks yang lebih baik?


Jawaban 1 - terkait dengan komentar dari @ S4V1N

Paket kueri yang diposkan berasal dari kueri yang saya jalankan di SSMS. Setelah komentar Anda, saya melakukan riset tentang sejarah server. Kueri aktual yang dikecualikan dari layanan terlihat sedikit berbeda (terkait 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) 

Juga, rencananya terlihat berbeda:

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

atau

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

Dan seperti yang Anda lihat di sini, kinerja DB kami hampir tidak dipengaruhi oleh permintaan ini.

SQL teratas

Jawaban 2 - terkait dengan jawaban dari @ Jo Obbish

Untuk menguji solusi saya mengganti Entity Framework dengan SqlCommand sederhana. Hasilnya adalah peningkatan kinerja yang luar biasa!

Rencana kueri sekarang sama seperti di SSMS dan logis membaca dan menulis turun ke ~ 8 per eksekusi.

Penurunan beban I / O keseluruhan menjadi hampir 0! I / O drop

Ini juga menjelaskan mengapa saya mendapatkan penurunan kinerja besar setelah saya mengubah rentang partisi dari bulanan ke harian. Hilangnya penghapusan partisi mengakibatkan lebih banyak partisi untuk memindai.

Steffen Mangold
sumber
2
Melihat rencana eksekusi, kueri itu tampaknya tidak bermasalah sama sekali. Ia hanya memindai partisi yang diperlukan dengan jumlah sedikit bacaan dan belum melaporkan pageiolatch_sh menunggu (sos_sched ..) sebagai gantinya. Yang bisa dimengerti karena Anda tidak punya pembacaan fisik pula. Apakah kumulatif itu menunggu, atau diambil alih dalam jangka waktu tertentu? Mungkin masalahnya adalah beberapa permintaan lain.
S4V1N
Saya memposting jawaban rinci untuk Anda @ S4V1N di atas
Steffen Mangold

Jawaban:

7

Anda mungkin dapat mengurangi PAGEIOLATCH_SHmenunggu permintaan ini jika Anda dapat mengubah tipe data yang dihasilkan oleh ORM. The Timestampkolom dalam tabel Anda memiliki tipe data DATETIMEtetapi parameter @p__linq__1dan @p__linq__2memiliki tipe data DATETIME2(7). Perbedaan itu adalah mengapa rencana kueri untuk kueri ORM jauh lebih rumit daripada rencana kueri pertama yang Anda posting yang memiliki filter pencarian hardcoded. Anda bisa mendapatkan petunjuk tentang ini di XML juga:

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

Seperti, dengan permintaan ORM Anda tidak bisa mendapatkan penghapusan partisi. Anda akan mendapatkan setidaknya beberapa pembacaan logis untuk setiap partisi yang didefinisikan dalam fungsi partisi, bahkan jika Anda hanya mencari data sehari. Dalam setiap partisi Anda mendapatkan pencarian indeks sehingga tidak butuh waktu lama bagi SQL Server untuk beralih ke partisi berikutnya, tetapi mungkin semua IO itu bertambah.

Saya melakukan reproduksi sederhana untuk memastikan. Ada 11 partisi yang didefinisikan dalam fungsi partisi. Untuk kueri ini:

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

Seperti inilah bentuk IO:

Tabel 'DemoUnitData'. Pindai hitungan 11, berbunyi logis 40

Ketika saya memperbaiki tipe data:

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 berkurang karena penghapusan partisi:

Tabel 'DemoUnitData'. Pindai hitungan 2, bacaan logis 8

Joe Obbish
sumber