Når du vælger den primære nøgle, vælger du normalt også den grupperede nøgle. De to er ofte forvirrede, men du skal forstå forskellen.
Primære nøgler er logisk forretning elementer. Den primære nøgle bruges af din applikation til at identificere en entitet, og diskussionen om primærnøgler handler i høj grad om at bruge naturlige nøgler eller surrogatnøgler. Linkene går i meget flere detaljer, men den grundlæggende idé er, at naturlige nøgler er afledt af en eksisterende enhedsegenskab som ssn
eller phone number
, mens surrogatnøgler ikke har nogen som helst betydning med hensyn til forretningsenheden, såsom id
eller rowid
og de er normalt af typen IDENTITY
eller en slags uuid. Min personlige mening er, at surrogatnøgler er overlegne i forhold til naturlige nøgler, og valget bør altid være identitetsværdier for kun lokale applikationer, guider til enhver form for distribueret data. En primær nøgle ændres aldrig i enhedens levetid.
Klyngede nøgler er nøglen, der definerer den fysiske lagring af rækker i tabellen. De fleste gange overlapper de med den primære nøgle (den logiske enheds-id), men det er faktisk ikke håndhævet eller påkrævet. Når de to er forskellige, betyder det, at der er et ikke-klynget unikt indeks på bordet, der implementerer den primære nøgle. Klyngede nøgleværdier kan faktisk ændre sig i løbet af rækkens levetid, hvilket resulterer i, at rækken fysisk flyttes i tabellen til en ny placering. Hvis du skal adskille den primære nøgle fra den klyngede nøgle (og nogle gange gør du det), er det betydeligt sværere at vælge en god klynget nøgle end at vælge en primær nøgle. Der er to primære faktorer, der driver dit klyngede nøgledesign:
- Det udbredte dataadgangsmønster .
- De opbevaringsovervejelser .
Dataadgangsmønster . Herved forstår jeg den måde, tabellen er forespurgt og opdateret på. Husk, at grupperede nøgler bestemmer rækkefølgen af rækkerne i tabellen. For visse adgangsmønstre gør nogle layouts hele verden med hensyn til forespørgselshastighed eller opdatering af samtidighed:
-
aktuelle vs. arkivdata. I mange applikationer er data, der tilhører den aktuelle måned, ofte tilgået, mens den tidligere sjældent tilgås. I sådanne tilfælde bruger tabeldesignet tabelopdeling efter transaktionsdato, ofte ved hjælp af en glidende vinduesalgoritme. Den aktuelle måneds partition opbevares på filgruppen, der er placeret på en hurtig disk, de arkiverede gamle data flyttes til filgrupper, der hostes på billigere, men langsommere lager. I dette tilfælde er den grupperede nøgle (dato) naturligvis ikke den primære nøgle (transaktions-id). Adskillelsen af de to er drevet af skalakravene, da forespørgselsoptimeringsværktøjet vil være i stand til at registrere, at forespørgslerne kun er interesserede i den aktuelle partition og ikke engang ser på de historiske.
-
FIFO kø stil behandling. I dette tilfælde har bordet to hot spots:halen, hvor indsættelser forekommer (enqueue), og hovedet, hvor sletninger forekommer (dequeue). Den grupperede nøgle skal tage højde for dette og organisere tabellen, så den fysisk adskiller hale- og hovedplaceringen på disken, for at tillade samtidighed mellem enqueue og dequeue, f.eks. ved at bruge en købestillingsnøgle. I ren køer denne klyngenøgle er den eneste nøgle, da der ikke er nogen primær nøgle på bordet (den indeholder meddelelser , ikke enheder ). Men de fleste gange er køen ikke ren, den fungerer også som lager for enhederne og linjen mellem køen og tabellen er sløret. I dette tilfælde er der også en primær nøgle, som ikke kan være den klyngede nøgle:Entiteter kan sættes i kø igen, og dermed ændre den klyngede nøgleværdi i kørækkefølgen, men de kan ikke ændre den primære nøgleværdi. Manglende overblik over adskillelsen er den primære årsag til, at brugertabelstøttede køer er så notorisk svære at få rigtigt og fyldt med dødvande:fordi køen og dekøen forekommer sammenflettet gennem bordet, i stedet for lokaliseret ved køens hale og hoved.
-
Korreleret behandling. Når applikationen er godt designet, vil den opdele behandlingen af korrelerede elementer mellem dens arbejdstråde. For eksempel er en processor designet til at have 8 arbejdstråde (f.eks. at matche de 8 CPU'er på serveren), så processorerne opdeler dataene indbyrdes, f.eks. arbejder 1 henter kun konti med navnet A til E, arbejder 2 F til J osv. I sådanne tilfælde skal tabellen faktisk grupperes efter kontonavnet (eller med en sammensat nøgle, der har den længst venstre position det første bogstav i kontonavnet), så arbejderne lokaliserer deres forespørgsler og opdateringer i tabellen. Sådan et bord ville have 8 forskellige hot spots, omkring det område, hver medarbejder koncentrerer sig i øjeblikket, men det vigtige er, at de ikke overlapper hinanden (ingen blokering). Denne form for design er udbredt på OLTP-design med høj gennemstrømning og i TPCC-benchmark-belastninger, hvor denne form for partitionering også afspejler sig i hukommelsesplaceringen af siderne, der er indlæst i bufferpuljen (NUMA-lokalitet), men jeg går udenom.
Opbevaringsovervejelser . Den klyngede nøgle bredde har store konsekvenser i opbevaringen af bordet. For én optager nøglen plads på hver side uden blade i b-træet, så en stor nøgle vil optage mere plads. For det andet, og ofte vigtigere, er, at den klyngede nøgle bruges som opslagsnøgle af hver ikke-klyngede nøgle, så hver ikke-klyngede nøgle bliver nødt til at gemme den fulde bredde af den klyngede nøgle for hver række. Det er det, der gør store klyngenøgler som varchar(256) og vejleder dårlige valg for klyngede indeksnøgler.
Også valget af nøglen har indflydelse på den klyngede indeksfragmentering, hvilket nogle gange påvirker ydeevnen drastisk.
Disse to kræfter kan nogle gange være antagonistiske, idet dataadgangsmønsteret kræver en vis stor klyngenøgle, som vil forårsage lagringsproblemer. I sådanne tilfælde er det selvfølgelig nødvendigt med en balance, men der er ingen magisk formel. Du måler og tester for at komme til sweet spot.
Så hvad får vi ud af alt dette? Begynd altid med at overveje en klynget nøgle, der også er den primære nøgle i formen entity_id IDENTITY(1,1) NOT NULL
. Adskil de to og organiser tabellen i overensstemmelse hermed (f.eks. opdeling efter dato), når det er passende.