Bij het kiezen van de primaire sleutel kiest u meestal ook voor de geclusterde sleutel. Die twee worden vaak verward, maar je moet het verschil begrijpen.
Primaire sleutels zijn logisch zakelijk elementen. De primaire sleutel wordt door uw toepassing gebruikt om een entiteit te identificeren, en de discussie over primaire sleutels gaat grotendeels over het gebruik van natuurlijke sleutels of surrogaatsleutels. De links gaan veel gedetailleerder in, maar het basisidee is dat natuurlijke sleutels zijn afgeleid van een bestaande entiteitseigenschap zoals ssn
of phone number
, terwijl surrogaatsleutels geen enkele betekenis hebben met betrekking tot de zakelijke entiteit, zoals id
of rowid
en ze zijn meestal van het type IDENTITY
of een soort van uuid. Mijn persoonlijke mening is dat surrogaatsleutels superieur zijn aan natuurlijke sleutels, en de keuze zou altijd identiteitswaarden moeten zijn voor alleen lokale toepassingen, richtlijnen voor alle soorten gedistribueerde gegevens. Een primaire sleutel verandert nooit tijdens de levensduur van de entiteit.
Geclusterde sleutels zijn de sleutel die de fysieke opslag van rijen in de tabel definieert. Meestal overlappen ze met de primaire sleutel (de logische entiteit-ID), maar dat wordt niet echt afgedwongen en ook niet vereist. Als de twee verschillend zijn, betekent dit dat er een niet-geclusterde unieke index op de tabel staat die de primaire sleutel implementeert. Geclusterde sleutelwaarden kunnen tijdens de levensduur van de rij veranderen, waardoor de rij fysiek in de tabel naar een nieuwe locatie wordt verplaatst. Als u de primaire sleutel van de geclusterde sleutel moet scheiden (en soms doet u dat), is het kiezen van een goede geclusterde sleutel aanzienlijk moeilijker dan het kiezen van een primaire sleutel. Er zijn twee belangrijke factoren die uw geclusterde sleutelontwerp bepalen:
- Het meest voorkomende gegevenstoegangspatroon .
- De overwegingen voor opslag .
Gegevenstoegangspatroon . Hierdoor begrijp ik de manier waarop de tabel wordt opgevraagd en bijgewerkt. Onthoud dat geclusterde sleutels de werkelijke volgorde van de rijen in de tabel bepalen. Voor bepaalde toegangspatronen maken sommige lay-outs het verschil in de wereld met betrekking tot de querysnelheid of de gelijktijdigheid van updates:
-
huidige versus archiefgegevens. In veel applicaties worden de gegevens van de huidige maand vaak geraadpleegd, terwijl de gegevens in het verleden zelden worden geraadpleegd. In dergelijke gevallen maakt het tafelontwerp gebruik van tabelpartitionering op transactiedatum, vaak met behulp van een glijdend vensteralgoritme. De partitie van de huidige maand wordt bewaard op de bestandsgroep die zich op een hete snelle schijf bevindt, de gearchiveerde oude gegevens worden verplaatst naar bestandsgroepen die worden gehost op goedkopere maar langzamere opslag. Uiteraard is in dit geval de geclusterde sleutel (datum) niet de primaire sleutel (transactie-ID). De scheiding van de twee wordt aangedreven door de schaalvereisten, aangezien de query-optimizer kan detecteren dat de query's alleen geïnteresseerd zijn in de huidige partitie en niet eens naar de historische partities kijken.
-
Verwerking in FIFO-wachtrijstijl. In dit geval heeft de tabel twee hotspots:de staart waar inserts plaatsvinden (enqueue) en de kop waar deletes plaatsvinden (dequeue). De geclusterde sleutel moet hiermee rekening houden en de tabel zodanig organiseren dat de staart- en koplocatie op schijf fysiek worden gescheiden, om gelijktijdigheid tussen wachtrij en dequeue mogelijk te maken, bijv. door gebruik te maken van een enqueue order key. In puur wachtrijen deze geclusterde sleutel is de enige sleutel, aangezien er geen primaire sleutel in de tabel staat (deze bevat berichten , niet entiteiten ). Maar meestal is de wachtrij niet zuiver, het fungeert ook als de opslag voor de entiteiten en de lijn tussen de wachtrij en de tafel is vervaagd. In dit geval is er ook een primaire sleutel, die niet de geclusterde sleutel kan zijn:entiteiten kunnen opnieuw in de wachtrij worden geplaatst, waardoor de geclusterde sleutelwaarde van de wachtrijvolgorde wordt gewijzigd, maar ze kunnen de primaire sleutelwaarde niet wijzigen. Het niet zien van de scheiding is de belangrijkste reden waarom wachtrijen die door gebruikerstafels worden ondersteund zo notoir moeilijk zijn om goed te krijgen en vol met impasses:omdat de wachtrij en de wachtrij doorschoten door de tafel plaatsvinden, in plaats van gelokaliseerd aan de staart en de kop van de wachtrij.
-
Gecorreleerde verwerking. Wanneer de toepassing goed is ontworpen, zal deze de verwerking van gecorreleerde items tussen de werkthreads verdelen. Een processor is bijvoorbeeld ontworpen om 8 werkthreads te hebben (bijvoorbeeld om overeen te komen met de 8 CPU's op de server), zodat de processors de gegevens onderling verdelen, bijv. werknemer 1 pikt alleen accounts op met de naam A tot E, werknemer 2 F tot J enz. In dergelijke gevallen moet de tabel eigenlijk worden geclusterd op de accountnaam (of op een samengestelde sleutel die de eerste letter van de accountnaam op de meest linkse positie heeft), zodat werknemers hun vragen en updates in de tabel kunnen lokaliseren. Zo'n tafel zou 8 verschillende hotspots hebben, rond het gebied waar elke werknemer zich momenteel op concentreert, maar het belangrijkste is dat ze elkaar niet overlappen (geen blokkering). Dit soort ontwerp komt veel voor bij OLTP-ontwerpen met hoge doorvoer en in TPCC-benchmarkbelastingen, waar dit soort partitionering ook wordt weerspiegeld in de geheugenlocatie van de pagina's die in de bufferpool zijn geladen (NUMA-lokaliteit), maar ik dwaal af.
Opslagoverwegingen . De geclusterde sleutel breedte heeft enorme gevolgen voor de opslag van de tafel. Ten eerste neemt de sleutel ruimte in op elke niet-bladpagina van de b-tree, dus een grote sleutel zal meer ruimte innemen. Ten tweede, en vaak belangrijker, is dat de geclusterde sleutel wordt gebruikt als de opzoeksleutel door elke niet-geclusterde sleutel, dus elke niet-geclusterde sleutel moet de volledige breedte van de geclusterde sleutel voor elke rij opslaan. Dit is wat grote geclusterde sleutels zoals varchar(256) maakt en slechte keuzes maakt voor geclusterde indexsleutels.
Ook de keuze van de sleutel heeft invloed op de geclusterde indexfragmentatie, wat soms de prestaties drastisch beïnvloedt.
Deze twee krachten kunnen soms tegenstrijdig zijn, omdat het datatoegangspatroon een bepaalde grote geclusterde sleutel vereist, wat opslagproblemen zal veroorzaken. In dergelijke gevallen is natuurlijk een balans nodig, maar er is geen magische formule. Je meet en test om tot de goede plek te komen.
Dus wat maken we van dit alles? Begin altijd met het overwegen van een geclusterde sleutel die ook de primaire sleutel is van de vorm entity_id IDENTITY(1,1) NOT NULL
. Scheid de twee en organiseer de tabel dienovereenkomstig (bijv. partitie op datum) indien van toepassing.