Bu sorgu neden kümelenmemiş dizinimi kullanmıyor ve bunu nasıl yapabilirim?


12

Sorgu performansını artırmayla ilgili bu soruyu takip ederken , dizinimi varsayılan olarak kullanmanın bir yolu olup olmadığını bilmek istiyorum.

Bu sorgu yaklaşık 2,5 saniye içinde çalışır:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

Bu yaklaşık 33 ms içinde çalışır:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 
ORDER BY [DateEntered], [DeviceID];

[ID] alanında (pk) kümelenmiş bir dizin ve [DateEntered], [DeviceID] üzerinde kümelenmemiş bir dizin var. İlk sorgu kümelenmiş dizini, ikinci sorgu kümelenmemiş dizinimi kullanır. Sorum iki bölümden oluşuyor:

  • Neden her iki sorguda [DateEntered] alanında bir WHERE yan tümcesi bulunduğundan, sunucu ilkinde kümelenmiş dizini kullanıyor, ancak ikincisinde kullanmıyor?
  • Kümelenmemiş dizini, bu sorguda, orderby olmadan da varsayılan olarak nasıl kullanılabilir hale getirebilirim? (Ya da neden bu davranışı istemeyeyim?)

DateEntered bir DateTime, bu durumda tarih bölümünü kullanıyorum, ama bazen birlikte hem tarih hem de zaman sorgu.
Nate

Yanıtlar:


9

ilk sorgu, daha önce açıkladığım eşiğe dayalı olarak bir tablo tarar: Milyonlarca satır içeren dar bir tabloda sorgu performansını artırmak mümkün müdür?

(büyük olasılıkla TOP 1000yan tümcesiz sorgunuz 46k satırdan fazla veya 35k ila 46k arasında bir değer döndürür. (gri alan ;-))

ikinci sorgu sipariş edilmelidir. NC dizininiz istediğiniz sırada sıralandığından, en iyileştiricinin bu dizini kullanması daha ucuzdur ve daha sonra eksik sütunları kümelenmiş bir dizin taraması yapmakla karşılaştırılmış olarak almak için kümelenmiş dizine yer imi aramaları sipariş etmek için.

ORDER BYyan tümcesindeki sütunların sırasını tersine çevirirseniz NC INDEX'in işe yaramadığı için kümelenmiş bir dizin taramasına geri dönersiniz.

edit ikinci sorunuzun cevabını unuttum, neden bunu istemiyorsunuz

Kümelenmemiş, örtülmeyen bir dizin kullanılması, NC dizininde bir rowID'nin aranması ve daha sonra eksik sütunların kümelenmiş dizinde aranması gerektiği anlamına gelir (kümelenmiş dizin bir tablonun tüm sütunlarını içerir). Kümelenmiş dizindeki eksik sütunları aramak için ES'ler Rastgele ES'lerdir.

Buradaki anahtar RANDOM. çünkü NC dizininde bulunan her satır için erişim yöntemlerinin kümelenmiş dizinde yeni bir sayfaya bakması gerekir. Bu rastgele ve bu nedenle çok pahalı.

Şimdi, diğer taraftan, iyileştirici kümelenmiş bir dizin taramasına da gidebilir. Tarama aralıklarını aramak ve yalnızca büyük parçalar halinde Kümelenmiş dizini okumaya başlamak için ayırma haritalarını kullanabilir. Bu sıralı ve çok daha ucuz. (tablonuz parçalanmadığı sürece :-)) Dezavantajı, TÜM kümelenmiş dizinin okunması gerekir. Tamponunuz ve potansiyel olarak büyük miktarda ES'ler için bu kötüdür. ama yine de, ardışık ES'ler.

Durumunuzda, iyileştirici 35k ile 46k arasındaki satırlara karar verir, tam kümelenmiş bir dizin taraması için daha ucuzdur. Evet, yanlış. Ve seçici küme WHEREveya büyük tablo olmayan dar kümelenmemiş dizinleri olan birçok durumda, bu yanlış gidiyor. (Masanız daha kötü, çünkü aynı zamanda çok dar bir masa.)

Artık, ORDER BYkümelenmiş dizinin tamamını taramak ve ardından sonuçları sipariş etmek daha pahalı hale getirir. Bunun yerine, optimize edici, önceden sipariş edilen NC endeksini kullanmanın ve ardından yer imi aramaları için rastgele IO cezasını ödemenin daha ucuz olduğunu varsayar.

Yani sipariş tarafından çözüm mükemmel bir "sorgu ipucu" türüdür. AMA, belirli bir noktada, sorgu sonuçlarınız çok büyük olduğunda, yer işareti araması rastgele IO'larının cezası o kadar büyük olacaktır ki yavaşlar. Optimize edicinin planları bu noktadan önce kümelenmiş dizin taramasına değiştireceğini varsayıyorum, ancak hiçbir zaman kesin olarak bilemezsiniz.

Sizin durumunuzda, ekleriniz girilen tarih tarafından sipariş edildiği sürece, sohbet ve önceki soruda (bağlantıya bakın) tartışıldığı gibi, girilenDate sütununda kümelenmiş dizin oluşturmak daha iyidir.


20

Sorguyu farklı sözdizimi kullanarak ifade etmek, bazen kümelenmemiş bir dizin kullanma isteğinizi optimize ediciye iletmenize yardımcı olabilir. Aşağıdaki formu size istediğiniz planı verdiğini bulmalısınız:

SELECT
    [ID],
    [DeviceID],
    [IsPUp],
    [IsWebUp],
    [IsPingUp],
    [DateEntered]
FROM [dbo].[Heartbeats]
WHERE
    [ID] IN
(
    -- Keys
    SELECT TOP (1000)
        [ID]
    FROM [dbo].[Heartbeats]
    WHERE 
        [DateEntered] >= CONVERT(datetime, '2011-08-30', 121)
        AND [DateEntered]  < CONVERT(datetime, '2011-08-31', 121)
);

Sorgu Planı

Bu planı, kümelenmemiş dizin bir ipucu ile zorlandığında üretilen planla karşılaştırın:

SELECT TOP (1000) 
    * 
FROM [dbo].[Heartbeats] WITH (INDEX(CommonQueryIndex))
WHERE 
    [DateEntered] BETWEEN '2011-08-30' and '2011-08-31';

Zorunlu Endeks İpucu Planı

Planlar temelde aynıdır (Anahtar Arama, kümelenmiş dizindeki bir aramadan başka bir şey değildir). Her iki plan formu da kümelenmemiş dizinde yalnızca bir arama ve kümelenmiş dizine en fazla 1000 arama gerçekleştirir.

Önemli fark Üst operatör pozisyonundadır. İki arama arasına yerleştirilmiş olan Üst, optimize edicinin iki arama işlemini kümelenmiş dizinin mantıksal olarak eşdeğer taramasıyla değiştirmesini önler. Optimize edici, mantıksal bir planın bölümlerini eşdeğer ilişkisel işlemlerle değiştirerek çalışır. Üst, ilişkisel bir işleç değildir, bu nedenle yeniden yazma, kümelenmiş bir dizin taramasına dönüştürmeyi önler. Optimize edici, Üst operatörü yeniden konumlandırabilseydi, maliyet tahmininin çalışması nedeniyle taramayı arama + aramaya göre tercih ederdi.

Taramaların ve aramaların maliyeti

Çok yüksek bir seviyede, optimizasyonun taramalar ve aramalar için maliyet modeli oldukça basittir: 320 rasgele aramanın 1350 sayfayı bir taramada okumakla aynı olduğunu tahmin eder . Bu, muhtemelen herhangi bir modern I / O sisteminin donanım yeteneklerine çok az benzemektedir, ancak pratik bir model olarak oldukça iyi çalışır.

Model ayrıca, her sorgunun önbellekte zaten veri veya dizin sayfası olmadan başlayacağı varsayılan bir dizi basitleştirici varsayım yapar. Bunun anlamı, her G / Ç'nin fiziksel bir G / Ç ile sonuçlanacağıdır - ancak bu pratikte nadiren olur. Soğuk bir önbellekte bile, önceden getirme ve okuma öncesi, gerekli sayfaların aslında sorgu işlemcisinin ihtiyaç duyduğu anda bellekte olma olasılığı yüksektir.

Başka bir husus, bellekte olmayan bir satır için ilk isteğin tüm sayfanın diskten alınmasına neden olacağıdır. Aynı sayfadaki sonraki satır istekleri büyük olasılıkla fiziksel bir G / Ç'ye neden olmaz. Maliyetleme modeli, bunun gibi bazı etkileri hesaba katmak için mantık içerir, ancak mükemmel değildir.

Tüm bunlar (ve daha fazlası), optimize edicinin muhtemelen olması gerekenden daha erken bir taramaya geçme eğiliminde olduğu anlamına gelir. Rastgele G / Ç, fiziksel bir işlem ortaya çıkarsa 'sıralı' G / Ç'den sadece 'çok daha pahalıdır' - bellekteki sayfalara erişmek gerçekten çok hızlıdır. Fiziksel bir okuma gerektiğinde bile, tarama parçalanma nedeniyle sıralı okumalara yol açmayabilir ve aramalar örüntü esasen sıralı olacak şekilde sıralanabilir. Buna ek olarak modern I / O sistemlerinin (özellikle katı hal) değişen performans karakteristiği ve her şey çok titrek görünmeye başlar.

Satır Hedefleri

Bir Planda bir Üst operatörün bulunması, maliyetlendirme yaklaşımını değiştirir. Optimize edici, bir tarama kullanarak 1000 satır bulmanın büyük olasılıkla tüm kümelenmiş dizinin taranmasını gerektirmeyeceğini bilecek kadar akıllıdır - 1000 satır bulunur bulunmaz durabilir. Üst işleçte 1000 satırlık bir 'satır hedefi' belirler ve satır kaynağından kaç satır gerektiğini (bu durumda bir tarama) tahmin etmek için oradan geri dönmek için istatistiksel bilgileri kullanır. Bu hesaplamanın detaylarını burada yazdım .

Bu yanıttaki resimler SQL Sentry Plan Explorer kullanılarak oluşturuldu .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.