Birincil anahtar olarak GUID ile veritabanı tasarımını düzeltmek için en iyi çözüm


18

Kötü performans gösteren bir veritabanı veya herhangi biri varsa daha iyi bir öneri düzeltmek için bu fikrin bazı onaylandıktan sonra duyuyorum. Her zaman daha iyi önerilere açıktır.

PK olarak GUID kullanan çok büyük bir veritabanım var (günde yaklaşık 1/2 milyon büyüyen 20+ milyon kayıt).

Benim açımdan bir gözetim ancak PK SQL sunucusunda kümelenmiş ve performans sorunlarına neden oluyor.

Bir kılavuzun nedeni - bu veritabanı kısmen 150 diğer veritabanıyla senkronize edilmiştir, bu nedenle PK'nın benzersiz olması gerekir. Senkronizasyon, SQL Server tarafından yönetilmez, bunun yerine, verilerin sistem gereksinimleri için senkronize olmasını sağlayan özel bir işlem vardır - hepsi bu GUID'ye dayanır.

150 uzak veritabanının her biri, merkezi SQL Veritabanında depolanan verilerin tamamını depolamaz. yalnızca gerçekte ihtiyaç duydukları verilerin bir alt kümesini saklarlar ve gereksinim duydukları veriler kendilerine özgü değildir (150 veritabanından 10'u, örneğin diğer site veritabanlarından aynı kayıtlardan bazılarına sahip olabilirler - paylaşırlar). Ayrıca - veriler aslında uzak noktalarda üretilir - merkezi noktada değil - bu nedenle GUID'lere ihtiyaç vardır.

Merkezi veritabanı sadece her şeyi senkronize tutmak için kullanılmaz, aynı zamanda 3000'den fazla kullanıcının sorguları bu çok büyük parçalı veritabanına karşı yürütülür. Zaten bu ilk testlerde büyük bir problem.

Neyse ki henüz yaşamıyoruz - bu yüzden en azından bir şey olan değişiklikler yapabilir ve gerekirse çevrimdışı yapabilirim.

Uzak veritabanlarının performansı sorun değildir - veri alt kümeleri oldukça küçüktür ve veritabanı genellikle toplamda 1GB'ın üzerine çıkmaz. Kayıtlar oldukça düzenli olarak ana sisteme geri beslenmekte ve artık gerekli olmadığında daha küçük BD'lerden çıkarılmaktadır.

Birçok kaydın birincil anahtarı olarak kümelenmiş bir GUID nedeniyle, tüm kayıtların koruyucusu olan merkezi DB'nin performansı sıkıntılıdır. Dizin parçalanması grafiklerin dışında.

Yani - performans sorunu düzeltmek için düşüncelerimi yeni bir sütun oluşturmak - İmzasız BIGINT IDENTITY (1,1) ve sonra tablo BIGINT sütununun Kümelenmiş PK değiştirmek olduğunu.

Birincil anahtar olan GUID alanında bir Benzersiz Kümelenmemiş dizin oluştururdum.

Daha küçük uzak 150 veritabanları, merkezi SQL Server veritabanındaki yeni PK hakkında bilgi sahibi olmak zorunda değildir - sadece veritabanındaki verileri düzenlemek ve kötü performans ve parçalanmayı durdurmak için kullanılacaktır.

Bu, merkezi SQL veritabanının performansını artıracak ve gelecekteki dizin parçalanma cehennemini engelleyecek mi (bir dereceye kadar)? ya da burada çok önemli bir şeyi kaçırdım ve beni ısırıp daha fazla keder yaratacak mı?


2
@mattytommo katılıyorum.
Paul Fleming

2
Çalıştırdığınız indeks birleştirme haftada bir kez en az?
Andomar

1
Kümelenmesi anlamlı olan bir şey var mı? Yani, hangi sorgu hızlı olmalı? Kesinlikle kılavuzda aralık taraması olmayacak, bu yüzden sadece otomatik bir artış seçmek yerine, seçebileceğiniz bazı sorgu zamanı optimum kümeleme olup olmadığını düşünün. Değilse, devam edin ve

2
@Borik Harika bir fikir değil, sahip olduğu ve büyüme hızına bağlı intolarak 4255 gün (11.5 yıl) içinde tükenecekti. Eğer bunu yaptıysa, sizi sadece 11.5 yıl içinde suçlar;)
mattytommo

1
Aksine bir görünüm: GUID veri tipinin neden bir sorun olduğunu düşünüyorsunuz? 128 bitlik bir tam sayıdır. Neden 64 bit tam sayı (bigint) veya 32 bit tam sayı (int) ile değiştirmenin hızda fark edilir bir fark yaratacağını düşünüyorsunuz? Ben parçalanma yol açan tüm sayfa bölme önlemek için, kümeleme anahtar kesinlikle başka bir şey değiştirmek gerektiğini düşünüyorum, ama veri türü sorun olduğundan emin değilseniz veri türünü değiştirmek gerektiğini sanmıyorum.
Greenstone Walker

Yanıtlar:


8

GUID üzerinde kümelenmemeniz GEREKİR. Bu GUID dışındaki kayıtları benzersiz bir şekilde tanımlamanıza izin verecek bir şeyiniz varsa , o diğer alanda benzersiz bir dizin oluşturmaya ve bu dizini kümelemeye bakmanızı öneririm. Değilse, benzersiz olmayan dizinler kullanarak bile diğer alanlarda kümeleme yapmakta serbestsiniz. Bununla birlikte, kümelenme yaklaşımı, verilerinizi bölmeyi ve sorgulamayı en iyi şekilde kolaylaştırır - bu nedenle, bir "bölge" alanı veya başka bir şey varsa, bu kümeleme planınız için aday olabilir.

Birini değiştirmeyle ilgili sorun BIGINT, diğer veritabanlarındaki verilere eklenmeler ve veritabanlarını merkezi mağazaya entegre etmektir. Bu bir değerlendirme değilse ve asla dikkate alınmayacaksa, evet, BIGINTendeks yeniden dengeleme sorununu güzel bir şekilde çözer.

Kümelenmiş bir dizin belirtmezseniz, SQL Server aynı şeyi yapar: bir satır kimliği alanı oluşturur ve diğer tüm dizinleri buna eşler. Yani, bunu kendiniz yaparak, tıpkı SQL'in çözeceği gibi çözüyorsunuz.


Tablodaki tek gerçekten benzersiz alan GUD'dır - diğer sütunlar benzersiz değildir ve başlangıçta benzersiz olabilecek sütun kombinasyonları vardır - ancak zamanla yinelenen bir kayıt oluşturma olasılıkları azdır. Çok uzak ama verilerin niteliği göz önüne alındığında mümkündür. Diğer tüm kümelenmemiş dizinler arama performansını vb geliştirmek için kümelenmiş dizine başvurduğunu okudum. GUID performans etkisi neden olarak kümelenmiş bir PK sahip olmaz? Mekanın farkındayım ve bir endişe olsa da - performans çok önemlidir.
Roddles

Kümelenmiş bir dizin belirtmezseniz performans isabeti, SQL'in sizin için sahne arkasında bir tane oluşturacağı ve diğer tüm dizinleri bu dizine eşleyeceği yönündedir. Yani, sizin durumunuzda, SQL'in bunu yapmasına izin vererek bir performans iyileştirmesi elde edersiniz , çünkü şu anda sıralama düzeni önemli olmadığında sıralama düzenini korumak için tüm verilerinizi diskte sürekli karıştırıyorsunuz. Daha fazla depolama alanına ihtiyacınız olacak, ancak depolamada büyük bir iyileşme göreceksiniz ve geri alma üzerinde minimum etkisi yoktur.
David T. Macknet

Yani sanırım BIGINT Kümelenmiş PK yapmazsam ve sadece PK'yi Kümelenmemiş GUID olarak değiştirirsem performans sonuçları nelerdir? Tabloda, sık sık aranacak başka kümelenmemiş dizinler vardır. Bu, bu aramaların performansını etkiler mi?
Roddles

+1 Ayrıca GUID'lerle kalmanızı da öneririm. Dağıtılmış sistemlerde bunların yerini almak çok zordur. Senin büyük tablo kümelenmiş dizin nasıl dayanan belirgin olmalıdır sorgulamak verileri.
Remus Rusanu

1
Merhaba Guys - Sadece bir güncelleme - Değişiklikleri yaptım ve PK'yi GUID'de Kümelenmemiş Yaptım ve SQL Server 2+ milyon kaydı veritabanına eklemekle meşgul. Aynı zamanda veri eklenirken, bilgi ve sorgulama için zaman zaman 10 dakika içinde zaman aşımına uğrayan ve 1-2 saniye içinde tamamlanan sorgular için sorgulayabildim. Yani - PK'nin kümelenmemiş hale getirilmesi ve BIGINT hakkında endişelenmemesi Güzel çalıştı. Herkesin katkıları ve yardımları için çok teşekkürler.
Roddles

1

Bu uzun bir emirdir.

Orta adam yaklaşımı önereyim.

Rasgele kılavuzlar üreten System.Guid.NewGuid () ile ilgili sorunlar yaşıyordum. (Ben istemci sıralı oluşturmak için veritabanına güvenmek yerine, kendi kılavuz oluşturmak için izin veriyordu).

Bir kez istemci tarafında bir UuidCreateSequential taşındı, benim performans özellikle INSERTs çok daha iyi oldu.

İşte DotNet istemci kodu voodoo. Eminim bir yerden rehin aldım:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;


namespace MyCompany.MyTechnology
{
  public static class Guid
  {


    [DllImport("rpcrt4.dll", SetLastError = true)]
    static extern int UuidCreateSequential(out System.Guid guid);


    public static System.Guid NewGuid()
    {
      return CreateSequentialUUID();
    }


    public static System.Guid CreateSequentialUUID()
    {
      const int RPC_S_OK = 0;
      System.Guid g;
      int hr = UuidCreateSequential(out g);
      if (hr != RPC_S_OK)
        throw new ApplicationException("UuidCreateSequential failed: " + hr);
      return g;
    }


  }
}














    /*

Original Reference for Code:
http://www.pinvoke.net/default.aspx/rpcrt4/UuidCreateSequential.html


*/

/*



Text From URL above:

UuidCreateSequential (rpcrt4)

Type a page name and press Enter. You'll jump to the page if it exists, or you can create it if it doesn't.
To create a page in a module other than rpcrt4, prefix the name with the module name and a period.
. Summary
Creates a new UUID 
C# Signature:
[DllImport("rpcrt4.dll", SetLastError=true)]
static extern int UuidCreateSequential(out Guid guid);


VB Signature:
Declare Function UuidCreateSequential Lib "rpcrt4.dll" (ByRef id As Guid) As Integer


User-Defined Types:
None.

Notes:
Microsoft changed the UuidCreate function so it no longer uses the machine's MAC address as part of the UUID. Since CoCreateGuid calls UuidCreate to get its GUID, its output also changed. If you still like the GUIDs to be generated in sequential order (helpful for keeping a related group of GUIDs together in the system registry), you can use the UuidCreateSequential function.

CoCreateGuid generates random-looking GUIDs like these:

92E60A8A-2A99-4F53-9A71-AC69BD7E4D75
BB88FD63-DAC2-4B15-8ADF-1D502E64B92F
28F8800C-C804-4F0F-B6F1-24BFC4D4EE80
EBD133A6-6CF3-4ADA-B723-A8177B70D268
B10A35C0-F012-4EC1-9D24-3CC91D2B7122



UuidCreateSequential generates sequential GUIDs like these:

19F287B4-8830-11D9-8BFC-000CF1ADC5B7
19F287B5-8830-11D9-8BFC-000CF1ADC5B7
19F287B6-8830-11D9-8BFC-000CF1ADC5B7
19F287B7-8830-11D9-8BFC-000CF1ADC5B7
19F287B8-8830-11D9-8BFC-000CF1ADC5B7



Here is a summary of the differences in the output of UuidCreateSequential:

The last six bytes reveal your MAC address 
Several GUIDs generated in a row are sequential 
Tips & Tricks:
Please add some!

Sample Code in C#:
static Guid UuidCreateSequential()
{
   const int RPC_S_OK = 0;
   Guid g;
   int hr = UuidCreateSequential(out g);
   if (hr != RPC_S_OK)
     throw new ApplicationException
       ("UuidCreateSequential failed: " + hr);
   return g;
}



Sample Code in VB:
Sub Main()
   Dim myId As Guid
   Dim code As Integer
   code = UuidCreateSequential(myId)
   If code <> 0 Then
     Console.WriteLine("UuidCreateSequential failed: {0}", code)
   Else
     Console.WriteLine(myId)
   End If
End Sub




*/

ALTERNATİF FİKİR:

Eğer ana db ve uzak db en "bağlı" (olduğu gibi, sp_linkserver) ...... o zaman ana db "uuid jeneratör" olarak kullanabilirsiniz.

Uuid'in "tek tek" olmasını istemezsiniz, bu çok fazla sohbettir.

Ama bir uuid seti alabilirsin.

Aşağıda bazı kodlar verilmiştir:

IF EXISTS (SELECT * FROM sys.objects WHERE object_id =
 OBJECT_ID(N'[dbo].[uspNewSequentialUUIDCreateRange]') AND type in (N'P',
 N'PC'))

 DROP PROCEDURE [dbo].[uspNewSequentialUUIDCreateRange]

 GO



 CREATE PROCEDURE [dbo].[uspNewSequentialUUIDCreateRange] (

 @newUUIDCount int --return

 )

 AS

 SET NOCOUNT ON

 declare @t table ( dummyid int , entryid int identity(1,1) , uuid
 uniqueidentifier default newsequentialid() )

 insert into @t ( dummyid ) select top (@newUUIDCount) 0 from dbo.sysobjects
 so with (nolock)

 select entryid , uuid from @t

 SET NOCOUNT OFF

 GO

/ *

--START TEST

 set nocount ON

 Create Table #HolderTable (entryid int , uuid uniqueidentifier )

 declare @NewUUIDCount int

 select @NewUUIDCount = 20

 INSERT INTO #HolderTable EXEC dbo.uspNewSequentialUUIDCreateRange
 @NewUUIDCount

 select * from #HolderTable

 DROP Table #HolderTable

 --END TEST CODE

* /


İlginç - ve düşünmediğim yaklaşım - bunu daha yakından inceleyeceğim, çünkü bu güzel görünüyor ve bazı test projeleri yürütüyor. Eğer merkezi veri tabanına geri bildirilen sıralı kılavuzlar üreten 150 veri tabanımız olsaydı, merkezi veri tabanına yerleştirildiğinde kılavuzlar hala oldukça rasgele olacağından bu hala parçalanmaya neden olmaz. Tabii ki kümelenmiş PK'yi düşürmek ve kümelenmemiş PK'ya sahip olmak mı demek istemiyorsanız?
Roddles

150 "uzak" veritabanı birer birer ekleniyor mu? Yoksa geceleri toplu olarak veri taşıyorlar mı? Yani bir kaya ile sert bir yer arasındasınız. Bigint kullanmak sonunda oda (belki) tükenecek ve hala birçok db's benzersiz değer elde etmek zorunda. İşte benim radikal fikrim. 150 uzak veritabanı UUID'lerini merkezi bir hizmetten alabilir mi? Bu bir fikir. 150 uzak veritabanı ana veritabanına "bağlı" mı (sp_addlinkedserver gibi)? Sonra düşünülebilir bir UDF var. Bakalım bulabilir miyim.
Ocak


0

Açıklamanıza göre, BIGINT ile devam edin. Bununla birlikte, GUID'lerin dizini benzersiz olmayabilir, çünkü GUID'lerin yine de global olarak benzersiz olması gerekir.


-1

GUID doğru şekilde saklanırsa uniqueidentifier herhangi bir performans sorunu olmamalıdır ... ve Sıralı GUID'yi daha iyi kullanabiliyorsanız ...

Ayrıca @mattytommo INT kullanarak yaklaşık 11.5 yıl iyi bir noktaya sahiptir ...


Evet - ama rehber SQL Server veritabanında değil, uzak 150 veritabanında oluşturulur - bu yüzden sıralı kılavuz kullanamam - ama yanıt için teşekkürler.
Roddles

Bu durumda bence planınız sağlam bir şeydir, yönettiğim DB'lerde benzer bir şey yaptım, bir INT DENTITY (1,1) oluşturdum ve onu kümelenmiş PK ve veri için insanca okunabilir tanımlayıcı olarak ayarladım yukarı çekin ve ben kaynak nerede izlemek için GUID (Index) izci olarak tuttu. Ama motivasyonum yerden tasarruf
etmekten

Cevaplarınız ve görüşleriniz için çok teşekkür ederiz. :)
Mart'ta Roddles
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.