Bir GUID'i Birincil Anahtar Olarak Kullanma


32

Genelde veritabanlarında Birincil Anahtar olarak otomatik artış kimliklerini kullanırım. GUID kullanmanın yararlarını öğrenmeye çalışıyorum. Bu makaleyi okudum: https://betterexplained.com/articles/the-quick-guide-to-guids/

Bu GUID'lerin uygulama seviyesindeki nesneleri tanımlamak için kullanıldığını biliyorum. Ayrıca veritabanı düzeyinde birincil anahtar olarak depolanıyorlar. Örneğin, şu sınıfa sahip olduğumu söyle:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

Bellekte yeni bir kişi oluşturmak istediğimi ve ardından kişiyi veritabanına eklediğimi söyleyin. Bunu sadece yapabilir miyim:

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Diyelim ki Birincil Anahtar olarak bir GUID içeren milyonlarca ve milyonlarca satır içeren bir veritabanım vardı. Bu her zaman benzersiz olacak mı? GUID'leri doğru bir şekilde anlıyor muyum?

Bu makaleyi daha önce okudum: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . GUID'ler ve tamsayılar arasında Birincil Anahtar olarak mutlu bir ortam önerdiği göründüğü için beni biraz şaşırtıyor.

Düzenle 11/06/18

Kılavuzlarımın gereksinimlerime uygunluktan daha uygun olduğuna inanmaya başladım. Bugünlerde CQRS kullanıyorum ve GUID'ler daha iyi bir şekilde uyuyor.

Bazı geliştiriciler burada etki alanı modeli eg dizeleri olarak GUID'lerini modellemek bildirisini yapın: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs - bu durumda: IdentityGuid bir dize olarak modellenmiş bir GUID'dir. Bunu burada belirtilenler dışında yapmak için herhangi bir sebep var mı: Dağıtılmış bir sistemde bir varlık tanımlayıcısı olarak bir özel değer nesnesi ya da bir Kılavuz kullanın? . GUID'i bir dize olarak modellemek "normal" mi, yoksa model ve veritabanında GUID olarak mı modellemeliyim?



7
Benzersiz olacağı garanti edilmez, ancak bir çarpışma görmeniz pek mümkün değildir. stackoverflow.com/questions/1155008/how-unique-is-uuid/…
icirellik

2
ayrıca bakınız: UUID çarpışmaları
gnat

2
Ayrıca bkz. Dba.stackexchange.com/questions/54690/… ve diğer birçok soru - bu konuyu sıkça sordular, cevaplandılar ve tartışıldılar.
Greenstone Walker,

1
Şu an üzerinde çalıştığım sistem UUID kullanıyor. Güzel bir özellik, bir ID'nin, o tablodaki bir kaydı tanımlayan sıralı bir ID'nin aksine bir kaydı benzersiz olarak tanımlamasıdır.
Justin,

Yanıtlar:


41

GUID'ler tanımı gereği "Globally Unique IDentifiers" dır. Java'da UUID'ler "Evrensel Benzersiz Kimlik Belirleyiciler" olarak adlandırılan benzer ancak biraz farklı bir kavram var. İsimler tüm pratik kullanım için değiştirilebilir.

GUID'ler, Microsoft'un veritabanı kümelemesinin nasıl çalışacağını öngörmekte merkezi bir konumdadır ve bazen bağlı kaynaklardan veri eklemeniz gerekirse, veri çarpışmalarını önlemeye gerçekten yardımcı olurlar.

Bazı Pro-GUID Gerçekleri:

  • GUID'ler kilit çakışmaları önler
  • GUID'ler ağlar, makineler vb. Arasında veri birleştirmeye yardımcı olur.
  • SQL Server, dizin parçalanmasını en aza indirmeye yardımcı olmak için yarı sıralı GUIDS desteğine sahiptir ( ref , bazı uyarılar)

GUID'lerle Bazı Çirkinlikler

  • Büyük, her biri 16 bayt
  • Sıra dışı oldukları için kimliğe göre sıralama yapamazsınız ve otomatik artırma kimliklerinde olduğu gibi ekleme siparişini almayı umarsınız.
  • Özellikle küçük veri kümelerinde (tablolara bakmak gibi) çalışmak daha zahmetlidir.
  • Yeni GUID uygulaması SQL Server'da C # kütüphanesinde olduğundan daha sağlamdır (sıralı GUID'leri SQL Server'dan alabilirsiniz, C # ise rastgeledir)

GUID'ler indekslerinizi daha büyük hale getireceğinden, bir sütunu indekslemenin disk alanı maliyeti daha yüksek olacaktır. Rasgele GUID'ler, dizinlerinizi parçalayacaktır.

Farklı ağlardan gelen verileri senkronize etmeyeceğinizi biliyorsanız, GUID'ler değerlerinden daha fazla ek yük taşıyabilir.

Bazen bağlı istemcilerden veri almaya ihtiyaç duyuyorsanız, bunlar anahtar çarpışmalarını önlemek için bu istemciler için sıra aralıklarını ayarlamaktan çok daha güçlü olabilirler.


18
Benim anladığım kadarıyla GUID'ler UUID'ler ile eş anlamlıdır. UUID standart addır. GUID, Microsoft'un RFC 4122'den önce onları oluşturduğu şeydir .
JimmyJames

13
"Sıra dışı, bu yüzden kimliği sıralayamazsınız ve otomatik artan ID'lerde olduğu gibi ekleme siparişini almayı umut edemezsiniz." Aşırı uç bir durumda daha düşük bir kimliğin daha sonra diske kaydedilmesi mümkün olsa da, ekleme zaman damgası gibi yararlı sıralama verilerine güvenirim. Kimlikler, bellek adresleri gibi ele alınmalıdır - her şeyin bir tane vardır, ancak değerin kendisi anlamsızdır. En çok tiebreaker için kullanın. Özellikle, eğer bir toplu yükünüz varsa, yerleştirme sırası garanti edilmez.
Clockwork-Muse,

8
@CortAmmon Wikipedia ve RFC 4122'ye göre , eşanlamlıdır. P. Leach, Microsoft’un RFC’nin yaratıcılarından biriydi. Sanırım RFC kurulduğundan beri ikisi aynı. RFC'den: "GUID (Globally Unique IDentifier) ​​olarak da bilinen UUID'ler (Evrensel Benzersiz Tanımlayıcı)." GUID'lerin MS tarafından oluşturulmadığına dikkat etmek de yararlıdır. Başka bir yerden kabul edilen bir teknoloji için yeni bir isim oluşturdular.
JimmyJames 21

6
"SQL Server, GUID'lerle başa çıkmak için optimizasyonlara sahip olduğundan, sorgu performansını çok fazla etkilememelidir." -1 Neredeyse yeterince optimize edilmedi. Tüm PK'ların kılavuzluk ettiği bir DB ile çalışıyorum ve bunun düşük performansın başlıca nedenlerinden biriyim.
Andy,

7
"SQL Server, GUID'lerle başa çıkmak için optimizasyonlara sahip olduğundan, sorgu performansını çok fazla etkilememelidir. " Doğru değil. Bu ifade, diğer veri türlerinin optimize edilmediğini varsayar. Veri tabanı sunucuları ayrıca örneğin basit int değerleriyle uğraşmak için optimizasyonlara da sahiptir. GUID'ler / UUID'ler, 4 baytlık bir int değeri kullanmaktan çok daha yavaştır. 16 bayt hiçbir zaman 4 bayt kadar hızlı olmayacaktır - özellikle yerel olarak en fazla 4 veya 8 bayt işleyen bir makinede.
Andrew Henle,

28

Bu her zaman benzersiz olacak mı?

Her zaman? hayır, her zaman değil; sonlu bir bit dizisidir.

Diyelim ki Birincil Anahtar olarak bir GUID içeren milyonlarca ve milyonlarca satır içeren bir veritabanım vardı.

Milyonlar ve milyonlar, muhtemelen güvendesiniz. Bir milyonlarca insan ve bir çarpışma olasılığı önemli hale geliyor. Yine de iyi haberler var: bu olana kadar disk alanınız zaten tükendi.

Bunu sadece yapabilir miyim?

Yapabilirsin; Tamamen iyi bir fikir değil. Etki alanı modeliniz normalde rasgele sayılar oluşturmamalıdır; onlar senin modeline girdi olmalılar.

Bunun ötesinde, güvenilmez bir ağla uğraşırken, yinelenen mesajlar alabileceğiniz, belirleyici bir şekilde oluşturulmuş bir UUID sizi yinelenen varlıklara sahip olmaktan koruyacaktır. Ancak, her birine yeni bir rasgele sayı atarsanız, çoğaltmayı tanımlamak için daha çok işiniz vardır.

RFC 4122'deki ad tabanlı uuid tanımına bakın

GUID'i bir dize olarak modellemek "normal" mi, yoksa model ve veritabanında GUID olarak mı modellemeliyim?

Çok önemli olduğunu sanmıyorum. Etki alanı modelinizin çoğu için bir tanımlayıcıdır ; sorduğunuz tek sorgu, diğer tanımlayıcılarla aynı olup olmadığıdır. Etki alanı modeliniz normalde bir tanımlayıcının bellek temsiline bakmaz.

GUID, etki alanı agnostik ayarınızda "ilkel tür" olarak mevcutsa, onu kullanırdım; destekleyici içeriğin mevcut olabilecek uygun optimizasyonları seçmesine izin verir.

Ancak, bilmeniz gereken, tanımlayıcının hem bellekte hem de depolamada temsil edilmesinin, uygulamanızda verdiğiniz bir karar olduğu ve bu nedenle kodun ayak izinin buna eşleştiğinden emin olmak için adımlar atmanız gerektiğidir. Karar küçük - bkz. Parnas 1972 .


20
"+1," gerçekleşen zamanda zaten disk alanınız bitiyor. "
w0051977

2
Deterministik olarak üretilmiş UUID ” kavramının şart olduğunu düşünüyorum (bkz. Veri Kasası 2)
alk

Aslında, diğer verilere dayanarak bir UUID / GUID'yi yeniden hesaplayabilmek, özellikle de kopyaları tespit etmek için çok büyük bir yardımdır. Bir zamanlar mesajları depolayan ve onları bir işleme hattından iten bir mesaj işleme sistemi kurdum. Mesajın bir karmasını yarattım ve bunu sistem boyunca ana anahtar olarak kullandım. Sadece bu, başlı başına, ölçeklenmemiz gerektiğinde mesajı tanımlayabilmem için bana bir sürü sorun çözdü.
Newtopian

Bir milyon milyon = 2 ^ 40. Bu, 2 ^ 79 çift olası çarpışma yapar. GUID 2 ^ 128 bite sahiptir, bu yüzden şans 2 ^ 49'da birdir. Aynı GUID'i iki kayıt için yeniden kullanan veya yanlışlıkla hiçbir şeyin olmadığı bir çarpışma olduğuna inanan bir hatanız olması çok daha muhtemeldir.
gnasher729

Tarihi sorularıma geri dönüyorum. Kabul etmeden önce; Düzenlemeye bakar mısın?
w0051977

11

GUID veya UUID , nasıl üretildiklerinden dolayı benzersiz olacaktır ve merkezi bir otorite ile iletişim kurmak zorunda kalmadan benzersizliği garanti etmenin güvenli bir yolunu sağlarlar.

Birincil Anahtar Olarak GUID'lerin Yararları:

  • Bir kümenin farklı kırıkları arasında veri kopyalayabilir ve PK çakışmaları hakkında endişelenmenize gerek yoktur.
  • Herhangi bir kayıt eklemeden önce ana anahtarınızı bilmenizi sağlar.
  • Alt kayıtları eklemek için işlem mantığını basitleştirir.
  • Kolayca tahmin edilemez.

Sağladığınız örnekte:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

Ekleme zamanından önce GUID değerini belirlemek, art arda alt kayıtları eklerken veri tabanına gidiş dönüşü kaydedebilir ve aynı işlemi gerçekleştirmenize izin verir.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

Birincil Anahtar Olarak GUID'lere Zarar Verenler:

  • Bunlar büyük 16 bayttır, bu da dizinler ve yabancı anahtarlar eklendikçe daha fazla yer tüketecekleri anlamına gelir.
  • Aslında rastgele sayılar olduğu için iyi sıralama yapmazlar.
  • Endeks kullanımı çok, çok, çok kötü.
  • Bir sürü yaprak hareket ediyor.
  • Hatırlamaları zor.
  • Sözlüleştirmek zor.
  • URL'nin okunmasını zorlaştırabilirler.

Uygulamanızın keskinleştirme veya kümelemeye ihtiyacı yoksa, int veya bigint gibi daha küçük, daha basit veri türlerine uymak en iyisidir.

Birçok veritabanının, GUID'lerin ve SQL Server'ın neden olduğu depolama sorunlarını azaltmaya çalışan kendi iç uygulamaları vardır, hatta UUID'in endekslerin daha iyi kullanılmasına izin vermesine yardımcı olmak için sıralı bir işlevi vardır ve genellikle daha iyi performans özelliklerine sahiptirler.

Ek olarak, uygulama ile çalışan bir test cihazı, kullanıcı veya geliştirici açısından, bir GUID üzerinden bir ID kullanmak, iletişimi önemli ölçüde geliştirir. Bir telefon üzerinden bir GUID okumak zorunda olduğunuzu düşünün.

Sonunda, büyük ölçekli kümeleme veya URL’leri şaşırtıcı bir gereklilik olmadığı sürece, otomatik artan ID'lere bağlı kalmak daha pratiktir.


1
Dikkate alınması gereken bir şey, UUID'nin türüne bağlı olarak, oluşturuldukları makineyi tanımlamak için potansiyel olarak kullanılabilecek bilgileri içermeleridir. Saf rastgele değişkenin yeterli entropi olmadan çarpışması daha muhtemel olabilir. Bir URI'da kullanılmadan önce bu dikkate alınmalıdır.
JimmyJames

Yine de, bir kişi birincil anahtarını asla bir URL’de göstermemeli Harici sisteme sızan güvenli veri olmadığından emin olmak için daha uygun bir yöntem kullanılmalıdır.
icirellik

1
Bir başka kullanım durumu daha var: dizilim için kilitlemenin bir darboğaz olduğu ağır uç OLTP veritabanları. Oracle DBA arkadaşıma göre, bu göründüğü kadar nadir değildir, bunun için büyük ölçekli veya kümelenmelere bile ihtiyacınız yoktur. • Sonunda, artıları ve eksileri tartışın (ve UUID'lerin artılarını / eksilerini, bazı posterlerde olduğu gibi UUID'lere özgü olmayan artı / eksilerle karıştırmayın) ve ölçün .
mirabilos,

1
Newsequentialid kullanıyorsanız, kimliği almak için db'ye gitmeniz gerekir (bir int int kimliği gibi), değil mi? Buradaki fayda nedir?
w0051977

1
@mirabilos Açık olmak gerekirse, korkunç derken, satır başına dakika alan kesici uçlara sahip olduk . Her şey yolunda başladı, ancak 10 binlerce satırın ardından, çok hızlı bir şekilde yana doğru gitti. Açık değilse, 10 binlerce satır çok küçük bir tablodur.
JimmyJames,

4

Hayır derim, birincil anahtar olarak GUID kullanmayın. Aslında şimdi böyle bir DB ile uğraşıyorum ve bunlar performans sorunlarının temel nedenlerinden biri.

Ekstra 12 bayt hızlıca toplanır; Unutmayın, çoğu PKs diğer tablolarda FK olacak ve bir tablodaki sadece üç FK artık her satır için fazladan 48 bayta sahip olacak. Bu tabloya ve indekslere ekler. Aynı zamanda disk G / Ç'ye ekler. Bu ekstra 12 baytın okunması ve yazılması gerekir.

Sıralı kılavuzları kullanmıyorsanız ve PK'ler kümelenmişse (varsayılan olarak bu olur), SQL zaman zaman daha fazla "doğru" noktaya sıkıştırmak için veri sayfalarının tamamını dolaşmak zorunda kalacak. Çok sayıda kesici uç, güncelleme ve silme işlemine sahip yüksek işlemli bir veritabanı için işler hızla azalır.

Eşitleme veya başka bir şey için bir tür benzersiz tanımlayıcıya ihtiyacınız varsa, kılavuz sütun ekleyin. Sadece PK yapmayın.


4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Bu GUID'leri kullanmak için en önemli nedendir.

Kodunuz kalıcılık katmanınız hakkında hiçbir şey bilmeden veya onlarla iletişim kurmadan benzersiz bir kimlik oluşturabilmeniz büyük bir avantajdır.

Sunucunuzda, PC telefonunuzda, dizüstü bilgisayarınızda, çevrimdışı cihazınızda az önce oluşturduğunuz Person nesnesinin ya da dünyanın her yerindeki tüm sunucularınızda benzersiz olanların dağıtıldığından emin olabilirsiniz.

Herhangi bir veritabanı rdb veya no-sql, dosyaya yapıştırabilir, herhangi bir web servisine gönderebilir veya gereksiz olarak atıp hemen atabilirsiniz.

Hayır, asla bir çarpışma olmaz.

Evet, ekler, endeks ile karıştırılması gerekebileceği için biraz daha yavaş olabilir.

Evet bir int den daha büyük.

  • Düzenle. bitirmeden önce ateş etmek zorunda kaldı.

Birçok insanın otomobil incitleri hakkında güçlü hissettiğini biliyorum ve bu DBA'larla tartışmalı bir konudur

Ama ben gerçekten rehberlerin ne kadar üstün olduğunu yeterince kuvvetle belirtemiyorum. Kılavuzları varsayılan olarak herhangi bir uygulamada kullanmalısınız.

otomatik inc ints birçok kusur var

  • No-Sql dağıtılmış db kullanıyorsunuz. Bir sonraki sayının ne olduğunu bulmak için diğer tüm örneklerle konuşamazsınız.

  • Bir mesaj kuyruk sistemi kullanıyorsunuz. Şeyler db isabet önce kimlikleri gerekir

  • Birkaç öğe oluşturuyorsunuz ve kaydetmeden önce bunları düzenliyorsunuz. Db'ye basmadan önce her birinin bir kimliğe ihtiyacı var

  • Satırları silmek ve yeniden eklemek istiyorsunuz. Otomatik numaralarınızı saymadığınızdan ve tükendiğinizden emin olun!

  • Bu yıl kaç sipariş aldığınızı her kullanıcıya göstermemek istersiniz.

  • İlişkileri sınamak ve ilişkileri korumak için anonimleştirilmiş verileri üretimden taşımak istiyorsunuz. Ancak mevcut tüm test verilerini silmeyin.

  • Tek kiracı ürününüzü çok kiracılı bir veritabanında birleştirmek istiyorsunuz ancak herkesin siparişi 56.

  • Kalıcı ancak geçici olan nesneler yaratırsınız. (eksik siparişler) tekrar, tüm varlıklarınızı artık mevcut olmayan şeylerle kullanmayın.

Liste sonsuzdur ve hepsi her zaman insanların başına gelen gerçek problemlerdir. biraz daha büyük FK cols nedeniyle disklerin tükenmesinden farklı

Nihayet ints ile büyük sorun onlardan tükendi olmasıdır !!! tamam teoride bilmiyorum, yükler var. Fakat pratikte, çünkü insanlar onlara anlamsız rastgele sayılar gibi davranmazlar. gibi şeyler yaparlar

  • oh Müşterilerin yeni olduğumuzu düşünmelerini istemiyorum. 10,000'den başla

  • Ne kadar ithal edildiğini bildiğimiz için, bir veri yükünü içe aktarmak zorunda kaldım.

  • veri kategorisine ihtiyacımız var. Her dönem bir sonraki milyonda başlıyor, böylece ilk rakamları sihirli sayı olarak kullanabiliriz.

  • Tüm verileri yeni kimlikleriyle tekrar sildim ve yeniden aktardım. Evet, denetim günlükleri bile.

  • Bileşik bir anahtar olan bu sayıyı bu diğer şeyin kimliği olarak kullanın.


1
Bu cevabın aslında yanlış bir tarafı yoktur, ancak (daha fazla aşağı oy kullanmamak için) belki de gerçek açık uygulamalar çarpışmalarla karşılaşmasa da teorik olarak mümkün olacağı konusunda açıklığa kavuşur. (Ya da belki 45+ exabyte veritabanları düşündüğümden daha yaygın ...). “En önemli sebep” dilinin biraz güçlü olduğunu düşünmeme rağmen, en yararlı bulduğum şey bu.
BurnsBA,

2
Bir oto inc int bir rehber daha çarpışması muhtemeldir
Ewan

4
-1 "Herhangi bir uygulamada varsayılan olarak kılavuz kullanmalısınız." ™ 'ya bağlıdır. Diğerlerinin de gösterdiği gibi, GUID / UUID, benzersiz olmaları kesinlikle garanti edilmez.
Max Vernon,

3
"Bağlıdır" cevapları işe yaramaz, bir int'nin daha iyi olduğu bazı garip uygulamalar olacağından emin olun. Ama şansın senin uygulaman onlardan biri değil. KILAVUZLAR alabileceğiniz en eşsiz şeydir
Ewan

2
Kılavuzların daha iyi olduğu bazı garip uygulamalar olacağını düşünüyorum. Benzersiz, dikkate alınması gereken en önemli şey değildir. Sizin "kusurlarınız", kitlesel olarak aşırı şişirilmiş ve kılavuzların pek çok dezavantajının hiçbirini düşünmüyorsunuz.
Andy,

2

Bu GUID'lerin uygulama seviyesindeki nesneleri tanımlamak için kullanıldığını biliyorum. Ayrıca veritabanı düzeyinde birincil anahtar olarak depolanıyorlar.

İşte burada durmalı ve yeniden düşünmelisin.

Veritabanı birincil anahtarınızın ASLA iş anlamında olmaması gerekir. Tanım olarak anlamsız olmalı.

Bu nedenle, GUID'i iş anahtarınız ve normal bir birincil anahtar (genellikle uzun bir int) veritabanı birincil anahtarı olarak ekleyin. Benzersizliği sağlamak için her zaman GUID'e benzersiz bir dizin koyabilirsiniz.

Tabii ki veritabanı teorisi konuşuyor, ama aynı zamanda iyi bir uygulama. Birincil anahtarların ticari anlamı olan (örneğin, bir müşteri bazı veritabanı kaynaklarını çalışan sayıları, müşteri numaraları vb. Olarak kullanarak tasarruf etmeyi düşündüğü) veritabanlarını ele aldım ve her zaman sorun çıkarır.


1
Bunun bir tamsayı ana anahtarını kullanarak uygulama katmanından yapılan sorgulamadan ne farkı var? Bu noktada, uygulama katmanındaki nesneleri tanımlamak için de kullanılıyor. Bir veritabanındaki nesneleri uygulama katmanından tanımlamak için bir yönteme ihtiyacınız vardır.
icirellik

@icirellik birincil anahtar, ebeveyn ve alt kayıtları ve benzerlerini bağlamak için veri tabanı tarafından iç kullanım içindir. Uygulama mantığı ile kullanım amaçlı DEĞİLDİR, ürün numarası veya adı gibi bunun için iş kimlikleri kullanırsınız.
17'de

2

Her zaman veritabanı tarafından oluşturulan, otomatik artan Birincil Anahtarları (PK) kullanın.

GUID / UUID yerine neden otomatik artış kullanıyorsunuz?

  • GUID (UUID) ler benzersiz olmadıkları için kilit çarpmaları önlemez ve sayısız kaynaktan üretildiği için benzersiz olmaları mümkün değildir.
  • GUID'ler, zaten zaman alan birleştirme işlemini çok uzun zaman alan, tam olmayan, PK ve FK sütunlarıyla işlemek için çok zaman alan büyük ölçüde artırdıklarından birleştirme işlemine yardımcı olmaz. Çoğu PK için, aynı boyutta en az 2 anahtara sahip en az 1 başka masa olacağını unutmayın: bu, kendi PK ve ilk masaya geri dönüş yapan bir FK'dır. Hepsi bir arada çözülmek zorunda.

Ama o zaman nasıl kırıkları, kümeleri, vb ele almak için?

  • Her bir parçayı / kümeyi / veritabanını / kendi otomatik artan anahtarını yöneten her şeyi tanımlayan ayrı sütunlardan oluşan çok sütunlu PK'ler oluşturun. Örneğin...

Kümelenmiş bir tablo için 3 sütunlu bir PK olabilir ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

Ama ne hakkında...?

  • Veritabanına yapılan çoklu yolculuklar - Çoğu uygulamanın, veritabanına ekleninceye kadar oluşturulmuş bir kaydı benzersiz bir şekilde tanımlamasına gerek yoktur; Uygulama gerçekten bu yeteneğe ihtiyaç duyuyorsa , veritabanına gönderilmeyen bir geçici PK uygulaması kullanın . Veritabanının eklendiğinde, satırın üzerine kendi otomatik artan PK'sini koymasına izin verin. Ekler geçici PK'yi kullanacak, güncelleme ve silme işlemleri veritabanı tarafından atanan kalıcı PK'yi kullanacaktır.

  • Performans - Bilgisayarlar, bir GUID (37) 'deki eleman başına olası değerler ve bir tamsayı (10) olması durumunda, mümkünse daha büyük alanlardan dolayı basit tam sayıları her şeyden çok daha hızlı işleyebilirler. Bir GUID'deki her karakterin önce CPU tarafından manipüle edilecek bir sayıya dönüştürülmesi gerektiğini de unutmayın.

Birincil Anahtarların Yaygın Suistimalleri PK'lerin, tablodaki bir satırı kesinlikle benzersiz şekilde tanımlamak için tek bir amacı vardır. Başka bir şey çok yaygın bir yanlış kullanımdır.

Eksik Kayıtları Algılama

  • Kayıp kayıtlar PK’lere bakılarak tespit edilemez. QA'yı en azından veri kalitesini sağlamaya çalışmak için kutsa. Bununla birlikte, onlar ve programcının modern veritabanı sistemlerinde anahtarların nasıl atandığını anlamadaki yetersizlikleri, kendilerini otomatik olarak artan bir PK'deki eksik bir sayının eksik veri anlamına geldiğine dair yanlış inançlara yol açmaktadır. Öyle değil çünkü ...
  • Performans için, veritabanı sistemleri, depodaki asıl veri tabanına yapılan gezileri asgariye indirmek için sayı dizilerini 'dizilerde' (partiler, aralıklar) tahsis eder. Bu sayı dizilerinin büyüklüğü genellikle DBA'nın kontrolü altındadır, ancak tablo bazında ayarlanamayabilir.
  • Anahtar paket ... ... bu dizilerden kullanılmayan numaralar asla veritabanına geri gönderilmez, bu yüzden her zaman PK numaralarında boşluklar vardır.
  • Neden sorduğun kullanılmamış numaralar olsun ki? Çünkü çeşitli veritabanı bakım işlemleri, dizilerin terk edilmesine neden olabilir. Bunlar yeniden başlatmalar, tabloların toplu olarak yeniden yüklenmesi, bazı yedeklemelerden geri yükleme türleri ve diğer bazı işlemler gibi şeylerdir.

sınıflandırma

  • PK'ya göre sıralama çok hataya açıktır, çünkü çoğu kişi satırları oluşturuldukları sırayla listelediğini ve bunun saat zamanına karşılık geldiğini düşünür. Çoğunlukla, ama mutlaka değil.
  • Veritabanı motorları maksimum performans için optimize edilmiştir ve tabiri caizse “basit” olan kısa basit olanları eklemek için uzun süren karmaşık bir işlemin sonuçlarının eklenmesinin geciktirilmesi anlamına gelebilir.

Tablo şeması konusundaki düşünceleriniz nedir, öyle ki tek benzersiz sütun veritabanında oluşturulan otomatik artan birincil anahtardır? Özellikle yabancı anahtar içermeyen ancak birincil anahtarı, ilgili birkaç tablo için yabancı anahtar olan tablolar için?
RibaldEddie

Bu satırlar boyunca cevaba çok daha fazla şey ekledim. Asıl cevap, asılmakta olduğum Android SE uygulaması nedeniyle eksikti. Uygulamanın büyük bir yeniden yazım geliştirme aşamasında olduğunu düşünüyorum.
DocSalvager

Öyleyse, bir tablonun otomatik artan birincil anahtarı için aynı olan herhangi bir sayıda satır içermesi uygun olur mu?
RibaldEddie

@RibaldEddie - DB'nin izin verdiği ölçüde ... kesinlikle. Silmeler kolaydır. Senaryonuz gerçekleştiğinde, yazılımda düzeltilmesi gereken bir hata olduğunu düşünür ve ardından her iki satırı da silerim. Çok daha yaygın bir durum olsa da, aynı şey için biraz farklı veriler içeren iki kayıt bu yüzden birleştirilmeleri gerekiyor. Bir sütunda bir kayıt boşsa ve diğerinde bir değer varsa, seçim açıktır ve otomatikleştirilebilir. Genellikle datetimestamp, otomatik bir birleştirme hakemliği yapmak için kullanılabilir. Bazı kopyalar, bir kişinin iş kurallarına göre birleşmesini tamamlamasını ve doğrulamasını gerektirir.
DocSalvager

1

Her şey gibi, bunu yapmanın avantajları ve dezavantajları vardır:

İyi:

  1. Anahtarlarınız her zaman aynı uzunluktadır (çok büyük veritabanları çok büyük anahtarlara sahip olabilir)

  2. Benzersizlik çok fazla garanti - ayrı bir sistemden oluşturduğunuzda ve / veya veritabanındaki son kimliği okumadığınızda bile

Kötü:

  1. Yukarıda da belirtildiği gibi - daha büyük endeksler ve veri deposu.

  2. ID ile sipariş veremezsiniz, başka bir şey sipariş etmeniz gerekir. Daha fazla dizin, muhtemelen daha az verimli.

  3. Daha az insan tarafından okunabilirler. Tamsayılar genellikle insanlar için ayrıştırılması, hatırlanması ve yazılması daha kolaydır. GUID'leri, birleştirilmiş birden çok tablodaki WHERE yan tümcelerinde kimlik olarak kullanmak başınızı eritebilir.

Her şey gibi, uygun olan yerlerde kullanın, dogmatik olmayın - çoğu durumda otomatik artan tamsayılar daha iyidir, bazen GUID'ler harikadır.


0

Evet, GUID'i birincil anahtar olarak kullanabilirsiniz. Aşağı taraf, endeksin büyüklüğü ve hızlı parçalanmasıdır.

Veritabanları arasında benzersizliğe ihtiyaç duymadığınız sürece (örneğin bir küme) tamsayı tercih edilir.


GUID jeneratörleri aynı GUID'i bir kereden fazla üretebilir, burada bir kusur vardır. İstekli olup olmadıklarına, özellikle saat keneleri arasındaki süreye bağlı olarak, ayrıntı derecelerine göre değişir. Örneğin, saat tabanlı bir jeneratör yalnızca her 100ms'de bir hareket edebilir ve bu 100ms içinde talep edilen 2 GUID'in aynı olmasını sağlar. Bundan kaçınmanın yolları vardır, ancak çoğu GUID üreticisi tamamen IP adresi ve / veya MAC adresi ve zaman damgası dışında çalışır.
17'de

0

İşte bu konuda benim aldığım - çözüm, her ikisinden de en iyisini alan, GUID ve int değerleri arasında bir yarım ev.

Sınıf, bir Comb GUID'ye benzer olan, sözde rasgele (ancak zamanla artan) Id değeri oluşturur .

En önemli avantaj, sunucuda oluşturulan otomatik artış değerlerini (bir dönüş turu gerektiren) kullanmak yerine, neredeyse sıfır yinelenmiş değer riski olan istemcide Id değerlerinin oluşturulmasına izin vermesidir.

Oluşturulan değerler bir GUID için 16 yerine 8 bayt kullanır ve belirli bir veritabanı sıralama düzenine bağlı değildir (örn . GUID'ler için Sql Server ). Değerler tüm işaretsiz uzun aralığı kullanacak şekilde genişletilebilir, ancak bu yalnızca tamsayı tiplerini imzalayan herhangi bir veritabanı veya diğer veri havuzunda sorunlara neden olabilir.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
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.