Beste Lösung zum Korrigieren des Datenbankdesigns mit der GUID als Primärschlüssel

18

Ich bin nach einiger Bestätigung dieser Idee, eine schlecht funktionierende Datenbank zu reparieren oder einen besseren Vorschlag, falls einer eine hat. Immer offen für bessere Vorschläge.

Ich habe eine sehr große Datenbank (mehr als 20 Millionen Datensätze, die um ungefähr eine halbe Million pro Tag wachsen), die GUID als PK verwenden.

Ein Versehen meinerseits, aber die PK ist auf SQL Server geclustert und verursacht Leistungsprobleme.

Der Grund für eine Guid: Diese Datenbank ist teilweise mit 150 anderen Datenbanken synchronisiert, sodass die PK eindeutig sein musste. Die Synchronisierung wird nicht von SQL Server verwaltet, sondern es wird ein benutzerdefinierter Prozess erstellt, der die Daten für die Anforderungen des Systems synchronisiert - und zwar basierend auf dieser GUID.

In jeder der 150 entfernten Datenbanken werden nicht die vollständigen Daten gespeichert, die in der zentralen SQL-Datenbank gespeichert sind. Sie speichern nur eine Teilmenge der Daten, die sie tatsächlich benötigen, und die Daten, die sie benötigen, sind nicht eindeutig für sie (10 der 150 Datenbanken enthalten möglicherweise einige der gleichen Datensätze aus Datenbanken anderer Sites, die sie beispielsweise gemeinsam nutzen). Außerdem werden die Daten tatsächlich an den entfernten Standorten und nicht an der zentralen Stelle generiert, weshalb die GUIDs erforderlich sind.

Die zentrale Datenbank wird nicht nur verwendet, um alles synchron zu halten, sondern es werden auch Abfragen von mehr als 3000 Benutzern für diese sehr große fragmentierte Datenbank ausgeführt. Bereits beim ersten Testen ist dies ein großes Problem.

Zum Glück sind wir noch nicht live - also kann ich Änderungen vornehmen und bei Bedarf Dinge offline schalten, was zumindest etwas ist.

Die Leistung der entfernten Datenbanken ist kein Problem - die Datenteilmengen sind ziemlich klein und die Datenbank wird in der Regel nie größer als 1 GB. Die Datensätze werden regelmäßig an das Hauptsystem zurückgespeist und von den kleineren BDs entfernt, wenn sie nicht mehr benötigt werden.

Die Leistung der zentralen Datenbank, in der alle Datensätze gespeichert sind, ist bedauerlich - aufgrund einer gruppierten GUID als Primärschlüssel für die vielen Datensätze. Die Indexfragmentierung ist nicht in den Diagrammen.

Meine Überlegungen zur Behebung des Leistungsproblems sind, eine neue Spalte zu erstellen - Unsigned BIGINT IDENTITY (1,1) und dann die Clustered PK der Tabelle BIGINT-Spalte zu ändern.

Ich würde einen eindeutigen nicht gruppierten Index auf dem GUID-Feld verursachen, das der Primärschlüssel war.

Die kleineren entfernten 150-Datenbanken müssen nichts über die neue PK in der Central SQL Server-Datenbank wissen. Sie wird lediglich zum Organisieren der Daten in der Datenbank und zum Unterbinden der schlechten Leistung und Fragmentierung verwendet.

Würde dies funktionieren und die Leistung der zentralen SQL-Datenbank verbessern und die zukünftige Hölle der Indexfragmentierung (bis zu einem gewissen Grad) verhindern? oder habe ich hier etwas sehr wichtiges verpasst, das aufspringen und mich beißen und noch mehr trauern wird?

Roddles
quelle
2
@mattytommo Ich stimme zu.
Paul Fleming
2
Sind Sie mit Index - Defragmentierung mindestens einmal pro Woche?
Andomar
1
Haben Sie irgendetwas, auf das Sie sich konzentrieren können? Dh welche Abfrage sollte schnell sein? Es wird definitiv kein Entfernungsscan in der Guid sein, also überlegen Sie, ob Sie eine optimale Clustering-Abfragezeit wählen können, anstatt nur eine automatische Schrittweite zu wählen. Wenn nicht, dann benutze den bigint
2
@Borik Keine gute Idee, basierend auf dem, was er hat und seiner Wachstumsrate, würde er intin 4255 Tagen (11,5 Jahren) erschöpfen . Wenn er das tun würde, würde er dich nur in 11,5 Jahren beschuldigen;)
mattytommo
1
Eine gegenteilige Ansicht: Warum ist der GUID-Datentyp Ihrer Meinung nach ein Problem? Es ist eine 128-Bit-Ganzzahl. Warum wird das Ersetzen durch eine 64-Bit-Ganzzahl (bigint) oder eine 32-Bit-Ganzzahl (int) Ihrer Meinung nach einen spürbaren Geschwindigkeitsunterschied bewirken? Ich denke, Sie sollten den Clustering-Schlüssel auf jeden Fall in einen anderen ändern, um zu vermeiden, dass alle Seiten geteilt werden, was zur Fragmentierung führt, aber ich denke nicht, dass Sie den Datentyp ändern sollten, es sei denn, Sie sind sich sehr sicher, dass der Datentyp das Problem ist.
Greenstone Walker

Antworten:

8

Sie müssen sich auf keinen Fall auf der GUID zusammenschließen. Wenn Sie über etwas verfügen, mit dem Sie andere Datensätze als diese GUID eindeutig identifizieren können , empfehlen wir Ihnen, einen eindeutigen Index für dieses andere Feld zu erstellen und diesen Index zu einem Cluster zu machen. Andernfalls können Sie auch mithilfe nicht eindeutiger Indizes Cluster für andere Felder erstellen. Der Ansatz, Cluster zu bilden, ist der beste, um Ihre Daten aufzuteilen und abzufragen. Wenn Sie also ein Gebietsfeld oder etwas anderes haben, ist dies möglicherweise ein Kandidat für Ihr Clustering-Schema.

Das Problem beim Wechsel zu a BIGINTwäre, Daten aus anderen Datenbanken zu ergänzen und deren Datenbank in den zentralen Speicher zu integrieren. Wenn dies keine Überlegung ist - und niemals eine Überlegung sein wird -, dann BIGINTwürde das das Problem der Indexanpassung gut lösen.

Wenn Sie hinter den Kulissen keinen Clustered-Index angeben, geht SQL Server ähnlich vor: Es erstellt ein Zeilen-ID-Feld und ordnet alle anderen Indizes diesem zu. Wenn Sie es also selbst tun, lösen Sie es so, wie SQL es lösen würde.

David T. Macknet
quelle
Das einzige wirklich eindeutige Feld in der Tabelle ist die GUD - die anderen Spalten sind nicht eindeutig und es gibt Kombinationen von Spalten, die anfangs eindeutig sein können -, aber mit der Zeit besteht eine geringe Wahrscheinlichkeit, dass sie einen doppelten Datensatz erzeugen. Sehr abgelegen, aber aufgrund der Art der Daten möglich. Ich habe gelesen, dass alle anderen nicht gruppierten Indizes auf den gruppierten Index verweisen, um die Suchleistung usw. zu verbessern. Würde eine gruppierte PK als GUID nicht zu Leistungseinbußen führen? Ich bin mir des Raumes bewusst und dabei ist ein Anliegen - Leistung steht an erster Stelle.
Roddles
Wenn Sie keinen Clustered-Index angeben, hat dies zur Folge, dass SQL einen hinter den Kulissen erstellt und alle anderen Indizes diesem Index zuordnet. Also, in Ihrem Fall würden Sie eine Leistung erhalten Verbesserung , indem sie SQL das tun, denn im Moment sind Sie ständig alle Ihrer Daten um auf der Festplatte schlurfen die Sortierreihenfolge zu erhalten , wenn die Sortierreihenfolge ist nicht wichtig. Sie benötigen mehr Speicherplatz, sehen jedoch eine erhebliche Verbesserung des Speichers und minimale / keine Auswirkungen auf den Abruf.
David T. Macknet
Die Frage ist also, was die Auswirkungen auf die Leistung sind, wenn ich die BIGINT-Cluster-PK nicht mache und sie einfach in eine Nicht-Cluster-GUID ändere. In der Tabelle befinden sich andere nicht gruppierte Indizes, die häufig durchsucht werden. Würde sich dies auf die Leistung dieser Suchvorgänge auswirken?
Roddles
+1 Ich würde auch vorschlagen, bei den GUIDs zu bleiben. Ist sehr schwer, sie in verteilten Systemen zu ersetzen. Der Clustered-Index für große Tabellen sollte anhand der Art und Weise ersichtlich sein, wie Sie die Daten abfragen .
Remus Rusanu
1
Hallo Leute - Nur ein Update - Ich habe die Änderungen vorgenommen und die PK zu einer Non-Clustered-on-GUID gemacht, und der SQL Server ist damit beschäftigt, die über 2 Millionen Datensätze in die Datenbank einzufügen. Gleichzeitig mit dem Einfügen der Daten konnte ich die Datenbank nach Informationen abfragen und die Abfragen, die zeitlich vor der Änderung um 10 Minuten abgelaufen waren, innerhalb von 1-2 Sekunden abschließen. Die PK nicht geclustert zu machen und sich keine Sorgen um den BIGINT zu machen, scheint also gut zu funktionieren. Vielen Dank für die Beiträge und die Unterstützung aller.
Roddles
1

Das ist eine große Aufgabe.

Lassen Sie mich einen Mittelmannansatz vorschlagen.

Ich hatte Probleme mit System.Guid.NewGuid (), das zufällige Guids generiert. (Ich habe dem Client erlaubt, eine eigene Guid zu erstellen, anstatt mich auf die Datenbank zu verlassen, um eine sequenzielle ID zu erstellen.)

Nachdem ich auf der Clientseite zu einem UuidCreateSequential gewechselt war, wurde meine Leistung VIEL besser, insbesondere bei INSERTs.

Hier ist der DotNet-Client-Code voodoo. Ich bin sicher, ich habe von irgendwoher verpfändet:

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




*/

ABWECHSELNDE IDEE:

Wenn Ihre Hauptdatenbank und Ihre Remote-Datenbank "verknüpft" sind (wie in "sp_linkserver"), können Sie die Hauptdatenbank als "UUID-Generator" verwenden.

Du willst nicht "eins nach dem anderen" von uuid bekommen, das ist zu viel Geschwätz.

Aber du könntest dir ein paar UUIDs schnappen.

Unten ist ein Code:

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

* /

granadaCoder
quelle
Interessant - und Ansatz, über den ich nicht nachgedacht hatte - ich werde dies näher untersuchen, da dies schön aussieht und einige Testprojekte auf den Weg bringt. Wenn 150 Datenbanken sequentielle Guids generieren würden, die an die zentrale Datenbank zurückgemeldet werden, würde dies nicht immer noch eine Fragmentierung verursachen, da die Guids beim Einfügen in die zentrale Datenbank immer noch ziemlich zufällig wären. Es sei denn, Sie wollen die Cluster-PK löschen und die nicht-Cluster-PK haben?
Roddles
Fügen die 150 "entfernten" Datenbanken jeweils eine ein? Oder verschieben sie Daten nachts in großen Mengen oder so? Also bist du irgendwie zwischen einem Felsen und einem harten Ort. Wenn Sie bigint verwenden, wird irgendwann der Platz knapp (vielleicht) und Sie müssen immer noch einen eindeutigen Wert für die vielen DBs erzielen. Also hier ist meine radikale Idee. Können die 150 entfernten Datenbanken ihre UUIDs von einem zentralen Dienst beziehen? Das ist eine Idee. Sind die 150 entfernten Datenbanken (wie in sp_addlinkedserver) mit der Hauptdatenbank "verbunden"? Dann habe ich eine UDF, die in Betracht gezogen werden könnte. Lassen Sie mich sehen, ob ich es finden kann.
GranadaCoder
Hier ist ein Artikel , dass die Gespräche über sequentialid des (nicht im Zusammenhang zu dem, was ich bereits geschrieben habe, denke ich , seine interessante) codeproject.com/Articles/388157/...
granadaCoder
0

Gehen Sie gemäß Ihrer Beschreibung zu BIGINT. Der Index für GUID kann jedoch nicht eindeutig sein, da GUIDs ohnehin global eindeutig sein sollen.

Jimbo
quelle
-1

Wenn GUID korrekt als uniqueidentifier gespeichert ist, sollte es keine Performance-Probleme geben ... und wenn Sie Sequential GUID noch besser nutzen können ...

Auch @mattytommo hat einen guten Punkt über 11,5 Jahre mit der Verwendung von INT ...

Borik
quelle
Ja, aber die Guid wird in den entfernten 150 Datenbanken generiert, nicht in der SQL Server-Datenbank. Ich kann also keine sequentialguid verwenden, aber danke für die Antwort.
Roddles
In diesem Fall ist Ihr Plan meiner Meinung nach solide. Ich habe eine der von mir verwalteten Datenbanken auf ähnliche Weise bearbeitet. Ich habe eine INT DENTITY (1,1) erstellt und als Clustered PK sowie als humane lesbare Kennung für Daten festgelegt Nach oben ziehen und ich habe GUID (Index) als Tracker behalten, um verfolgen zu können, woher es stammt. Aber meine Motivation war mehr aus Platzersparnis ...
Borik
Vielen Dank und viel Dank für Ihre Antworten und Erkenntnisse. :)
Roddles