Saya memiliki masalah besar dengan paku CPU 100% karena rencana eksekusi yang buruk yang digunakan oleh permintaan tertentu. Saya menghabiskan berminggu-minggu sekarang dengan saya sendiri.
Database saya
DB sampel saya berisi 3 tabel sederhana.
[Datalogger]
CREATE TABLE [model].[DataLogger](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[ProjectID] [bigint] NULL,
CONSTRAINT [PK_DataLogger] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
[Inverter]
CREATE TABLE [model].[Inverter](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[SerialNumber] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Inverter] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY],
CONSTRAINT [UK_Inverter] UNIQUE NONCLUSTERED
(
[DataLoggerID] ASC,
[SerialNumber] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [model].[Inverter] WITH CHECK
ADD CONSTRAINT [FK_Inverter_DataLogger]
FOREIGN KEY([DataLoggerID])
REFERENCES [model].[DataLogger] ([ID])
[InverterData]
CREATE TABLE [data].[InverterData](
[InverterID] [bigint] NOT NULL,
[Timestamp] [datetime] NOT NULL,
[DayYield] [decimal](18, 2) NULL,
CONSTRAINT [PK_InverterData] PRIMARY KEY CLUSTERED
(
[InverterID] ASC,
[Timestamp] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
Stat dan Maintanance
The [InverterData]
meja berisi beberapa juta baris (berbeda dalam beberapa contoh PaaS) dipartisi di jung bulanan.
Semua pengindeks didefragmentasi dan semua stat dibangun kembali / direorganisasi sesuai kebutuhan setiap hari / minggu.
Kueri saya
Kueri adalah Entity Framework yang dihasilkan dan juga sederhana. Tapi saya berlari 1.000 kali per menit dan kinerja sangat penting.
SELECT
[Extent1].[InverterID] AS [InverterID],
[Extent1].[DayYield] AS [DayYield]
FROM [data].[InverterDayData] AS [Extent1]
INNER JOIN [model].[Inverter] AS [Extent2] ON [Extent1].[InverterID] = [Extent2].[ID]
INNER JOIN [model].[DataLogger] AS [Extent3] ON [Extent2].[DataLoggerID] = [Extent3].[ID]
WHERE ([Extent3].[ProjectID] = @p__linq__0)
AND ([Extent1].[Date] = @p__linq__1) OPTION (MAXDOP 1)
The MAXDOP 1
petunjuk adalah untuk masalah lain dengan rencana paralel lambat.
Rencana "baik"
Lebih dari 90% dari waktu rencana yang digunakan cepat kilat dan terlihat seperti ini:
Masalah
Pada siang hari, rencana yang baik berubah secara acak menjadi rencana yang buruk dan lambat.
Rencana "buruk" digunakan selama 10-60 menit dan kemudian diubah kembali ke rencana "baik". Paket "buruk" menaikkan CPU hingga 100% permanen.
Begini tampilannya:
Apa yang saya coba sejauh ini
Pikiran pertama saya Hash Match
adalah si bocah nakal. Jadi saya memodifikasi kueri dengan petunjuk baru.
...Extent1].[Date] = @p__linq__1) OPTION (MAXDOP 1, LOOP JOIN)
The LOOP JOIN
harus memaksa untuk menggunakan Nested Loop
instan Hash Match
.
Hasilnya adalah rencana 90% terlihat seperti sebelumnya. Tetapi rencana itu juga berubah secara acak menjadi yang buruk.
Paket "buruk" sekarang terlihat seperti ini (urutan loop tabel berubah):
CPU juga mengintip hingga 100% selama rencana "baru buruk".
Larutan?
Terlintas di benak saya untuk memaksakan rencana "baik". Tapi saya tidak tahu apakah ini ide yang bagus.
Di dalam paket terdapat indeks yang direkomendasikan yang menyertakan semua kolom. Tapi ini akan menggandakan tabel lengkap dan memperlambat inserst yang sering tinggi.
Tolong bantu aku!
Pembaruan 1 - terkait dengan komentar @James
Berikut adalah kedua paket (beberapa bidang tambahan ditampilkan dalam paket karena berasal dari tabel sebenarnya):
Rencana salah 1 (Pertandingan Hash)
Pembaruan 2 - terkait dengan jawaban @David Fowler
Paket yang buruk mulai menggunakan nilai parameter acak. Jadi normal saya @p__linq__1 ='2016-11-26 00:00:00.0000000' @p__linq__0 =20825
hari lubang dan dari rencana buruk datang pada nilai yang sama.
Saya tahu masalah sniffing parameter dari prosedur tersimpan dan cara menghindarinya di dalam SP. Apakah Anda memiliki petunjuk untuk saya bagaimana menghindari masalah ini untuk permintaan saya?
Membuat indeks yang direkomendasikan akan mencakup semua kolom. Ini akan menggandakan tabel lengkap dan memperlambat inserst, yang sering terjadi. Itu tidak "terasa" benar untuk membangun indeks yang hanya mengkloning tabel. Juga saya bermaksud menggandakan ukuran data tabel besar ini.
Pembaruan 3 - terkait dengan komentar @David Fowler
Itu juga tidak berhasil dan saya pikir itu tidak bisa. Untuk pemahaman yang lebih baik saya akan menjelaskan kepada Anda bagaimana kueri dipanggil.
Misalkan saya memiliki 3 entitas dalam [DataLogger]
tabel. Siang hari saya menelepon 3 pertanyaan yang sama berulang-ulang:
Permintaan basis:
...WHERE ([Extent3].[ProjectID] = @p__linq__0) AND ([Extent1].[Date] = @p__linq__1)
Parameter:
@p__linq__0 = 1; @p__linq__1 = '2018-01-05 00:00:00.0000000'
@p__linq__0 = 2; @p__linq__1 = '2018-01-05 00:00:00.0000000'
@p__linq__0 = 3; @p__linq__1 = '2018-01-05 00:00:00.0000000'
Parameter @p__linq__1
selalu tanggal yang sama. Tapi itu mengambil rencana buruk secara acak pada permintaan yang berjalan berkali-kali dengan rencana yang baik sebelumnya. Dengan parameter yang sama!
Perbarui 4 - terkait dengan komentar @Nic
Pemeliharaan berjalan setiap malam dan terlihat seperti ini.
Indeks
Jika Indeks terpecah-pecah lebih dari 5% ...
ALTER INDEX [{index}] ON [{table}] REORGANIZE
Jika Indeks terpecah-pecah lebih dari 30%, maka Indeks itu dibangun kembali ...
ALTER INDEX [{index}] ON [{table}] REBUILD WITH (ONLINE=ON, MAXDOP=1)
Jika Indeks dipartisi, itu akan terbukti tentang fragmentasi dan diubah per partisi ...
ALTER INDEX [{index}] ON [{table}] REBUILD PARTITION = {partitionNr} WITH (ONLINE=ON, MAXDOP=1)
Statistik
Semua statistik akan diperbarui jika modification_counter
lebih tinggi dari 0 ...
UPDATE STATISTICS [{schema}].[{object}] ([{stats}]) WITH FULLSCAN
atau dipartisi ..
UPDATE STATISTICS [{schema}].[{object}] ([{stats}]) WITH RESAMPLE ON PARTITIONS({partitionNr})
Pemeliharaan termasuk semua statistik, juga yang dihasilkan secara otomatis.
sumber
Jawaban:
Lihat rencananya, ada beberapa perbedaan antara yang baik dan yang buruk. Hal pertama yang perlu diperhatikan adalah bahwa rencana yang baik melakukan pencarian di InverterDayData di mana kedua paket yang buruk melakukan pemindaian. Mengapa demikian, jika Anda memeriksa perkiraan baris, Anda akan melihat bahwa rencana yang baik mengharapkan 1 baris sedangkan rencana yang buruk mengharapkan 6661 dan sekitar 7000 baris.
Sekarang lihat nilai parameter yang dikompilasi,
Good Plan @ p__linq__1 = '2016-11-26 00: 00: 00.0000000' @ p__linq__0 = 20825
Paket Buruk @ p__linq__1 = '2018-01-03 00: 00: 00.0000000' @ p__linq__0 = 20686
jadi itu terlihat bagi saya seperti itu masalah sniffing parameter, nilai parameter apa yang Anda berikan ke query itu ketika berkinerja buruk?
Ada rekomendasi indeks dalam rencana buruk pada InverterDayData yang terlihat masuk akal, saya akan mencoba menjalankannya dan melihat apakah itu membantu Anda. Mungkin memungkinkan SQL untuk melakukan pemindaian di atas meja.
sumber
...OPTION (OPTIMIZE FOR UNKNOWN)
petunjuk.