Büyük tablodaki birleştirmeyi optimize etme


10

~ 250 milyon kayıtları olan bir tabloya erişen bir sorgudan biraz daha fazla performans koaksiyelim. Gerçek (tahmin edilmeyen) yürütme planını okuduğumdan, ilk darboğaz şuna benzeyen bir sorgu:

select
    b.stuff,
    a.added,
    a.value
from
    dbo.hugetable a
    inner join
    #smalltable b on a.fk = b.pk
where
    a.added between @start and @end;

İlgili tabloların ve dizinlerin tanımları için aşağıya bakınız.

Yürütme planı, #smalltable üzerinde iç içe bir döngü kullanıldığını ve devasa tablo üzerinden dizin taramasının 480 kez yürütüldüğünü gösterir (#smalltable içindeki her satır için). Bu bana geriye doğru görünüyor, bu yüzden bunun yerine birleştirme birleşimini kullanmaya zorladım:

select
    b.stuff,
    a.added,
    a.value
from
    dbo.hugetable a with(index = ix_hugetable)
    inner merge join
    #smalltable b with(index(1)) on a.fk = b.pk
where
    a.added between @start and @end;

Söz konusu dizin (tam tanım için aşağıya bakın ), artan sırada fk (birleştirme yüklemi), eklenen (nerede yan tümcesinde kullanılır) & id (yararsız) sütunlarını kapsar ve değer içerir .

Ancak, bunu yaptığımda, sorgu 2 1/2 dakika 9 üzerinden patlar. Ben ipuçları her tablo üzerinde tek bir geçiş yapar, ancak açıkça değil, daha verimli bir birleşim zorlamak umuyordum.

Herhangi bir rehberlik bekliyoruz. Gerekirse ek bilgi sağlanır.

Güncelleme (2011/06/02)

Tablodaki endekslemeyi yeniden düzenledikten sonra, önemli performanslar elde ettim, ancak büyük tablodaki verileri özetlemek için yeni bir engele çarptım. Sonuç, şu anda aşağıdaki gibi görünen aylara göre bir özettir:

select
    b.stuff,
    datediff(month, 0, a.added),
    count(a.value),
    sum(case when a.value > 0 else 1 end) -- this triples the running time!
from
    dbo.hugetable a
    inner join
    #smalltable b on a.fk = b.pk
group by
    b.stuff,
    datediff(month, 0, a.added);

Şu anda, devasa dizinin kümelenmiş bir dizini pk_hugetable (added, fk)(birincil anahtar) ve kümelenmemiş bir dizini diğer yöne gidiyor ix_hugetable (fk, added).

Yukarıdaki 4. sütun olmadan, iyileştirici dış giriş olarak # küçük harf kullanarak ve iç döngü olarak kümelenmemiş bir dizin araması kullanarak (480 kez tekrar yürütülür) iç içe geçmiş bir döngü birleşimi kullanır. Beni ilgilendiren, tahmini satırlar (12.958.4) ile gerçek satırlar (74.668.468) arasındaki eşitsizliktir. Bu aramaların göreceli maliyeti% 45'tir. Ancak çalışma süresi bir dakikadan azdır.

4. sütun ile çalışma süresi 4 dakikaya yükselir. Bu sefer kümelenmiş endeksi (2 yürütme) aynı göreceli maliyet (% 45) için arar, bir karma eşleşmesi (% 30) yoluyla toplanır, sonra #smalltable (% 0) üzerinde bir karma birleştirme yapar.

Bir sonraki eylemimden emin değilim. Benim endişem, ne tarih aralığı araması ne de birleştirme tahmini garanti edilmez, hatta sonuç kümesini büyük ölçüde azaltabilecek olanların hepsi. Çoğu durumda tarih aralığı, kayıtların yalnızca% 10-15'ini keser ve fk'deki iç bağlantı belki% 20-30'u filtreleyebilir.


Will A tarafından talep edildiği gibi, sonuçları sp_spaceused:

name      | rows      | reserved    | data        | index_size  | unused
hugetable | 261774373 | 93552920 KB | 18373816 KB | 75167432 KB | 11672 KB

#smalltable şu şekilde tanımlanır:

create table #endpoints (
    pk uniqueidentifier primary key clustered,
    stuff varchar(6) null
);

İken dbo.hugetable olarak tanımlanır:

create table dbo.hugetable (
    id uniqueidentifier not null,
    fk uniqueidentifier not null,
    added datetime not null,
    value decimal(13, 3) not null,

    constraint pk_hugetable primary key clustered (
        fk asc,
        added asc,
        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];

Aşağıdaki dizin tanımlandığında:

create nonclustered index ix_hugetable on dbo.hugetable (
    fk asc, added asc, id asc
) include(value) 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];

İd alanı, ısrarla bir önceki DBA ait bir eserdir gereksiz tüm tabloları her yerde bir GUID, hiçbir istisna olmalıdır.


Sp_spaceused 'dbo.hugetable' sonucunu ekleyebilir misiniz, lütfen?
Will A

Bitti, tablo tanımlarının başlamasının hemen üstüne eklendi.
Hızlı Joe Smith

Kesinlikle öyle. Onun saçma boyutu buna bakmamın nedeni.
Hızlı Joe Smith

Yanıtlar:


5

Kişisel ix_hugetablegörünüyor oldukça yararsız çünkü:

  • o ise kümelenmiş dizin (PK)
  • Kümelenmiş bir dizin tüm anahtar olmayan sütunları DAHİL ETTİĞİ için hiçbir fark yaratmaz (en düşük yapraktaki anahtar olmayan değerler = INCLUDEd = kümelenmiş bir dizinin ne olduğu)

Buna ek olarak: - eklenen veya fk ilk olmalıdır - ID ilk = çok fazla kullanılmıyor

Kümelenmiş anahtarı (added, fk, id)bırakıp bırakmayı deneyin ix_hugetable. Zaten denedin (fk, added, id). Başka bir şey yoksa, çok fazla disk alanı ve dizin bakımı kazanırsınız

Başka bir seçenek, FORCE ORDER ipucunu tablo sırası boh yollarıyla denemek ve JOIN / INDEX ipuçlarını denemek olabilir. Optimize edici seçeneklerini kaldırdığınız için JOIN / INDEX ipuçlarını kişisel olarak kullanmamaya çalışıyorum. Yıllar önce bana (SQL Guru ile seminer), büyük bir masanız olduğunda GÜÇ SİPARİŞ ipucunun yardımcı olabileceği söylendi: YMMV 7 yıl sonra ...

Oh, ve DBA'nın nerede yaşadığını bize bildirin, böylece bazı perküsyon ayarlaması yapabiliriz

02 Haziran güncellemesinden sonra düzenleyin

Dördüncü sütun, kümelenmemiş dizinin bir parçası değildir, bu nedenle kümelenmiş dizini kullanır.

Kümelenmiş dizinin değer sütununa erişmek zorunda kalmaması için NC dizinini değer sütununu DAHİL olarak değiştirmeyi deneyin

create nonclustered index ix_hugetable on dbo.hugetable (
    fk asc, added asc
) include(value)

Not: Değer null değeri yoksa, COUNT(*)anlamsal olarak aynıdır . Ama TOPLA için varlığa değil , gerçek değere ihtiyacı vardır .

Eğer değiştirirseniz Örnek olarak, COUNT(value)hiç COUNT(DISTINCT value) olmadan endeksi değişen bir değer olarak değer işlemek zorundadır çünkü değil varlığı olarak tekrar sorgu ayrılmalıyız.

Sorgunun 3 sütuna ihtiyacı vardır: katma, fk, değer. İlk 2, anahtar sütunlar gibi filtrelenir / birleştirilir. değer sadece dahil böylece kullanılabilir. Kaplama indeksinin klasik kullanımı.


Hah, kümelenmiş ve kümelenmemiş dizinlerin fk ve farklı bir sırayla eklendiğini kafamda buldum. Bunu farketmediğime inanamıyorum, neredeyse ilk başta bu şekilde kurulduğuna inanamadığım kadar. Kümelenmiş endeksi yarın değiştireceğim, sonra yeniden inşa edilirken bir kahve içmek için caddeden aşağı gideceğim.
Hızlı Joe Smith

Dizin oluşturmayı değiştirdim ve büyük masadaki arama sayısını azaltmak için ama boşuna değil FORCE ORDER ile bir bash yaptım. Sorum güncellendi.
Hızlı Joe Smith

@Quick Joe Smith: cevabımı güncelledim
gbn

Evet, bunu kısa bir süre sonra denedim. Dizin yeniden oluşturma çok uzun sürdüğü için, unuttum ve başlangıçta tamamen alakasız bir şey yapmak için hızlandıracağımı düşündüm.
Hızlı Joe Smith

2

hugetableYalnızca addedsütunda bir dizin tanımlayın .

DB'ler, sütun listesinin yalnızca sağından en solda sayılan değerlere sahip olduğu için çok parçalı (çok sütunlu) bir dizin kullanır. Sorgunuz fk, ilk sorgunun nerede yan tümcesinde belirtilmediğinden, dizini yok sayar.


Yürütme planı gösterileri endeksi (ix_hugetable) yani edilir seeked ediliyor. Yoksa bu dizinin sorgu için uygun olmadığını mı söylüyorsunuz?
Hızlı Joe Smith

Dizin uygun değil. Kim bilir nasıl "endeksi kullanarak". Tecrübe bana bunun senin sorunun olduğunu söylüyor. Deneyin ve bize nasıl gittiğini söyleyin.
Bohemian

@Quick Joe Smith - @ Bohemian'ın önerisini denediniz mi? Sonuçlar nerede?
Lieven Keersmaekers

2
Kabul etmiyorum: ON deyimi önce mantıksal olarak işlenir ve etkili bir şekilde pratikte bir WHERE'dir, bu nedenle OP her iki sütunu da denemek zorundadır. Fk'de hiçbir dizin oluşturma yok = JOIN için fk değerini almak için kümelenmiş dizin taraması veya anahtar araması. Lütfen açıkladığınız davranışa bazı referanslar da ekleyebilir misiniz? Özellikle verilen SQL Server için, bu RDBMS için daha az geçmişe sahip yanıtınız var. Aslında, aI olarak geriye dönük olarak -1 bu yorumu yazın
gbn

2

Yürütme planı, #smalltable üzerinde iç içe bir döngü kullanıldığını ve devasa tablo üzerinden dizin taramasının 480 kez yürütüldüğünü gösterir (#smalltable içindeki her satır için).

Bu, bir döngü doğru seçim katılmak varsayalım, sorgu optimizer kullanmak için beklediğiniz sipariş. Alternatif olarak 250M kez döngü yapmak ve #temp tablosuna her seferinde bir arama yapmak - saat / gün sürebilir.

MERGE birleştirmesinde kullanılmaya zorladığınız dizin hemen hemen 250 milyon satır * her satırın büyüklüğüdür - küçük değil, en az birkaç GB. sp_spaceused'Birkaç GB' çıkışından yola çıkarak oldukça yetersiz olabilir - MERGE katılımı, çok G / Ç yoğun olacak endeks üzerinden trol olmanızı gerektirir.


Anladığım kadarıyla, 3 tür birleştirme algoritması vardır ve birleştirme birleştirmesi, her iki giriş de birleştirme yüklemesi tarafından sipariş edildiğinde en iyi performansa sahiptir. Doğru ya da yanlış, elde etmeye çalıştığım sonuç bu.
Hızlı Joe Smith

2
Ama bundan daha fazlası var. #Smalltable çok sayıda satıra sahipse, bir birleştirme birleşimi uygun olabilir. Adından da anlaşılacağı gibi, az sayıda satır varsa, bir döngü birleşimi doğru seçim olabilir. #Smalltable'ın bir veya iki satırı olduğunu ve diğer tablodaki bir avuç satırla eşleştiğini hayal edin - burada birleştirme birleştirmesini haklı çıkarmak zor olurdu.
Will A

Ben daha fazlası olduğunu düşündüm; Bunun ne olabileceğini bilmiyordum. Muhtemelen tahmin ettiğiniz gibi, veritabanı optimizasyonu benim güçlü bir takım değil.
Hızlı Joe Smith

@Quick Joe Smith - sp_spaceused için teşekkürler. 75GB dizin ve 18GB veri - ix_hugetable tablodaki tek dizin değil mi?
Will A

1
+1 İrade. Planlamacı şu anda doğru olanı yapıyor. Sorun, tablolarınızın kümelenme biçimi nedeniyle rastgele disk aramalarında yatıyor.
Denis de Bernardy

1

Dizininiz yanlış. Bkz. İndeksler dos ve donts .

İşler durduğunda, tek kullanışlı endeksiniz küçük tablonun birincil anahtarındadır. Bu yüzden tek makul plan, küçük masayı seq taramak ve karışıklığı büyük olanla birleştirmektir.

Üzerinde kümelenmiş bir dizin eklemeyi deneyin hugetable(added, fk). Bu, planlayıcının devasa tablodan geçerli satırları aramasını sağlamalı ve iç içe döngü veya birleştirme küçük tabloyla birleştirilmelidir.


Bu bağlantı için teşekkürler. Yarın işe geldiğimde bunu deneyeceğim.
Hızlı Joe Smith
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.