Indextabel-patroon

Maak en onderhoud afzonderlijke opzoektabellen voor gegevens die toepassingen vaak gebruiken in query's wanneer een gegevensarchief geen geschikte secundaire indexen biedt. Deze methode verbetert de leesprestaties door volledige gegevensscans te voorkomen wanneer query's geen primaire sleutel of partitiesleutel gebruiken.

Context en probleem

Veel gegevensarchieven organiseren de gegevens voor een verzameling entiteiten met behulp van de primaire sleutel. Een toepassing kan deze sleutel gebruiken om gegevens te zoeken en op te halen. In de volgende afbeelding ziet u een voorbeeld van een gegevensarchief met klantgegevens die zijn ingedeeld op basis van de primaire sleutel, klant-id.

Diagram met een tabel met klantgegevens die is geordend op basis van de primaire sleutel, klant-id. De kolom Klantgegevens bevat de achternamen en steden van de klant.

In de afbeelding ziet u een tabel met twee kolommen met klantrecords. De eerste kolom, primaire sleutel (klant-id), bevat klant-id's 1 tot en met 9, gevolgd door een beletselteken, id 1000 en nog een beletselteken. De tweede kolom, Klantgegevens, bevat klantgegevens die bestaan uit een achternaam en plaats, gevolgd door een beletselteken. Zichtbare voorbeelden zijn id 1, die is toegewezen aan de achternaam Smith en de stad Redmond, en id 2, die is toegewezen aan de achternaam Jones en de stad Seattle. Een andere rij toont Smith in Chicago en een andere rij toont Smith in Redmond. Nog een rij toont Jones in Chicago.

Hoewel de primaire sleutel waardevol is voor query's die gegevens ophalen op basis van de waarde van deze sleutel, kan een toepassing die gegevens moet ophalen op basis van een ander veld de primaire sleutel voor die query niet gebruiken. In het bovenstaande voorbeeld kan een toepassing de primaire sleutel Customer ID niet gebruiken voor het ophalen van klanten als in de query uitsluitend wordt verwezen naar de waarde van een ander kenmerk, zoals de plaats waarin de klant is gevestigd. Als u een query wilt uitvoeren die alleen naar de stad verwijst, moet de toepassing mogelijk elke klantrecord ophalen en onderzoeken. Dit kan een traag proces zijn.

Veel relationele databasebeheersystemen ondersteunen secundaire indexen. Een secundaire index is een afzonderlijke gegevensstructuur die is geordend op een of meer niet-primaire (secundaire) sleutelvelden. Een secundaire index geeft aan waar de gegevens voor elke geïndexeerde waarde worden opgeslagen. De items in een secundaire index worden meestal gesorteerd op de waarde van de secundaire sleutels om het snel opzoeken van gegevens mogelijk te maken. Het databasebeheersysteem onderhoudt deze indexen meestal automatisch.

Met relationele databases kunnen meerdere secundaire indexen verschillende querypatronen ondersteunen. In een tabel Klanten in een relationele database waarin de klant-id de primaire sleutel is, is het bijvoorbeeld handig om een secundaire index toe te voegen via het veld Plaats als de toepassing klanten regelmatig opzoekt door de plaats waar ze zich bevinden.

Hoewel secundaire indexen gebruikelijk zijn in relationele systemen, bieden niet alle gegevensarchieven een equivalente functie. Sommige gegevensarchieven hebben geen secundaire indexen, terwijl andere indexen bieden die niet voldoen aan de query-, partitionerings- of prestatievereisten van een workload. In deze gevallen moeten toepassingen kiezen tussen volledige scans en handmatig indexbeheer.

Solution

Maak een indextabel die de gegevens ordent op basis van een opgegeven sleutel. In de volgende sectie worden drie algemene strategieën beschreven voor het structureren van een indextabel. In de twee volgende secties worden toepassingen van indextabellen in specifieke scenario's beschreven.

Standaardstructureringsstrategieën

De volgende strategieën worden vaak gebruikt om een indextabel te structuren. Kies een strategie op basis van het aantal secundaire indexen dat uw scenario vereist en de aard van de query's die door uw toepassing worden uitgevoerd.

Volledige denormalisatie

Volledige denormalisatie dupliceert de gegevens in elke indextabel, maar ordent deze op andere sleutels dan de primaire sleutel. In de volgende afbeelding ziet u indextabellen die dezelfde klantgegevens ordenen op Town en LastName.

Diagram van indextabellen die klantgegevens dupliceren en ordenen op basis van de sleutels Town en LastName.

Deze strategie werkt goed voor leesintensieve werkbelastingen waarbij gegevens onregelmatig worden gewijzigd. Naarmate de updatesnelheid toeneemt, voegt het onderhouden van elke kopie verwerkingsoverhead toe (zie consistentiecomplexiteit). Voor gegevenssets met een groot volume kan het opslaan van de kopieën ook aanzienlijke ruimte vereisen.

Genormaliseerde index

Een genormaliseerde indextabel ordent gegevens op andere sleutels dan de primaire sleutel. De genormaliseerde indextabel verwijst naar de oorspronkelijke gegevens met behulp van de primaire sleutel in plaats van die gegevens te dupliceren, zoals wordt weergegeven in de volgende afbeelding. De oorspronkelijke gegevens staan in een zogenaamde feitentabel (Fact Table).

Tip

Hier betekent feitentabel de gezaghebbende brontabel waarnaar wordt verwezen door een index. Het impliceert geen dimensionaal gegevensmodel.

Diagram van genormaliseerde indextabellen die verwijzen naar een feitentabel per primaire sleutel in plaats van gegevens te dupliceren.

Deze techniek bespaart ruimte en vermindert de overhead die gepaard gaat met het onderhouden van dubbele gegevens. Het nadeel is dat een toepassing twee opzoekbewerkingen moet uitvoeren om gegevens te vinden met behulp van een secundaire sleutel. De toepassing moet de primaire sleutel voor de gegevens in de indextabel vinden en vervolgens de primaire sleutel gebruiken om de gegevens in de feitentabel op te zoeken.

Gedeeltelijke denormalisatie

Gedeeltelijke denormalisatie maakt indextabellen die vaak opgehaalde velden dupliceren en worden ingedeeld op andere sleutels dan de primaire sleutel. In de feitentabel wordt verwezen naar minder vaak gebruikte velden. In de volgende afbeelding ziet u hoe vaak geopende gegevens in elke indextabel worden gedupliceerd.

Diagram van gedeeltelijk genormaliseerde indextabellen die vaak gebruikte velden dupliceren en verwijzen naar een feitentabel voor resterende gegevens.

Met deze strategie worden de eerste twee benaderingen in balans. U kunt snel gegevens voor algemene query's ophalen met behulp van één opzoekactie, terwijl de overhead voor ruimte en onderhoud niet zo belangrijk is als het dupliceren van de hele gegevensset.

Samengestelde sleutels

Sommige toepassingen voeren vaak query's uit op gegevens door een combinatie van waarden op te geven, bijvoorbeeld 'Zoek alle klanten die in Redmond wonen en die een achternaam van Smith hebben'. In dit geval maakt u indexsleutels op basis van meerdere kenmerken, zoals de kenmerken Town en LastName.

Gebruik een codering die de grenzen van onderdelen behoudt, zodat verschillende waardecombinaties niet dezelfde sleutel kunnen produceren. Als query's afhankelijk zijn van de sleutelvolgorde, moet u er ook voor zorgen dat de codering de vereiste sorteervolgorde behoudt onder de sleutelsorteringsregels van het gegevensarchief.

In de volgende afbeelding ziet u een indextabel op basis van samengestelde sleutels. De sleutels worden gesorteerd op Town en vervolgens op LastName voor records met dezelfde waarde voor Town.

Diagram van een indextabel en een feitentabel. De indextabel is geordend op samengestelde sleutels die worden gevormd door het samenvoegen van de kenmerken Town en LastName.

Het diagram bevat een indextabel aan de linkerkant en een feitentabel aan de rechterkant. De feitentabel ordent klantgegevens op basis van de primaire sleutel, klant-id. Elke rij Klantgegevens bevat een achternaam, plaats en een beletselteken dat andere gegevens aangeeft. De indextabel is geordend op basis van een samengestelde sleutel waarin Town en LastName worden gecombineerd. De tweede kolom, Klantreferentie (ID) en veelgebruikte gegevens, bevat een klant-id en een beletselteken. Pijlen breiden zich uit van indextabelrijen naar de feitentabel, waarbij elke pijl verwijst naar de rij Klant-id waarnaar wordt verwezen door die indexvermelding. De kruispijlen laten zien dat vermeldingen die door de samengestelde sleutel zijn gerangschikt, kunnen verwijzen naar klantrijen op verschillende posities in de feitentabel.

Indextabellen over gesharde gegevens

Indextabellen kunnen query's op gesharde gegevens versnellen. Ze zijn vooral handig wanneer de shardsleutel is gehasht. In de volgende afbeelding ziet u een voorbeeld waarin de shardsleutel een hash is van de klant-id. De indextabel ordent vermeldingen op niet-gehashte waarden, Plaats en Achternaam en slaat de bijbehorende hash-shardsleutel op bij elke vermelding.

Deze rangschikking biedt ondersteuning voor bereik- en geordende zoekacties op de niet-hashwaarden, terwijl de routeringsgegevens worden opgegeven die nodig zijn om elke record op te halen uit de juiste shard. Een query zoals Alle klanten zoeken die in Redmond wonen, kan bijvoorbeeld de overeenkomende items vinden in een aaneengesloten blok in de indextabel. De toepassing volgt vervolgens de verwijzingen naar de klantgegevens door gebruik te maken van de shard-sleutels die zijn opgeslagen in de indextabel.

Diagram van shard-gegevenstabellen en een indextabel die snel zoeken naar shardgegevens biedt door niet-gehashte waarden toe te passen aan gehashte shardsleutels.

Problemen en overwegingen

Houd rekening met de volgende punten wanneer u besluit hoe u dit patroon implementeert:

  • Onderhoudsoverhead. Het onderhouden van secundaire indexen kan aanzienlijke overhead toevoegen. Analyseer en begrijp de query's die door uw toepassing worden gebruikt. Maak alleen indextabellen wanneer u ze waarschijnlijk regelmatig gebruikt. Maak geen speculatieve indextabellen ter ondersteuning van query's die een toepassing niet uitvoert of slechts af en toe uitvoert. Naarmate het aantal querypatronen groeit, neemt het aantal indextabellen toe en voegt elk oppervlak toe om het operationele oppervlak te bewaken, te onderhouden en fouten op te sporen. Weeg het queryvoordeel van elke indextabel af tegen de operationele kosten van het onderhouden van een andere afgeleide gegevensstructuur. Controleer regelmatig bestaande indextabellen om alle tabellen te identificeren en te verwijderen die niet meer worden opgevraagd.

  • Opslag- en doorvoerkosten. Het dupliceren van gegevens in een indextabel verhoogt de opslagkosten in verhouding tot het aantal indextabellen en de grootte van gekopieerde velden. Het onderhouden van meerdere kopieën van gegevens voegt ook inspanning toe. Elke schrijfbewerking naar een indextabel verbruikt doorvoercapaciteit, zoals transacties op basis van de limieten van het opslagaccount in Azure-tabelopslag of aanvraageenheden in Azure Cosmos DB. De kosten beperken zich niet tot opslag, maar omvatten ook schrijfdoorvoer.

  • Dubbele opzoekstraf. Het implementeren van een indextabel als een genormaliseerde structuur die verwijst naar de oorspronkelijke gegevens, betekent dat een toepassing twee opzoekbewerkingen moet uitvoeren om gegevens te vinden. Met de eerste bewerking wordt de indextabel doorzocht om de primaire sleutel op te halen, terwijl de tweede bewerking nodig is om met behulp van de primaire sleutel de gewenste gegevens op te halen.

  • Consistentiecomplexiteit. Als een systeem een aantal indextabellen over grote gegevenssets bevat, kan het lastig zijn om de consistentie tussen indextabellen en de oorspronkelijke gegevens te behouden. Als de brongegevens en indexvermeldingen niet in dezelfde transactie kunnen worden bijgewerkt, ontwerpt u de toepassing rond een uiteindelijke consistentiemodel. Leg elke bronwijziging duurzaam vast voordat u de schrijfbewerking bevestigt. Gebruik bijvoorbeeld een databasewijzigingsfeed, gebruik het Transactional Outbox-patroon om een outboxrecord te schrijven in dezelfde transactie als de bronwijziging, of plaats een opdracht in een wachtrij voordat u de brongegevens wijzigt. Gebruik in de op opdrachten gebaseerde benadering een werkrol om de opdracht te verwerken en de brongegevens en de bijbehorende indexen bij te werken. Werk de brongegevens niet bij en publiceer vervolgens onafhankelijk een bericht over indexupdates, omdat een fout tussen deze bewerkingen de index verouderd kan laten.

    Ontwerp de asynchrone verwerker zo dat deze idempotent is, omdat berichtaflevering en nieuwe pogingen ertoe kunnen leiden dat dezelfde update meer dan één keer kan worden uitgevoerd. Updates voor dezelfde bronrecord kunnen ook in een andere volgorde binnenkomen. Neem een bronversie of volgnummer op in elke indexupdate en pas een update alleen toe als deze nieuwer is dan de versie in de index. Behoud voor verwijderingen een versie van tombstone of een equivalent hoogwatermarkering, zodat een vertraagde oudere update de verwijderde indexvermelding niet opnieuw kan maken. Tijdens het interval tussen het schrijven van brongegevens en de asynchrone indexupdate kunnen query's op basis van de indextabel verouderde verwijzingen retourneren naar records die zijn bijgewerkt of verwijderd in de brongegevens, en kunnen ze onlangs toegevoegde records weglaten.

  • Indextabellen partitioneren. Indextabellen kunnen zelf ook worden gepartitioneerd of geshard, wat queryroutering complexer maakt en vereist dat de partitioneringsstrategie aansluit bij de querypatronen waarvoor de index is ontworpen.

Wanneer gebruikt u dit patroon?

Gebruik dit patroon wanneer een toepassing vaak gegevens moet ophalen met een andere sleutel dan de primaire sleutel (of shard) en het gegevensarchief geen systeemeigen ondersteuning biedt voor secundaire indexen of de bijbehorende systeemeigen indexen niet voldoen aan de query-, partitionerings- of prestatievereisten van de workload.

Dit patroon is mogelijk niet geschikt wanneer:

  • De gegevens zijn vluchtig. De gegevens worden zo vaak gewijzigd dat de schrijfsnelheid groter is dan de snelheid waarmee indextabellen asynchroon kunnen worden vernieuwd. Het verouderingsvenster groeit totdat de index permanent verouderd is, waardoor deze ineffectief is en de opslag- en doorvoeroverhead van het onderhouden van de indextabel groter is dan eventuele querybesparingen.

  • U hebt niet-discriminerende sleutels. Een veld dat als secundaire sleutel voor een indextabel is geselecteerd, is niet-discriminerend en kan slechts een kleine set waarden hebben (bijvoorbeeld een Booleaans veld dat registreert of een item actief is). De indextabel brengt volledige opslag- en doorvoerkosten in rekening, maar biedt minimale queryselectiviteit.

  • Gegevenswaarden hebben een scheve verdeling. Het saldo van de gegevenswaarden voor een veld dat als secundaire sleutel voor een indextabel is geselecteerd, is sterk scheefgetrokken. Als bijvoorbeeld 90 procent van de records dezelfde waarde in een veld bevat, kan het maken en onderhouden van een indextabel om gegevens op te zoeken op basis van dit veld mogelijk meer overhead opleveren dan het opeenvolgend scannen van de gegevens. Als query's echter vaak doelwaarden bevatten die in de resterende 10 procent liggen, kan deze index nog steeds nuttig zijn.

Ontwerp van werkbelasting

Een architect moet beoordelen hoe het indextabelpatroon kan worden gebruikt in het ontwerp van hun workload om invulling te geven aan de doelen en principes die aan bod komen in de pijlers van het Azure Well-Architected Framework. De volgende tabel bevat richtlijnen over hoe dit patroon de doelstellingen van elke pijler ondersteunt.

Pilaar Hoe dit patroon ondersteuning biedt voor pijlerdoelen
Betrouwbaarheid helpt uw workload te voldoen aan de tolerantie- en hersteldoelen door redundantie te bouwen en functionaliteit te behouden tijdens storingen. Asynchroon indexonderhoud kan voorkomen dat tijdelijke fouten bij het bijwerken van indexen het schrijven van brongegevens blokkeren. Idempotente verwerking, verwerking van dode letters, bewaking en afstemming helpen bij het herstellen van indexconsistentie na fouten.

- RE:07 Zelfbehoud
- RE:10 Monitoring
Prestatie-efficiëntie helpt uw workload efficiënt te voldoen aan de vereisten door middel van optimalisaties in schalen, gegevens en code. Indextabellen helpen snelle zoekacties in te schakelen op niet-primaire sleutelvelden zonder dat er volledige gegevensscans nodig zijn. Voor gesharde gegevensopslag kunnen indextabellen items ordenen op niet-gehashte waarden om bereik- en sorteerquery’s te ondersteunen die met alleen de shard-sleutel niet efficiënt kunnen worden uitgevoerd.

- PE:05 Schaalbaarheid en partitioneren
- PE:08 Gegevensprestaties

Net als bij elke ontwerpbeslissing, als dit patroon compromissen introduceert binnen een pijler, kunt u deze overwegen tegen de doelstellingen van de andere pijlers.

Example

Overweeg een toepassing waarin informatie over films wordt opgeslagen. Stel dat de catalogus groot is en veel wordt geraadpleegd, dat elke film één primair genre heeft, dat query's naar acteurs vaak voorkomen, dat castgegevens zelden veranderen en dat een korte vertraging in de indexering acceptabel is. In Table Storage wordt elke entiteit opgeslagen als een gestructureerde set benoemde eigenschappen. Elke entiteit bevat een PartitionKey, een RowKeyen een tijdstempel en entiteiten in dezelfde tabel kunnen verschillende sets eigenschappen hebben.

Table Storage gebruikt een samengestelde primaire sleutel die bestaat uit PartitionKey en RowKey. De PartitionKey waarde bepaalt de partitie waarin een entiteit wordt opgeslagen. Binnen een partitie identificeert de RowKey waarde een entiteit op unieke wijze. Table Storage is geoptimaliseerd voor query's die beide sleutels opgeven of een aaneengesloten bereik met rijsleutelwaarden binnen één partitie ophalen.

Tip

Table Storage ondersteunt transactionele updates voor entiteiten in dezelfde tabel en partitie via entiteitsgroeptransacties. Een transactie kan geen feitentabel en een afzonderlijke indextabel omvatten. Als u een feitenentiteit en indexentiteiten atomisch wilt bijwerken, slaat u deze op in dezelfde tabel met dezelfde PartitionKey. Entiteitsgroeptransacties zijn beperkt tot 100 entiteiten per batch met een maximale nettolading van 4 MiB.

Maak voor dit voorbeeld een Azure tabel met partities voor elk genre met behulp van een gecodeerde genre-id als de partitiesleutel en een stabiele, unieke film-id als de rijsleutel. Sla de genre- en filmnamen op als eigenschappen. In de volgende afbeelding worden leesbare namen gebruikt in plaats van id's om het voorbeeld gemakkelijker te volgen.

Diagram van filmgegevens in Azure tabelpartities. Leesbare genre- en filmnamen vertegenwoordigen gecodeerde genrepartitiesleutels en unieke filmrijsleutels.

Deze aanpak is minder effectief wanneer de toepassing ook films moet opvragen op hoofdrolspeler. Maak in dat geval een afzonderlijke Azure tabel die fungeert als een indextabel. Gebruik een gecodeerde, stabiele actor-id als de partitiesleutel en de film-id als de rijsleutel. Sla actor- en filmnamen op als eigenschappen. In de volgende afbeelding worden leesbare namen gebruikt in plaats van id's. Als een film meer dan één acteur in de hoofdrol heeft, komt diezelfde film in meerdere partities voor.

De volgende figuur toont de actorindextabel.

Diagram van actorpartities die fungeren als indextabellen die filmgegevens dupliceren, acteurs als partitiesleutels gebruiken en films gebruiken als rijsleutels.

Rondleiding door het ontwerp

De filmtabel maakt gebruik van genre als partitiesleutel, wat betekent dat query's die op genre worden gefilterd efficiënt worden uitgevoerd als partitiescans via aaneengesloten rijsleutelbereiken. Table Storage ondersteunt echter slechts één geclusterde index op PartitionKey en RowKey. Er zijn geen secundaire indexen. Voor een query zoals 'alle films zoeken met een specifieke actor' is een volledige tabelscan vereist voor elke genrepartitie, wat duur is op schaal.

De actorindextabel lost deze beperking op door het toegangspatroon om te draaien. Elke actor-id wordt een partitiesleutel en elke film-id wordt een rijsleutel, zodat op actor gebaseerde query's worden omgezet als efficiënte partitiezoekacties. Omdat elke partitie slechts één actorfilm bevat, retourneert de query een aaneengesloten reeks entiteiten zonder niet-gerelateerde gegevens te scannen.

Omdat film- en actorvermeldingen afzonderlijke tabellen en partitiesleutels gebruiken, kunnen deze vermeldingen geen entiteitsgroeptransactie delen. Houd de actor-index bij via een duurzaam, asynchroon mechanisme voor het vastleggen van wijzigingen, en ontwerp de toepassing zo dat deze tijdelijk verouderde queryresultaten kan tolereren.

De indextabel past gedeeltelijke denormalisatie toe: elke vermelding dupliceert veelgebruikte velden (zoals de namen van andere actoren), zodat de meest voorkomende zoekopdrachten uitsluitend met de indextabel en met één zoekopdracht kunnen worden beantwoord. Voor minder vaak geraadpleegde velden bevat de vermelding de genre-partitiesleutel uit de oorspronkelijke filmtabel, waardoor een gerichte puntquery op de genrepartitie mogelijk wordt om het volledige record op te halen. Dit ontwerp zorgt voor een balans tussen querysnelheid en opslagkosten en onderhoudsoverhead.

Volgende stappen 

De volgende patronen zijn mogelijk ook relevant wanneer u dit patroon implementeert:

  • Sharding-patroon. Het patroon Indextabel wordt vaak gebruikt in combinatie met gegevens die zijn gepartitioneerd met behulp van shards. In het Sharding-patroon wordt beschreven hoe u een gegevensarchief in een set shards deelt.

  • Patroon voor gematerialiseerde weergaven. In plaats van gegevens te indexeren ter ondersteuning van query's die gegevens samenvatten, is het beter om een gerealiseerde weergave van de gegevens te maken. In dit patroon wordt beschreven hoe u vooraf ingevulde weergaven over gegevens genereert ter ondersteuning van efficiënte samenvattingsquery's.

  • Transactioneel outbox-patroon. Gebruik het patroon Transactional Outbox om wijzigingen voor asynchroon indexonderhoud betrouwbaar te publiceren wanneer brongegevens en indexvermeldingen niet in één transactie kunnen worden bijgewerkt.