CPU 100% dengan rencana eksekusi yang buruk

8

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 1petunjuk adalah untuk masalah lain dengan rencana paralel lambat.

Rencana "baik"

Lebih dari 90% dari waktu rencana yang digunakan cepat kilat dan terlihat seperti ini:

rencana cepat

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:

rencana lambat

Apa yang saya coba sejauh ini

Pikiran pertama saya Hash Matchadalah si bocah nakal. Jadi saya memodifikasi kueri dengan petunjuk baru.

...Extent1].[Date] = @p__linq__1) OPTION (MAXDOP 1, LOOP JOIN)

The LOOP JOINharus memaksa untuk menggunakan Nested Loopinstan 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):

rencana juga lambat

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 yang bagus

Rencana salah 1 (Pertandingan Hash)

Rencana buruk 2 (Nested Loop)

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 =20825hari 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:

  1. @p__linq__0 = 1; @p__linq__1 = '2018-01-05 00:00:00.0000000'
  2. @p__linq__0 = 2; @p__linq__1 = '2018-01-05 00:00:00.0000000'
  3. @p__linq__0 = 3; @p__linq__1 = '2018-01-05 00:00:00.0000000'

Parameter @p__linq__1selalu 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_counterlebih 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.

Contoh

Steffen Mangold
sumber
Anda tidak menyebutkan ini, tetapi apakah Anda memperbarui statistik di salah satu tabel ini baru-baru ini?
Nic
Thx @Tidak saya menambahkan pembaruan pada pertanyaan. Silakan lihat informasi terperinci di sana.
Steffen Mangold

Jawaban:

3

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.

David Fowler
sumber
terima kasih atas jawaban Anda! Saya telah memodifikasi pertanyaan untuk Anda (di akhir).
Steffen Mangold
Saya tidak menyadari indeks yang disarankan mencakup seluruh tabel, itu bukan ide yang baik. Anda bisa saja mencoba indeks pada kolom tanggal.
David Fowler
menggunakan petunjuk OPTIMIZE FOR akan memaksa rencana untuk mengkompilasi dengan nilai-nilai yang baik, OPTION (OPTIMIZE FOR (@ p__linq__1 = '2016-11-26 00: 00: 00.0000000', @ p__linq__0 20825)) tetapi hati-hati dengan yang itu dan uji itu untuk memastikan bahwa itu tidak akan menyebabkan Anda sakit kepala yang tak terduga
David Fowler
hey @david, saya memposting pembaruan.
Steffen Mangold
Oke itu masalah sniffing parameter! Karena jumlah baris yang berbeda di dalam tabel dipartisi Indeks SEEK atau SCAN dipilih. Saya memperbaikinya dengan ...OPTION (OPTIMIZE FOR UNKNOWN)petunjuk.
Steffen Mangold