Milyonlarca satır içeren dar bir tabloda sorgu performansını artırmak mümkün müdür?


14

Şu anda tamamlamak için ortalama 2500 ms süren bir sorgu var. Masam çok dar, ama 44 milyon sıra var. Performansı artırmak için hangi seçeneklere ihtiyacım var veya bu mümkün olduğunca iyi mi?

Sorgu

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

Tablo

CREATE TABLE [dbo].[Heartbeats](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DeviceID] [int] NOT NULL,
    [IsPUp] [bit] NOT NULL,
    [IsWebUp] [bit] NOT NULL,
    [IsPingUp] [bit] NOT NULL,
    [DateEntered] [datetime] NOT NULL,
 CONSTRAINT [PK_Heartbeats] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

İçerik

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Ek dizinler eklemek yardımcı olur mu? Eğer öyleyse, neye benziyorlardı? Mevcut performans kabul edilebilir, çünkü sorgu sadece ara sıra çalışıyor, ama bir öğrenme alıştırması olarak merak ediyorum, bunu daha hızlı yapmak için yapabileceğim bir şey var mı?

GÜNCELLEME

Bir kuvvet dizini ipucu kullanmak için sorguyu değiştirdiğimde, sorgu 50ms yürütür:

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

Doğru seçili bir DeviceID yantümcesi eklemek de 50ms aralığına vurur:

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

ORDER BY [DateEntered], [DeviceID]Orijinal sorguyu eklerseniz , ben 50ms aralığındayım:

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

Bunların hepsi beklediğim dizini (CommonQueryIndex) kullanıyor, bu yüzden sorum şu an, bu dizini böyle sorgularda kullanılmaya zorlamanın bir yolu var mı? Yoksa masamın boyutu optimize ediciyi çok fazla atıyor mu ve sadece bir ORDER BYveya ipucu kullanmalıyım?


Sanırım performansı bir ölçüde artıracak bir "DateEntered" üzerinde kümelenmemiş bir dizin daha ekleyebilirsiniz
Praveen

@Praveen Temelde mevcut dizinimle aynı mı olurdu? Aynı alanda iki dizin olacağından özel bir şey yapmam gerekir mi?
Nate

@Doğa, tabloya kalp atışı denildiğinden ve 44 milyon kayıt bulunduğundan, bu masada ağır ekler olduğunu varsayıyorum? Dizin oluşturma ile, hızlandırmak için yalnızca bir kaplama dizini ekleyebilirsiniz. Ancak belirttiğiniz gibi bu sorguyu yalnızca ara sıra kullandığınızda, ağır uçlar yaparsanız kesinlikle buna karşı çıkmanızı şiddetle tavsiye ederim. Temelde kesici uç yükünüzü iki katına çıkarır. Kurumsal sürümde mi çalışıyorsunuz?
Edward Dortland

NC dizininizde deviceID olduğunu fark ettim. Bunu nerede cümlenize dahil etmek mümkün mü? Ve bu, eşik değerin altında ayarlanan sonucu düşürür mü? <35k kayıt (en iyi 1000 yan tümcesi olmadan).
Edward Dortland

1
son soru, Her zaman dateEntered sırasına göre ekliyor musunuz? Veya aygıtlar birbirinden zaman uyumsuzluk ekleyebileceğinden, bunlar düzensiz olabilir. Kümelenmiş dizini DateEntered sütununa değiştirmeyi deneyebilirsiniz. Kümelenmiş dizininizin izin sayfalarınız artık 445 sayfa. Eğer int'ten datetime'a giderseniz bu iki katına çıkar. Ancak bu durumda, bu kötü olmayabilir.
Edward Dortland

Yanıtlar:


13

Optimize edici neden ilk dizininiz için geçerli değil:

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

[DateEntered] Sütununun seçiciliği ile ilgilidir.

Bize masanızda 44 milyon satır olduğunu söylediniz. satır boyutu:

Kimlik için 4 bayt, Aygıt Kimliği için 4 bayt, tarih için 8 bayt ve 4 bit sütunlar için 1 bayt. 17 bayt + 7 bayt ek yükü (etiketler, Null bitmap, var col offset ,, col count) satır başına toplam 24 Byte.

Bu kabaca 140 bin sayfaya çevrilirdi. Bu 44 milyon satırı saklamak için.

Şimdi iyileştirici iki şey yapabilir:

  1. Tabloyu tarayabilir (kümelenmiş dizin taraması)
  2. Veya endeksinizi kullanabilir. Dizininizdeki her satır için, kümelenmiş dizinde bir yer işareti araması yapılması gerekir.

Belirli bir noktada, kümelenmemiş dizininizde bulunan her dizin girdisi için kümelenmiş dizindeki tüm bu tekil aramaları yapmak daha pahalı hale gelir. Bunun eşiği genellikle toplam arama sayısı toplam tablo sayfası sayısının% 25 ila% 33'ünü aşmalıdır.

Bu durumda: 140k /% 25 = 35000 satır 140k /% 33 = 46666 satır.

(@RBarryYoung, 35k toplam satırların% 0,08'i ve 46666% 0,10'dur, bu yüzden karışıklığın olduğu yer budur)

Bu nedenle, nerede yan tümceniz 35000 ve 46666 satır arasında bir yere neden olacaksa. (Bu, üst tümcenin altındadır!) Kümelenmemiş kümenizin kullanılmaması ve kümelenmiş dizin taramasının kullanılması büyük olasılıktır.

Bunu değiştirmenin sadece iki yolu:

  1. Nerede olduğunuz maddeyi daha seçici hale getirin. (Eğer mümkünse)
  2. * İşaretini bırakın ve bir kaplama dizini kullanabilmek için yalnızca birkaç sütun seçin.

Şimdi bir select * kullansanız bile bir kaplama dizini oluşturabileceğinizden emin olun. Ekleme / güncelleme / silme işlemleriniz için büyük bir ek yük oluşturan hoever. Bunun en iyi çözüm olup olmadığından emin olmak için iş yükünüz hakkında daha fazla bilgi sahibi olmalıyız (okuma ve yazma).

Tarih saatinden küçük tarih saatine geçiş, kümelenmiş dizinde boyut olarak% 16, kümelenmemiş dizininizde boyut olarak% 24 azalmadır.


tarama eşiği normalde bundan çok daha düşüktür (% 10 veya daha düşük), ancak aralık bir yıl öncesinden tek bir gün olduğundan, bu eşiği bile yapmamalıdır. Ve bir örtme dizini eklendiğinden, Kümelenmiş bir Dizin Taraması verilmez. Bu indeks WHERE yan tümcesini SARG yapabildiğinden, tercih edilmelidir.
RBarryYoung

@RBarryYoung [EnteredDate], [DeviceID] üzerindeki kümelenmemiş dizinin neden kullanılmadığını açıklamaya çalışıyordum. İkimiz de aynı fikirde olduğumuzu düşünüyorum, sadece sayfa açısından konuşuyorum. Daha açık hale getirmek için cevabımı değiştireceğim.
Edward Dortland

Yanıtladığımı daha net hale getirmek için cevabı değiştirdim. @RBarryYoung'un önerdiği kaplama dizininin neden kullanılmadığını açıklayamıyorum. Sadece bir milyon satırda test ettim ve kaplama endeksini kullanarak optimize edici.
Edward Dortland

Çok kapsamlı bir yanıt için teşekkürler, çok mantıklı. İş yüküne ilişkin olarak, tabloda 5 dakikalık periyot için 150-300 kesici uç ve raporlama amacıyla günde birkaç okuma bulunur.
Nate

Kaplama endeksinin genel başlığı, dar bir tablo olduğu ve "kaplama" nın, satırın çoğunu zaten içeren önceden var olan dizine yalnızca bir ek olduğu göz önüne alındığında gerçekten önemli değildir.
RBarryYoung

8

PK'nizin kümelenmesinin özel bir nedeni var mı? Birçok insan bunu yapar, çünkü varsayılan olarak budur veya PK'lerin kümelenmesi gerektiğini düşünürler. Hayır. Kümelenmiş dizinler genellikle aralık sorguları için (bunun gibi) veya bir alt tablonun yabancı anahtarında en iyisidir.

Kümeleme dizininin bir etkisi, veriler küme b ağacının yaprak düğümlerinde depolandığından, tüm verileri bir araya getirmesidir. Bu nedenle, bir aralığın 'çok geniş' olmasını istemediğinizi varsayarsak, optimizer b ağacının hangi bölümünün verileri içerdiğini tam olarak bilecek ve bir satır tanımlayıcısı bulmak ve ardından verilerin nereye atlaması gerekmeyecek (bir NC indeksi ile uğraşırken yaptığı gibi). Bir aralığın 'çok geniş' değeri nedir? Saçma bir örnek, sadece bir yıllık kayda sahip bir tablodan 11 ay veri isteyebilir. İstatistiklerinizin güncel olduğunu varsayarsak, bir günlük veri çekmek sorun olmamalı. (Yine de, dünün verilerini arıyorsanız ve istatistikleri üç gündür güncellemediyseniz, optimize edici sorun yaşayabilir.)

Bir "SELECT *" sorgusu çalıştırdığınız için, motorun tablodaki tüm sütunları (birisinin uygulamanızın o anda ihtiyaç duymadığı yeni bir tane eklese bile) döndürmesi gerekir, böylece bir kaplama dizini veya bir dizin dahil sütunlarla, hiç yardımcı olmaz. (Bir dizindeki tablodaki her sütunu dahil ediyorsanız, yanlış bir şey yapıyorsunuzdur.) Optimize edici muhtemelen bu NC dizinlerini yok sayar.

Peki ne yapmalı?

Benim önerim NC dizinini bırakmak, kümelenmiş PK'yi kümelenmemiş olarak değiştirmek ve [DateEntered] üzerinde kümelenmiş bir dizin oluşturmak olacaktır. Aksi ispatlanana kadar daha basittir.


Satırların artan sırayla eklendiğini varsayarsak, bu en basit cevaptır - ancak doğrusal olmayan sırayla eklemek parçalanmaya neden olur.
Kirk Broadhurst

Herhangi bir b-ağacı yapısına veri eklenmesi yapının dengesini kaybetmesine neden olur. Küme sırasında satır ekleseniz bile, dizinler dengeyi kaybeder. Tabloları yeniden endeksleme, parçalamayı kaldırır ve herhangi bir DBA, tabloya "yeterli" veri eklendikten sonra tabloların yeniden endekslenmesi gerektiğini söyler. ("Yeterli" tanımı tartışılabilir veya "ne zaman" tartışma olabilir.) Soruda, yeniden endekslemenin bir nedenle yapılamayacağını söyleyen hiçbir şey göremiyorum.
darin boğazı

4

Orada o "*" var sürece, o zaman çok fazla fark olacağını hayal edebiliyorum tek şey dizin tanımınızı değiştirmek olacaktır:

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)INCLUDE (ID, IsWebUp, IsPingUp, IsPUp)
 WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Yorumlarda belirtildiği gibi, bu dizini kullanmalıdır, ancak değilse bir ORDER BY veya bir dizin ipucu ile ikna edebilirsiniz.


Ben sadece denedim ve ben hala hemen hemen aynı yerde, 2500ms sunucu yanıtı ve 10ms istemci işlem süresi bekleyin.
Nate

Sorgu planını kaydedin.
RBarryYoung

Kümelenmiş Dizin kullanıyor gibi görünüyor. (SEÇ Maliyet: 0% <- Üst Maliyet: 20% <- Kümelenmiş Endeks Tarama PK_Heartbeats Maliyet: 80%)
Nate

Evet, bu doğru değil, bazı şeyler istatistikleri / optimize ediciyi fırlatıyor. Yeni dizini kullanmaya zorlamak için bir ipucu ekleyin.
RBarryYoung

@Max Vernon: Belki, ama bu sorgu planında işaretlenmiş olmalıydı.
RBarryYoung

3

Buna biraz farklı bakardım.

  • Evet, eski bir konu olduğunu biliyorum ama ilgimi çekti.

Tarih saat sütununu dökümü - int olarak değiştiririm. Bir arama tablonuz olsun veya tarihiniz için bir dönüşüm yapın.

Kümelenmiş dizini dökümü - yığın olarak bırakın ve tarihi temsil eden yeni INT sütununda kümelenmemiş bir dizin oluşturun. yani bugün 20121015 olacaktır. Bu düzen önemlidir. Tabloyu ne sıklıkta yüklediğinize bağlı olarak, bu dizini DESC sırasında oluşturmaya bakın. Bakım maliyeti daha yüksek olacak ve bir doldurma faktörü veya bölümleme getirmek isteyeceksiniz. Bölümleme, çalışma sürenizi azaltmanıza da yardımcı olur.

Son olarak, SQL 2012 kullanabiliyorsanız, SEQUENCE kullanmayı deneyin - ekler için kimlikten () daha iyi performans gösterir.


İlginç bir çözüm. Sorumdan açık olmasa da, DateTime'ın zaman kısmı çok önemlidir. Genellikle o dönemdeki belirli zamanları gözden geçirmek için tarihe göre sorgulama yaparım. Bu çözümü bunu hesaba katmak için nasıl ayarlarsınız?
Nate

Bu durumda, tarih / saat sütununu saklayın, tarih için int sütununu ekleyin (aralığınız saat öğesini değil, tarih öğesini temel aldığından). Ayrıca, TIME veri türünü kullanmayı düşünebilir ve ardından tarihi etkili bir şekilde ayırabilirsiniz. Bu şekilde, veri ayak iziniz daha küçüktür ve yine de sütunun Zaman öğesine sahipsiniz.
Jeremy Lowell

1
Neden daha önce özledim emin değilim ama kümelenmiş dizin ve kümelenmemiş dizin de satır sıkıştırma kullanın. Masanızla hızlı bir test yaptım ve bulduğum şey: Yukarıda tanımlanan tabloda bir dizi veri (5.8 milyon satır) oluşturdum. Kümelenmiş ve kümelenmemiş dizini sıkıştırdım (sıraladım). tam sorgunuza dayalı olarak mantıksal okumalar 2,074'ten 1,433'e düştü. Bu önemli bir düşüş ve tek başına size yardımcı olacağından eminim - ve çok düşük risk.
Jeremy Lowell
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.