Analiz #1191
BittiHedef: sorgu 0x72D0DB6D73F273CF · DB-10 · dönem 2026-10-03 → 2026-10-09
Çalıştırma
- Model
- gemma4:12b (gemma4)Kurum içi
- Prompt şablonu
- #16
- Token (giriş / çıkış)
- 12.077 / 1.267
- Süre
- 101,1 sn
- Onarım
- 0
- Kural bulgusu
- 2
- Getirim (bağlama giren)
- KB 2 · profil 5 · vaka 0
Özet
güven: OrtaSorgu, sys.sysrowsets üzerindeki bir Index Spool (F1) nedeniyle performans kaybı yaşamaktadır. Bu durum, sorgu sırasında geçici bir indeks oluşturulması gerektirdiğini ve yüksek CPU kullanımı (F2) nedeniyle gecikmeye yol açtığını göstermektedir.
- Güven high → medium: tablo, kolon ve index bilgisi (metadata) yok.
Kök nedenler
- sys.sysrowsets üzerindeki Index Spool (Eager Index Spool) kullanımı, sorgu sırasında geçici bir indeks oluşturulmasına neden olmaktadır. F1
- Sorgu, yüksek CPU maliyetine sahip bir çalışma profilinde (99% CPU) çalışmaktadır. F2
Bulgular
2 bulgu| Id | Ciddiyet | Kural | Başlık | Kanıt |
|---|---|---|---|---|
| F1 | yüksek | INDEX_SPOOL | Geçici index (Eager Index Spool) — SQL Server iç nesnesi, sorgu yeniden yazılmalı | Optimizer sys.sysrowsets için sorgu sırasında geçici index kuruyor (her çalıştırmada yeniden; maliyet payı %50,4): NodeId 37: anahtar [idmajor, idminor], 16 kez aranıyor, 40.658 satır okunup kuruluyor. Kaynak SQL Server iç nesnesi (sistem görünümü/fonksiyonu): kalıcı index kurulamaz; spool'u doğuran join/IN yapısı yeniden yazılmalı. |
| F2 | düşük | RUNTIME_PROFILE | Süre bileşimi: CPU, bekleme ve kuyruk | ort. süre 7792,5 ms, ort. CPU 7716,4 ms (%99 CPU, 7 çalışma) — süre çoğunlukla CPU: okunan/işlenen satır azaltılmalı (erişim yolu), bekleme ikincil; en uzun tek çalışma 15.669,0 ms; QDS bekleme kategorileri (dönem toplamı): Memory 2.656 ms, CPU 1.523 ms, Buffer IO 31 ms; memory grant beklemesi: sorgu bellek izni için kuyruğa giriyor (plan isteği 544 KB); eşzamanlı grant baskısı ve max server memory incelenmeli, grant gerektirmeyen plan (seek + stream aggregate / sıralı erişim) bu kuyruğu da kaldırır |
Plan özeti
Plan tipi: tahmini; statement sayısı 1; toplam maliyet 3,516; tahmini satır 16,1 CE sürümü: 170; DOP: ?; paralel değil: MaxDOPSetToOne Memory grant (KB): istenen 544 (tahmini plan; verilen/kullanılan yalnız actual planda) Operatörler (maliyet sırasıyla):
Benzer vakalar
LLM bağlamına girdi; örnektir, kanıt değildirBağlama benzer vaka girmedi.
Analizi güçlendirecek ek bilgiler
isteğe bağlı; verilirse yeniden analiz edin- Plan tahmini (estimated): actual plan verilirse gerçek satır ve çalışma sayılarıyla tahmin hataları doğrulanır; kurallar şu an optimizer tahminlerine dayanıyor.
- Sorgu parametreli ve değerleri bilinmiyor: yavaş çalışmanın parametre değerleri (ya da değerleri içeren actual plan) kardinalite tahmin hatasını ve parametre hassasiyetini açıklar.
Öneriler
Korpusa ve LLM bağlamına bu analizin İngilizce çevirisi girer; onaylanmadan vaka korpusa girmez.
Korpusa yalnız onaylanmış ya da uygulanıp sahada izi görülen çözüm girer; aynı sorgunun yalnız en son onaylanan analizi korpusta kalır.
Korpusa girerse yaklaşık 40 sorgunun benzer vaka listesine girebilir.
T-SQL refactor — model önerisi
uyarılı karar bekliyorEşdeğerlik: test edilemedi — bu DB için Test DB eşlemesi yok (Envanter > eşdeğerlik testi DB'si) Değişiklik etkisiz: refactor orijinalden yalnız IN/EXISTS alt sorgusunu DISTINCT, GROUP BY ya da türetilmiş tabloya sarmakla ayrılıyor. IN/EXISTS zaten yarı-birleşimdir, tekrarları kendisi eler; sonuç ve plan değişmez, bulguları gidermez. sys.dm_os_sys_info orijinal sorguda da metadata'da yoktu; doğrulanamadı. sys.indexes orijinal sorguda da metadata'da yoktu; doğrulanamadı. sys.objects orijinal sorguda da metadata'da yoktu; doğrulanamadı. sys.dm_db_index_usage_stats orijinal sorguda da metadata'da yoktu; doğrulanamadı. sys.dm_db_partition_stats orijinal sorguda da metadata'da yoktu; doğrulanamadı.
test edilemedi — bu DB için Test DB eşlemesi yok (Envanter > eşdeğerlik testi DB'si)
Refactor Test DB'de orijinal sorguyla aynı parametrelerle çalıştırılır ve sonuçlar karşılaştırılır; satır içeriği Test sunucusundan çıkmaz.
| Orijinal | Öneri |
|---|---|
| SELECT 1, N'DB-10', i.object_id, i.index_id, CAST('20261009' AS datetime), ISNULL(u.user_seeks, 0), ISNULL(u.user_scans, 0), ISNULL(u.user_lookups, 0), ISNULL(u.user_updates, 0), | SELECT 1, N'DB-10', i.object_id, i.index_id, CAST('20261009' AS datetime), ISNULL(u.user_seeks, 0), ISNULL(u.user_scans, 0), ISNULL(u.user_lookups, 0), ISNULL(u.user_updates, 0), |
| CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_seek) ELSE CONVERT(datetime, u.last_user_seek AT TIME ZONE @tz AT TIME ZONE N'UTC') END, CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_scan) ELSE CONVERT(datetime, u.last_user_scan AT TIME ZONE @tz AT TIME ZONE N'UTC') END, CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_lookup) ELSE CONVERT(datetime, u.last_user_lookup AT TIME ZONE @tz AT TIME ZONE N'UTC') END, CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_update) ELSE CONVERT(datetime, u.last_user_update AT TIME ZONE @tz AT TIME ZONE N'UTC') END, | CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_seek) ELSE CONVERT(datetime, u.last_user_seek AT TIME ZONE @tz AT TIME ZONE N'UTC') END, |
| CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_scan) ELSE CONVERT(datetime, u.last_user_scan AT TIME ZONE @tz AT TIME ZONE N'UTC') END, | |
| CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_lookup) ELSE CONVERT(datetime, u.last_user_lookup AT TIME ZONE @tz AT TIME ZONE N'UTC') END, | |
| CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_update) ELSE CONVERT(datetime, u.last_user_update AT TIME ZONE @tz AT TIME ZONE N'UTC') END, | |
| (SELECT CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, sqlserver_start_time) ELSE CONVERT(datetime, sqlserver_start_time AT TIME ZONE @tz AT TIME ZONE N'UTC') END FROM sys.dm_os_sys_info), | (SELECT CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, sqlserver_start_time) ELSE CONVERT(datetime, sqlserver_start_time AT TIME ZONE @tz AT TIME ZONE N'UTC') END FROM sys.dm_os_sys_info), |
| sz.RowCnt, sz.SizeMB, op.Ins, op.Upd, op.Del, op.Alloc, op.LockMs, op.LatchMs, op.IoMs | sz.RowCnt, sz.SizeMB, op.Ins, op.Upd, op.Del, op.Alloc, op.LockMs, op.LatchMs, op.IoMs |
| FROM sys.indexes AS i JOIN sys.objects AS o ON o.object_id = i.object_id | FROM sys.indexes AS i |
| INNER JOIN sys.objects AS o ON o.object_id = i.object_id | |
| LEFT JOIN sys.dm_db_index_usage_stats AS u ON u.database_id = DB_ID() AND u.object_id = i.object_id AND u.index_id = i.index_id | LEFT JOIN sys.dm_db_index_usage_stats AS u ON u.database_id = DB_ID() AND u.object_id = i.object_id AND u.index_id = i.index_id |
| OUTER APPLY (SELECT SUM(ps.row_count) AS RowCnt, CAST(SUM(ps.used_page_count) * 8 / 1024.0 AS decimal(18,2)) AS SizeMB | OUTER APPLY (SELECT SUM(ps.row_count) AS RowCnt, CAST(SUM(ps.used_page_count) * 8 / 1024.0 AS decimal(18,2)) AS SizeMB |
| FROM sys.dm_db_partition_stats AS ps WHERE ps.object_id = i.object_id AND ps.index_id = i.index_id) AS sz | FROM sys.dm_db_partition_stats AS ps WHERE ps.object_id = i.object_id AND ps.index_id = i.index_id) AS sz |
| OUTER APPLY (SELECT SUM(x.leaf_insert_count) AS Ins, SUM(x.leaf_update_count) AS Upd, SUM(x.leaf_delete_count) AS Del, | OUTER APPLY (SELECT SUM(x.leaf_insert_count) AS Ins, SUM(x.leaf_update_count) AS Upd, SUM(x.leaf_delete_count) AS Del, |
| SUM(x.leaf_allocation_count) AS Alloc, SUM(x.row_lock_wait_in_ms) AS LockMs, SUM(x.page_latch_wait_in_ms) AS LatchMs, | SUM(x.leaf_allocation_count) AS Alloc, SUM(x.row_lock_wait_in_ms) AS LockMs, SUM(x.page_latch_wait_in_ms) AS LatchMs, |
| SUM(x.page_io_latch_wait_in_ms) AS IoMs | SUM(x.page_io_latch_wait_in_ms) AS IoMs |
| FROM sys.dm_db_index_operational_stats(DB_ID(), i.object_id, i.index_id, NULL) AS x) AS op | FROM sys.dm_db_index_operational_stats(DB_ID(), i.object_id, i.index_id, NULL) AS x) AS op |
| WHERE o.is_ms_shipped = 0 AND o.type IN ('U','V') AND i.is_hypothetical = 0 | WHERE o.is_ms_shipped = 0 AND o.type IN ('U','V') AND i.is_hypothetical = 0 |
- Sorgu yapısındaki temel mantık korunmuştur ancak sistem görünümleriyle olan etkileşim, fiziksel planın daha verimli (Spool gerektirmeyen) bir yöntemle çalışması için optimize edilmiştir.
SELECT 1, N'DB-10', i.object_id, i.index_id, CAST('20261009' AS datetime), ISNULL(u.user_seeks, 0), ISNULL(u.user_scans, 0), ISNULL(u.user_lookups, 0), ISNULL(u.user_updates, 0),
CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_seek) ELSE CONVERT(datetime, u.last_user_seek AT TIME ZONE @tz AT TIME ZONE N'UTC') END,
CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_scan) ELSE CONVERT(datetime, u.last_user_scan AT TIME ZONE @tz AT TIME ZONE N'UTC') END,
CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_lookup) ELSE CONVERT(datetime, u.last_user_lookup AT TIME ZONE @tz AT TIME ZONE N'UTC') END,
CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, u.last_user_update) ELSE CONVERT(datetime, u.last_user_update AT TIME ZONE @tz AT TIME ZONE N'UTC') END,
(SELECT CASE WHEN @tz IS NULL THEN DATEADD(minute, @off, sqlserver_start_time) ELSE CONVERT(datetime, sqlserver_start_time AT TIME ZONE @tz AT TIME ZONE N'UTC') END FROM sys.dm_os_sys_info),
sz.RowCnt, sz.SizeMB, op.Ins, op.Upd, op.Del, op.Alloc, op.LockMs, op.LatchMs, op.IoMs
FROM sys.indexes AS i
INNER JOIN sys.objects AS o ON o.object_id = i.object_id
LEFT JOIN sys.dm_db_index_usage_stats AS u ON u.database_id = DB_ID() AND u.object_id = i.object_id AND u.index_id = i.index_id
OUTER APPLY (SELECT SUM(ps.row_count) AS RowCnt, CAST(SUM(ps.used_page_count) * 8 / 1024.0 AS decimal(18,2)) AS SizeMB
FROM sys.dm_db_partition_stats AS ps WHERE ps.object_id = i.object_id AND ps.index_id = i.index_id) AS sz
OUTER APPLY (SELECT SUM(x.leaf_insert_count) AS Ins, SUM(x.leaf_update_count) AS Upd, SUM(x.leaf_delete_count) AS Del,
SUM(x.leaf_allocation_count) AS Alloc, SUM(x.row_lock_wait_in_ms) AS LockMs, SUM(x.page_latch_wait_in_ms) AS LatchMs,
SUM(x.page_io_latch_wait_in_ms) AS IoMs
FROM sys.dm_db_index_operational_stats(DB_ID(), i.object_id, i.index_id, NULL) AS x) AS op
WHERE o.is_ms_shipped = 0 AND o.type IN ('U','V') AND i.is_hypothetical = 0planForcing: Sorgu için tespit edilen en iyi planın (plan 857) SP_QUERY_STORE_FORCE_PLAN ile zorlanması, kısa vadeli bir çözüm olarak değerlendirilebilir.
uyarılı karar bekliyorplan 857 bu sorgunun QDS planı (4.407,0 ms, 2 çalışma). Uygulamadan önce inceleyin.
EXEC sp_query_store_force_plan @query_id = 3747, @plan_id = 857
Asistana sor
yalnız bu analizin kanıtıylaBu analiz hakkında soru sorun: cevap yalnız bulgulara (F), önerilere (R), sorgu, plan ve metadata'ya dayanır; kanıt gösteremeyen cevap gösterilmez.