SQL Server 2012'de bir PK GUID'in indekslenmesi


13

Geliştiricilerim, tablolarını hemen hemen tüm tabloları için GUID'leri PK olarak kullanacak şekilde ayarladılar ve SQL Server varsayılan olarak bu PK'larda kümelenmiş dizini kurdu.

Sistem nispeten genç ve en büyük tablolarımız bir milyondan biraz fazla, ancak dizinlememize bir göz atıyoruz ve yakın gelecekte ihtiyaç duyulabileceği için hızlı bir şekilde ölçeklendirmek istiyoruz.

İlk eğim, kümelenmiş dizini, bir DateTime öğesinin bigint temsili olan oluşturulan alana taşımaktı. Ancak, ben CX benzersiz yapmak için tek yolu bu CX GUID sütun dahil olmak üzere, ancak önce oluşturulan tarafından olacaktır.

Bu, kümeleme anahtarını çok genişletir mi ve yazımların performansını artırır mı? Okumalar da önemlidir, ancak yazımlar muhtemelen bu noktada daha büyük bir endişe kaynağıdır.


1
GUID'ler nasıl oluşturulur? NEWID mi yoksa NEWSEQUENTIALID mi?
16'da swasheck

6
Kümelenmiş kılavuz ve insert performansı yalnızca "performans" ın hemen önündeki kelime en aza indirilmişse bir cümle içinde olmalıdır
billinkc

2
Bu geliştiricileri öğle yemeği için dışarı çıkarın ve onlara NEWID () 'i tekrar birincil anahtar olarak kullanırlarsa, üzerlerinde düşük performans göstereceğinizi açıklayın. Bunu önlemek için size ne yapacağınızı çok çabuk soracaklar. Bu noktada, bunun yerine KİMLİK (1,1) kullanın. (belki hafif bir aşırı basitleştirme, ancak 10'dan 9'u işe yarayacaktır).
Max Vernon

3
Rehberliğe olan nefretimizin nedeni, geniş (16 bayt) olmaları ve yaratılmadığı zaman newsequentialidrastgele olmalarıdır. Kümelenmiş anahtarlar, dar ve arttıklarında en iyisidir. Bir GUID tam tersidir: yağ ve rastgele. Neredeyse kitaplarla dolu bir kitaplık düşünün. OED geliyor ve kılavuzların rastgele olması nedeniyle, rafın ortasına yerleştiriliyor. Bir şeyleri düzenli tutmak için, kitapların sağ yarısında zaman yoğun bir görev olan yeni bir yere girmek gerekiyor. GUID'in veritabanınıza yaptığı ve performansınızı öldürdüğü şey budur.
billinkc

7
Benzersiz tanımlayıcıları kullanma sorununu çözmenin yolu, çizim tahtasına geri dönüp benzersiz tanımlayıcıları kullanmamaktır . Sistem küçükse korkunç değiller , ancak en az birkaç milyon + satır tablonuz (veya bundan daha büyük herhangi bir tablo) varsa, anahtarlar için benzersiz tanımlayıcılar kullanarak ezilmeye başlayacaksınız.
Jon Seigel

Yanıtlar:


20

GUID'lerle ilgili birincil sorunlar, özellikle sıralı olmayanlar:

  • Anahtarın boyutu (INT için 16 bayt ve 4 bayt): Bu, kümelenmiş dizininizse, anahtarınızdaki veri miktarının 4 katını ve diğer dizinler için bu ek alanı depoladığınız anlamına gelir.
  • Dizin parçalanması: Anahtar değerlerin tamamen rasgele doğası nedeniyle sıralı olmayan bir GUID sütununu birleştirmek neredeyse imkansızdır.

Peki bu durumunuz için ne anlama geliyor? Tasarımınıza geliyor. Sisteminiz sadece yazma işlemiyle ilgiliyse ve veri alma konusunda hiçbir endişeniz yoksa, Thomas K tarafından özetlenen yaklaşım doğrudur. Bununla birlikte, bu stratejiyi uygulayarak, bu verileri okumak ve depolamak için birçok potansiyel sorun yarattığınızı unutmayın. Jon Seigel'in belirttiği gibi , daha fazla yer kaplayacak ve aslında bellek şişkinliğine sahip olacaksınız.

GUID'lerle ilgili asıl soru bunların ne kadar gerekli olduğudur. Geliştiriciler, küresel benzersizliği sağladıkları için onlardan hoşlanıyorlar, ancak bu tür bir benzersizliğin gerekli olduğu nadir bir durum. Ancak, maksimum değer sayınız 2,147,483,647'den (4 bayt işaretli tamsayının maksimum değeri) azsa, muhtemelen anahtarınız için uygun veri türünü kullanmamanız gerektiğini düşünün. BIGINT (8 bayt) kullanarak bile, maksimum değeriniz 9,223,372,036,854,775,807'dir. Benzersiz bir anahtar için otomatik olarak artan bir değere ihtiyacınız varsa, bu genellikle global olmayan herhangi bir veritabanı (ve birçok global veritabanı) için yeterlidir.

Son olarak, bir kümeyi kümelenmiş bir dizine karşı kullandığım sürece, yalnızca veri yazıyorsanız, ekler için ek yükü en aza indirdiğiniz için bir yığın en verimli olacaktır. Ancak, SQL Server yığınları veri almak için son derece verimsiz. Benim deneyimim, bir ilan etme şansınız varsa, kümelenmiş bir dizinin her zaman arzu edilir olmasıdır. Bir tabloya kümelenmiş bir dizin eklenmesinin (4 milyar + kayıt) genel seçim performansını 6 kat artırdığını gördüm.

Ek bilgi:


13

Bir OLTP sisteminde anahtarlar ve kümeler olarak GUID ile ilgili yanlış bir şey yoktur (tabloda kümenin artan boyutundan muzdarip çok sayıda dizin yoksa). Aslında, IDENTITY sütunlarından çok daha ölçeklenebilir.

GUID'in SQL Server'da büyük bir sorun olduğuna dair yaygın bir inanç var - büyük ölçüde, bu oldukça basit bir şekilde yanlış. Nitekim, GUID yaklaşık 8'den fazla çekirdeğe sahip kutularda önemli ölçüde daha ölçeklenebilir olabilir:

Üzgünüm, ama geliştiricileriniz haklı. GUID hakkında endişelenmeden önce diğer şeyler için endişelen.

Nihayet: neden ilk önce bir küme indeksi istiyorsunuz? Endişeniz çok sayıda küçük dizine sahip bir OLTP sistemiyse, bir yığınla daha iyi durumda olursunuz.

Şimdi, parçalanmanın (GUID'in sunacağı) okumalarınıza ne yaptığını ele alalım. Parçalanma ile ilgili üç önemli sorun vardır:

  1. Sayfa maliyet diski G / Ç'yi böler
  2. Tam sayfaların yarısı tam sayfalar kadar bellek tasarruflu değildir
  3. Sayfaların sıra dışı depolanmasına neden olur, bu da sıralı G / Ç'yi daha az olası kılar

Sorunuzdaki endişeniz "Daha fazla donanım eklemek sistemi daha hızlı hale getiriyor" olarak tanımlayabileceğimiz ölçeklenebilirlikle ilgili olduğundan, bunlar sorunlarınızın en azıdır. Her birini sırayla ele almak için

Reklam 1) Ölçek istiyorsanız, G / Ç satın almaya gücünüz vardır. Ucuz bir Samsung / Intel 512GB SSD bile (birkaç USD / GB) 100K IOPS'un üzerine çıkacaktır. Yakında 2 soketli bir sistemde bunu tüketmeyeceksiniz. Ve eğer buna girerseniz, bir tane daha satın alın ve hazırsınız

Reklam 2) Tablonuzda silme işlemi yaparsanız, yine de yarım dolu sayfanız olur. Ve olmasanız bile, bellek ucuz ve en büyük OLTP sistemleri hariç herkes için - sıcak veriler oraya sığmalıdır. Ölçek ararken, sayfalara daha fazla veri toplamak alt optimizasyon sağlar.

Reklam 3) Sık sayfa bölünmüş, yüksek oranda parçalanmış verilerden oluşan bir tablo, sıralı olarak doldurulmuş tablolarla tam olarak aynı hızda rastgele G / Ç yapar

Birleşmeyle ilgili olarak, OLTP'de iş yükü gibi görebileceğiniz iki ana birleştirme türü vardır: Karma ve döngü. Her birine sırayla bakalım:

Karma birleştirme: Karma birleştirme, küçük tablonun tarandığını ve daha büyük olanın genellikle arandığını varsayar. Küçük tabloların bellekte olması çok olasıdır, bu nedenle burada G / Ç endişeniz değildir. Parçalanmış endekste parçalanmayan bir endekste aynı maliyetin bulunmasına zaten değindik

Döngü birleştirmesi: Dış tablo aranacaktır. Aynı maliyet

Ayrıca çok sayıda kötü tablo taraması da devam ediyor olabilir - ancak GUID yine endişeniz değil, uygun indeksleme.

Şimdi, bazı yasal aralık taramalarınız devam edebilir (özellikle yabancı anahtarlara katılırken) ve bu durumda, parçalanmış veriler parçalanmamış verilere kıyasla daha az "paketlenir". Ancak, iyi bir şekilde dizine eklenmiş bir 3NF verilerinde hangi birleştirmelerin göreceğinizi düşünelim:

  1. Başvurduğu tablonun birincil anahtarına yabancı anahtar başvurusu olan bir tablodan birleştirme

  2. Diğer taraftan

Reklam 1) Bu durumda, birincil anahtara tek bir arama yapacaksınız - n'yi 1'e katıyorsunuz. Parçalanma veya değil, aynı maliyet (bir arama)

Reklam 2) Bu durumda, aynı anahtara katılıyorsunuz, ancak birden fazla satır alabilir (aralık arama). Bu durumda birleştirme 1'den n'ye kadardır. Bununla birlikte, aradığınız yabancı tablo, parçalanmış bir dizinde parçalanmış olmayan bir dizinde aynı sayfada olması muhtemel olan AYNI anahtarını arıyorsunuz.

Bir an için bu yabancı anahtarları düşünün. Birincil anahtarlarımızı "mükemmel" bir şekilde sıralamış olsanız bile - bu anahtara işaret eden herhangi bir şey yine de sıralı olmayacaktır.

Tabii ki, bazı bankalarda para konusunda ucuz ve işlemde yüksek olan bir sanal makinede çalışıyor olabilirsiniz. Sonra tüm bu tavsiye kaybolacak. Ancak bu sizin dünyanızsa, ölçeklenebilirlik muhtemelen aradığınız şey değildir - her ikisi de farklı şeyler olan performans ve yüksek hız / maliyet arıyorsunuz.


1
Yorumlar uzun tartışmalar için değildir; bu görüşme sohbete taşındı .
Paul White 9

5

Thomas: Bazı noktalarınız tam mantıklı ve hepsine katılıyorum. SSD'lerde iseniz, optimize ettiğiniz şeyin dengesi değişir. Rasgele ve ardışık eğirme diski ile aynı tartışma değildir.

Özellikle saf bir DB görünüm almanın korkunç yanlış olduğunu kabul ediyorum. Yalnızca DB performansını artırmak için uygulamanızı yavaş ve ölçeklendirilemez hale getirmek oldukça yanlış yönlendirilebilir.

IDENTITY (veya sekans veya DB'de üretilen herhangi bir şey ) ile ilgili en büyük sorun, bir anahtar oluşturmak için DB'ye gidiş dönüş gerektirdiği için korkunç derecede yavaş olması ve DB'nizde otomatik olarak bir darboğaz oluşturması, uygulamaların bir anahtarı kullanmaya başlamak için bir DB çağrısı yapın. GUID oluşturmak bunu anahtarı oluşturmak için uygulamayı kullanarak çözer, global olarak benzersiz (tanım gereği) olması garanti edilir ve böylece uygulama katmanları, bir DB gidiş dönüşü gerçekleştirmeden ÖNCE kaydı iletmek için kullanabilir.

Ancak GUID'lere bir alternatif kullanma eğilimindeyim Burada bir veri türü için kişisel tercihim, uygulama tarafından oluşturulan global olarak benzersiz bir BIGINT. Bunu nasıl yapabiliriz? En önemsiz örnekte, bir GUID oluşturmak için uygulamanıza küçük, ÇOK hafif bir işlev eklersiniz. Karma işlevinizin hızlı ve nispeten hızlı olduğunu varsayarsak (bir örnek için Google'dan CityHash'a bakın: http://google-opensource.blogspot.in/2011/04/introducing-cityhash.html - tüm derleme adımlarını doğru yaptığınızdan emin olun, veya basit kod için http://tools.ietf.org/html/draft-eastlake-fnv-03 FNV1a varyantı ) bu, hem uygulama tarafından oluşturulan benzersiz tanımlayıcılardan hem de CPU'ların daha iyi çalıştığı 64 bit anahtar değerinden yararlanır .

BIGINTs üretmenin başka yolları da vardır ve her iki bu algosta da karma çarpışma şansı vardır - okuma ve bilinçli kararlar alma.


2
Yanıtınızı OP'nin sorusuna bir cevap olarak düzenlemenizi ve (şu an olduğu gibi) Thomas'ın cevabına bir cevap olarak düzenlememenizi öneriyorum. Yine de Thomas (, MikeFal) ile öneriniz arasındaki farkları vurgulayabilirsiniz.
ypercubeᵀᴹ

2
Lütfen soruya verdiğiniz cevabı belirtin. Eğer yapmazsan senin için kaldıracağız.
JNK

2
Yorumlar için teşekkürler Mark. Cevabınızı düzenlediğinizde (ki çok iyi bir bağlam sağladığını düşünüyorum) bir şeyi değiştirirdim: KİMLİK, INSERT'e dikkat ederseniz sunucuya ek bir gidiş dönüş gerektirmez. Her zaman
INSERT'i

1
"Bir anahtar oluşturmak için DB gidiş-dönüş bir yolculuk gerektiriyor gibi korkunç yavaş" ile ilgili - bir gidiş-dönüş gezisinde ihtiyacınız olduğu kadar kapmak olabilir.
AK

"Tek bir seferde ihtiyacınız olduğu kadar kapabilirsiniz" - Bunu IDENTITY sütunları veya temelde veritabanı düzeyinde DEFAULT kullandığınız başka bir yöntemle yapamazsınız.
Avi Cherry
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.