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:
Ini adalah properti statistik saat ini dari partisi yang paling banyak digunakan:
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)
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_SH
menunggu.
Pertanyaan
Saya telah membaca bahwa PAGEIOLATCH_SH
menunggu 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.
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!
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.
sumber
Jawaban:
Anda mungkin dapat mengurangi
PAGEIOLATCH_SH
menunggu permintaan ini jika Anda dapat mengubah tipe data yang dihasilkan oleh ORM. TheTimestamp
kolom dalam tabel Anda memiliki tipe dataDATETIME
tetapi parameter@p__linq__1
dan@p__linq__2
memiliki tipe dataDATETIME2(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: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:
Seperti inilah bentuk IO:
Ketika saya memperbaiki tipe data:
IO berkurang karena penghapusan partisi:
sumber