Dela via


Datamodellering i Azure Cosmos DB

GÄLLER FÖR: NoSQL

Även om schemafria databaser, som Azure Cosmos DB, gör det superlätt att lagra och köra frågor mot ostrukturerade och halvstrukturerade data bör du ägna lite tid åt att tänka på din datamodell för att få ut mesta möjliga av tjänsten när det gäller prestanda och skalbarhet och lägsta kostnad.

Hur kommer data att lagras? Hur ska programmet hämta och köra frågor mot data? Är programmet läsintensivt eller skrivintensivt?

När du har läst den här artikeln kan du svara på följande frågor:

  • Vad är datamodellering och varför ska jag bry mig?
  • Hur skiljer sig modellering av data i Azure Cosmos DB från en relationsdatabas?
  • Hur gör jag för att expressdatarelationer i en icke-relationell databas?
  • När bäddar jag in data och när länkar jag till data?

Tal i JSON

Azure Cosmos DB sparar dokument i JSON. Vilket innebär att det är nödvändigt att noggrant avgöra om det är nödvändigt att konvertera tal till strängar innan de lagras i json eller inte. Alla tal bör helst konverteras till en String, om det finns någon chans att de ligger utanför gränserna för dubbla precisionsnummer enligt IEEE 754 binary64. Json-specifikationen anger orsakerna till varför användning av siffror utanför den här gränsen i allmänhet är en dålig praxis i JSON på grund av sannolika samverkansproblem. Dessa problem är särskilt relevanta för kolumnen partitionsnyckel eftersom den är oföränderlig och kräver datamigrering för att ändra den senare.

Bädda in data

När du börjar modellera data i Azure Cosmos DB försöker du behandla dina entiteter som fristående objekt som representeras som JSON-dokument.

Som jämförelse ska vi först se hur vi kan modellera data i en relationsdatabas. I följande exempel visas hur en person kan lagras i en relationsdatabas.

Relationsdatabasmodell

Strategin när du arbetar med relationsdatabaser är att normalisera alla dina data. Normalisering av dina data innebär vanligtvis att du tar en entitet, till exempel en person, och delar upp dem i diskreta komponenter. I exemplet kan en person ha flera kontaktinformationsposter och flera adressposter. Kontaktuppgifter kan delas upp ytterligare genom att ytterligare extrahera vanliga fält som en typ. Samma sak gäller för adress, varje post kan vara av typen Start eller Företag.

Den vägledande premissen när du normaliserar data är att undvika att lagra redundanta data på varje post och i stället referera till data. I det här exemplet måste du använda JOINS för att effektivt skriva tillbaka (eller avnormalisera) dina data vid körning för att kunna läsa en person med alla deras kontaktuppgifter och adresser.

SELECT p.FirstName, p.LastName, a.City, cd.Detail
FROM Person p
JOIN ContactDetail cd ON cd.PersonId = p.Id
JOIN ContactDetailType cdt ON cdt.Id = cd.TypeId
JOIN Address a ON a.PersonId = p.Id

Skrivåtgärder i många enskilda tabeller krävs för att uppdatera en enskild persons kontaktuppgifter och adresser.

Nu ska vi ta en titt på hur vi skulle modellera samma data som en fristående entitet i Azure Cosmos DB.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "addresses": [
        {
            "line1": "100 Some Street",
            "line2": "Unit 1",
            "city": "Seattle",
            "state": "WA",
            "zip": 98012
        }
    ],
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555", "extension": 5555}
    ]
}

Med den här metoden har vi avnormaliserat personposten genom att bädda in all information som är relaterad till den här personen, till exempel deras kontaktuppgifter och adresser, i ett enda JSON-dokument . Eftersom vi inte är begränsade till ett fast schema har vi dessutom flexibiliteten att göra saker som att ha kontaktuppgifter för olika former helt och hållet.

Att hämta en fullständig personpost från databasen är nu en enda läsåtgärd mot en enda container och för ett enda objekt. Att uppdatera kontaktinformationen och adresserna för en personpost är också en enda skrivåtgärd mot ett enda objekt.

Genom att avnormalisera data kan programmet behöva utfärda färre frågor och uppdateringar för att slutföra vanliga åtgärder.

När du ska bädda in

I allmänhet använder du inbäddade datamodeller när:

  • Det finns inneslutna relationer mellan entiteter.
  • Det finns en-till-få-relationer mellan entiteter.
  • Det finns inbäddade data som ändras sällan.
  • Det finns inbäddade data som inte växer utan bindning.
  • Det finns inbäddade data som efterfrågas ofta tillsammans.

Kommentar

Vanligtvis ger avnormaliserade datamodeller bättre läsprestanda .

När du inte ska bädda in

Tumregeln i Azure Cosmos DB är att avnormalisera allt och bädda in alla data i ett enda objekt, men detta kan leda till vissa situationer som bör undvikas.

Ta det här JSON-kodfragmentet.

{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "comments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        …
        {"id": 100001, "author": "jane", "comment": "and on we go ..."},
        …
        {"id": 1000000001, "author": "angry", "comment": "blah angry blah angry"},
        …
        {"id": ∞ + 1, "author": "bored", "comment": "oh man, will this ever end?"},
    ]
}

Det kan vara så här en postentitet med inbäddade kommentarer skulle se ut om vi modellerade ett typiskt blogg- eller CMS-system. Problemet med det här exemplet är att kommentarsmatrisen är obundna, vilket innebär att det inte finns någon (praktisk) gräns för antalet kommentarer som ett enskilt inlägg kan ha. Detta kan bli ett problem eftersom objektets storlek kan bli oändligt stor, så är en design som du bör undvika.

När objektets storlek växer påverkas möjligheten att överföra data över tråden och läsa och uppdatera objektet i stor skala.

I det här fallet är det bättre att tänka på följande datamodell.

Post item:
{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "recentComments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        {"id": 3, "author": "jane", "comment": "....."}
    ]
}

Comment items:
[
    {"id": 4, "postId": "1", "author": "anon", "comment": "more goodness"},
    {"id": 5, "postId": "1", "author": "bob", "comment": "tails from the field"},
    ...
    {"id": 99, "postId": "1", "author": "angry", "comment": "blah angry blah angry"},
    {"id": 100, "postId": "2", "author": "anon", "comment": "yet more"},
    ...
    {"id": 199, "postId": "2", "author": "bored", "comment": "will this ever end?"}   
]

Den här modellen har ett dokument för varje kommentar med en egenskap som innehåller postidentifieraren. Detta gör att inlägg kan innehålla valfritt antal kommentarer och kan växa effektivt. Användare som vill se mer än de senaste kommentarerna frågar den här containern och skickar postId, som bör vara partitionsnyckeln för kommentarscontainern.

Ett annat fall där inbäddning av data inte är en bra idé är när inbäddade data ofta används i objekt och ändras ofta.

Ta det här JSON-kodfragmentet.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        {
            "numberHeld": 100,
            "stock": { "symbol": "zbzb", "open": 1, "high": 2, "low": 0.5 }
        },
        {
            "numberHeld": 50,
            "stock": { "symbol": "xcxc", "open": 89, "high": 93.24, "low": 88.87 }
        }
    ]
}

Detta kan representera en persons aktieportfölj. Vi har valt att bädda in aktieinformationen i varje portföljdokument. I en miljö där relaterade data ändras ofta, som ett aktiehandelsprogram, innebär inbäddning av data som ändras ofta att du ständigt uppdaterar varje portföljdokument varje gång en aktie handlas.

Lager zbzb kan handlas många hundratals gånger på en enda dag och tusentals användare kan ha zbzb på sin portfölj. Med en datamodell som exemplet skulle vi behöva uppdatera tusentals portföljdokument många gånger varje dag, vilket leder till ett system som inte skalar bra.

Referensdata

Inbäddning av data fungerar bra i många fall, men det finns scenarier när avnormalisering av dina data orsakar fler problem än det är värt. Så vad gör vi nu?

Relationsdatabaser är inte den enda platsen där du kan skapa relationer mellan entiteter. I en dokumentdatabas kan du ha information i ett dokument som relaterar till data i andra dokument. Vi rekommenderar inte att du skapar system som passar bättre för en relationsdatabas i Azure Cosmos DB eller någon annan dokumentdatabas, men enkla relationer är bra och kan vara användbara.

I JSON valde vi att använda exemplet på en aktieportfölj från tidigare men den här gången refererar vi till aktieposten i portföljen i stället för att bädda in den. På så sätt, när lagerartikeln ändras ofta under dagen, är det enda dokument som behöver uppdateras det enskilda lagerdokumentet.

Person document:
{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        { "numberHeld":  100, "stockId": 1},
        { "numberHeld":  50, "stockId": 2}
    ]
}

Stock documents:
{
    "id": "1",
    "symbol": "zbzb",
    "open": 1,
    "high": 2,
    "low": 0.5,
    "vol": 11970000,
    "mkt-cap": 42000000,
    "pe": 5.89
},
{
    "id": "2",
    "symbol": "xcxc",
    "open": 89,
    "high": 93.24,
    "low": 88.87,
    "vol": 2970200,
    "mkt-cap": 1005000,
    "pe": 75.82
}

En omedelbar nackdel med den här metoden är dock om ditt program krävs för att visa information om varje aktie som lagras när en persons portfölj visas. I det här fallet skulle du behöva göra flera resor till databasen för att läsa in informationen för varje lagerdokument. Här har vi tagit ett beslut om att förbättra effektiviteten för skrivåtgärder, vilket sker ofta under dagen, men i sin tur komprometterat läsåtgärderna som potentiellt har mindre inverkan på prestanda för det här systemet.

Kommentar

Normaliserade datamodeller kan kräva fler turer till servern.

Hur är det med främmande nycklar?

Eftersom det för närvarande inte finns något begrepp om en begränsning, sekundärnyckel eller på annat sätt, är alla relationer mellan dokument som du har i dokument i praktiken "svaga länkar" och verifieras inte av själva databasen. Om du vill se till att de data som ett dokument refererar till faktiskt finns måste du göra detta i ditt program, eller genom att använda utlösare på serversidan eller lagrade procedurer i Azure Cosmos DB.

När du ska referera till

I allmänhet använder du normaliserade datamodeller när:

  • Representerar en-till-många-relationer .
  • Representerar många-till-många-relationer .
  • Relaterade data ändras ofta.
  • Refererade data kan vara obundna.

Kommentar

Normalisering ger normalt bättre skrivprestanda .

Var placerar jag relationen?

Relationens tillväxt hjälper dig att avgöra i vilket dokument referensen ska lagras.

Om vi observerar JSON som modellerar utgivare och böcker.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press",
    "books": [ 1, 2, 3, ..., 100, ..., 1000]
}

Book documents:
{"id": "1", "name": "Azure Cosmos DB 101" }
{"id": "2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "3", "name": "Taking over the world one JSON doc at a time" }
...
{"id": "100", "name": "Learn about Azure Cosmos DB" }
...
{"id": "1000", "name": "Deep Dive into Azure Cosmos DB" }

Om antalet böcker per utgivare är litet med begränsad tillväxt kan det vara användbart att lagra bokreferensen i utgivardokumentet. Men om antalet böcker per utgivare är obundet skulle den här datamodellen leda till föränderliga, växande matriser, som i exempelutgivardokumentet.

Att växla runt lite skulle resultera i en modell som fortfarande representerar samma data men nu undviker dessa stora föränderliga samlingar.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press"
}

Book documents:
{"id": "1","name": "Azure Cosmos DB 101", "pub-id": "mspress"}
{"id": "2","name": "Azure Cosmos DB for RDBMS Users", "pub-id": "mspress"}
{"id": "3","name": "Taking over the world one JSON doc at a time", "pub-id": "mspress"}
...
{"id": "100","name": "Learn about Azure Cosmos DB", "pub-id": "mspress"}
...
{"id": "1000","name": "Deep Dive into Azure Cosmos DB", "pub-id": "mspress"}

I det här exemplet har vi tagit bort den obundna samlingen i utgivardokumentet. I stället har vi bara en referens till utgivaren för varje bokdokument.

Hur gör jag för att modellera många-till-många-relationer?

I en relationsdatabas modelleras många-till-många-relationer ofta med kopplingstabeller, som bara kopplar poster från andra tabeller tillsammans.

Koppla tabeller

Du kan vara frestad att replikera samma sak med hjälp av dokument och skapa en datamodell som liknar följande.

Author documents:
{"id": "a1", "name": "Thomas Andersen" }
{"id": "a2", "name": "William Wakefield" }

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101" }
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "b3", "name": "Taking over the world one JSON doc at a time" }
{"id": "b4", "name": "Learn about Azure Cosmos DB" }
{"id": "b5", "name": "Deep Dive into Azure Cosmos DB" }

Joining documents:
{"authorId": "a1", "bookId": "b1" }
{"authorId": "a2", "bookId": "b1" }
{"authorId": "a1", "bookId": "b2" }
{"authorId": "a1", "bookId": "b3" }

Det här skulle fungera. Att läsa in en författare med sina böcker eller läsa in en bok med författaren kräver dock alltid minst två extra frågor mot databasen. En fråga till det anslutna dokumentet och sedan en annan fråga för att hämta det faktiska dokumentet som ansluts.

Om den här kopplingen bara sammanfogar två datadelar, varför inte släppa den helt? Betänk följande exempel.

Author documents:
{"id": "a1", "name": "Thomas Andersen", "books": ["b1", "b2", "b3"]}
{"id": "a2", "name": "William Wakefield", "books": ["b1", "b4"]}

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101", "authors": ["a1", "a2"]}
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users", "authors": ["a1"]}
{"id": "b3", "name": "Learn about Azure Cosmos DB", "authors": ["a1"]}
{"id": "b4", "name": "Deep Dive into Azure Cosmos DB", "authors": ["a2"]}

Nu, om jag hade en författare, jag omedelbart vet vilka böcker de har skrivit, och omvänt om jag hade en bok dokument läst jag skulle känna till ID:t för författarna. Detta sparar den mellanliggande frågan mot kopplingstabellen, vilket minskar antalet serverresor som programmet måste göra.

Hybriddatamodeller

Vi har nu tittat på att bädda in (eller avnormalisera) och referera till (eller normalisera) data. Varje metod har fördelar och kompromisser.

Det behöver inte alltid vara antingen eller, var inte rädd för att blanda ihop saker lite.

Baserat på programmets specifika användningsmönster och arbetsbelastningar kan det finnas fall där blandning av inbäddade och refererade data är meningsfullt och kan leda till enklare programlogik med färre turer runt servern samtidigt som en bra prestandanivå bibehålls.

Överväg följande JSON.

Author documents:
{
    "id": "a1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "countOfBooks": 3,
    "books": ["b1", "b2", "b3"],
    "images": [
        {"thumbnail": "https://....png"}
        {"profile": "https://....png"}
        {"large": "https://....png"}
    ]
},
{
    "id": "a2",
    "firstName": "William",
    "lastName": "Wakefield",
    "countOfBooks": 1,
    "books": ["b1"],
    "images": [
        {"thumbnail": "https://....png"}
    ]
}

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
        {"id": "a2", "name": "William Wakefield", "thumbnailUrl": "https://....png"}
    ]
},
{
    "id": "b2",
    "name": "Azure Cosmos DB for RDBMS Users",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
    ]
}

Här har vi (mestadels) följt den inbäddade modellen, där data från andra entiteter är inbäddade i dokumentet på den översta nivån, men andra data refereras till.

Om du tittar på bokdokumentet kan vi se några intressanta fält när vi tittar på matrisen med författare. Det finns ett id fält som är det fält som vi använder för att referera tillbaka till ett redigeringsdokument, standardpraxis i en normaliserad modell, men sedan har name vi också och thumbnailUrl. Vi kunde ha fastnat med id och lämnat programmet för att få ytterligare information som behövs från respektive redigeringsdokument med hjälp av "länken", men eftersom vårt program visar författarens namn och en miniatyrbild med varje bok som visas kan vi spara en rundtur till servern per bok i en lista genom att avnormalisera vissa data från författaren.

Visst, om författarens namn ändrades eller om de ville uppdatera sitt foto skulle vi behöva uppdatera varje bok de någonsin publicerade men för vårt program, baserat på antagandet att författare inte ändrar sina namn ofta, är detta ett acceptabelt designbeslut.

I exemplet finns det förberäknade aggregeringsvärden för att spara dyr bearbetning på en läsåtgärd. I exemplet är en del av de data som är inbäddade i redigeringsdokumentet data som beräknas vid körning. Varje gång en ny bok publiceras skapas ett bokdokument och fältet countOfBooks anges till ett beräknat värde baserat på antalet bokdokument som finns för en viss författare. Den här optimeringen skulle vara bra i läsintensiva system där vi har råd att göra beräkningar på skrivningar för att optimera läsningar.

Möjligheten att ha en modell med förberäknade fält är möjlig eftersom Azure Cosmos DB stöder transaktioner med flera dokument. Många NoSQL-butiker kan inte göra transaktioner mellan dokument och förespråkar därför designbeslut, till exempel "alltid bädda in allt", på grund av den här begränsningen. Med Azure Cosmos DB kan du använda utlösare på serversidan eller lagrade procedurer som infogar böcker och uppdaterar författare i en ACID-transaktion. Nu behöver du inte bädda in allt i ett dokument bara för att vara säker på att dina data förblir konsekventa.

Skilja mellan olika dokumenttyper

I vissa scenarier kanske du vill blanda olika dokumenttyper i samma samling. Detta är vanligtvis fallet när du vill att flera relaterade dokument ska sitta i samma partition. Du kan till exempel placera både böcker och bokrecensioner i samma samling och partitioneras med bookId. I sådana fall vill du vanligtvis lägga till i dina dokument med ett fält som identifierar deras typ för att särskilja dem.

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "bookId": "b1",
    "type": "book"
}

Review documents:
{
    "id": "r1",
    "content": "This book is awesome",
    "bookId": "b1",
    "type": "review"
},
{
    "id": "r2",
    "content": "Best book ever!",
    "bookId": "b1",
    "type": "review"
}

Azure Synapse Link för Azure Cosmos DB är en molnbaserad HTAP-funktion (hybridtransaktions- och analysbearbetning) som gör att du kan köra analyser i nära realtid över driftdata i Azure Cosmos DB. Azure Synapse Link skapar en sömlös integrering mellan Azure Cosmos DB och Azure Synapse Analytics.

Den här integreringen sker via Azure Cosmos DB-analysarkivet, en kolumnrepresentation av dina transaktionsdata som möjliggör storskalig analys utan att påverka dina transaktionsarbetsbelastningar. Det här analysarkivet är lämpligt för snabba och kostnadseffektiva frågor på stora driftdatauppsättningar, utan att kopiera data och påverka prestandan för dina transaktionsarbetsbelastningar. När du skapar en container med analysarkiv aktiverat, eller när du aktiverar analysarkiv i en befintlig container, synkroniseras alla transaktionsinfogningar, uppdateringar och borttagningar med analysarkivet nästan i realtid, inga ändringsflödes- eller ETL-jobb krävs.

Med Azure Synapse Link kan du nu ansluta direkt till dina Azure Cosmos DB-containrar från Azure Synapse Analytics och få åtkomst till analysarkivet utan kostnader för enheter för begäran (enheter för begäran). Azure Synapse Analytics stöder för närvarande Azure Synapse Link med Synapse Apache Spark och serverlösa SQL-pooler. Om du har ett globalt distribuerat Azure Cosmos DB-konto blir det tillgängligt i alla regioner för det kontot när du har aktiverat analysarkiv för en container.

Automatisk schemainferens för analysarkiv

Azure Cosmos DB-transaktionslager anses vara radorienterade halvstrukturerade data, men analysarkivet har kolumnformat och strukturerat format. Den här konverteringen görs automatiskt för kunder med hjälp av schemainferensreglerna för analysarkivet. Det finns gränser i konverteringsprocessen: maximalt antal kapslade nivåer, maximalt antal egenskaper, datatyper som inte stöds med mera.

Kommentar

I samband med analysarkivet betraktar vi följande strukturer som egenskap:

  • JSON-element eller sträng/värde-par avgränsade med " : .
  • JSON-objekt avgränsade av { och }.
  • JSON-matriser avgränsade av [ och ].

Du kan minimera effekten av schemainferenskonverteringar och maximera dina analysfunktioner med hjälp av följande tekniker.

Normalisering

Normaliseringen blir meningslös eftersom du med Azure Synapse Link kan ansluta mellan dina containrar med hjälp av T-SQL eller Spark SQL. De förväntade fördelarna med normalisering är:

  • Mindre datafotavtryck i både transaktions- och analysarkiv.
  • Mindre transaktioner.
  • Färre egenskaper per dokument.
  • Datastrukturer med färre kapslade nivåer.

De här två sista faktorerna, färre egenskaper och färre nivåer, bidrar till prestanda för dina analysfrågor men minskar också risken för att delar av dina data inte representeras i analysarkivet. Som beskrivs i artikeln om regler för automatisk schemainferens finns det gränser för antalet nivåer och egenskaper som representeras i analysarkivet.

En annan viktig faktor för normalisering är att SQL-serverlösa pooler i Azure Synapse stöder resultatuppsättningar med upp till 1 000 kolumner, och att exponera kapslade kolumner också räknas mot den gränsen. Med andra ord har både analysarkiv och Synapse SQL-serverlösa pooler en gräns på 1 000 egenskaper.

Men vad ska man göra eftersom avnormalisering är en viktig datamodelleringsteknik för Azure Cosmos DB? Svaret är att du måste hitta rätt balans för dina transaktions- och analytiska arbetsbelastningar.

Partition Key (Partitionsnyckel)

Din Azure Cosmos DB-partitionsnyckel (PK) används inte i analysarkivet. Och nu kan du använda anpassad partitionering i analysarkivet till kopior av analysarkivet med valfri PK. På grund av den här isoleringen kan du välja en PK för dina transaktionsdata med fokus på datainmatning och punktläsningar, medan frågor mellan partitioner kan göras med Azure Synapse Link. Nu ska vi se ett exempel:

I ett hypotetiskt globalt IoT-scenario device id är ett bra PK eftersom alla enheter har en liknande datavolym och med det kommer du inte att ha ett problem med frekvent partition. Men om du vill analysera data för mer än en enhet, till exempel "alla data från igår" eller "summor per stad", kan du ha problem eftersom det är frågor mellan partitioner. Dessa frågor kan skada din transaktionsprestanda eftersom de använder en del av ditt dataflöde i enheter för begäranden för att köra. Men med Azure Synapse Link kan du köra dessa analysfrågor utan kostnader för enheter för begäranden. Kolumnformatet för analysarkivet är optimerat för analysfrågor och Azure Synapse Link tillämpar den här egenskapen för att ge bra prestanda med Azure Synapse Analytics-körningar.

Namn på datatyper och egenskaper

Artikeln regler för automatisk schemainferens listar vilka datatyper som stöds. Även om datatypen som inte stöds blockerar representationen i analysarkivet, kan datatyper som stöds bearbetas på olika sätt av Azure Synapse-körningen. Ett exempel är: När du använder DateTime-strängar som följer ISO 8601 UTC-standarden representerar Spark-pooler i Azure Synapse dessa kolumner eftersom sträng- och SQL-serverlösa pooler i Azure Synapse representerar dessa kolumner som varchar(8000).

En annan utmaning är att inte alla tecken accepteras av Azure Synapse Spark. Medan blanksteg accepteras är tecken som kolon, grav accent och kommatecken inte. Anta att dokumentet har en egenskap med namnet "Förnamn, Efternamn". Den här egenskapen representeras i analysarkivet och Synapse SQL-serverlös pool kan läsa den utan problem. Men eftersom det finns i analysarkivet kan Azure Synapse Spark inte läsa några data från analysarkivet, inklusive alla andra egenskaper. I slutet av dagen kan du inte använda Azure Synapse Spark när du har en egenskap med de tecken som inte stöds i deras namn.

Utplattade data

Alla egenskaper i rotnivån för dina Azure Cosmos DB-data representeras i analysarkivet som en kolumn och allt annat som finns i djupare nivåer av dokumentdatamodellen representeras som JSON, även i kapslade strukturer. Kapslade strukturer kräver extra bearbetning från Azure Synapse-körningar för att platta ut data i strukturerat format, vilket kan vara en utmaning i stordatascenarier.

Dokumentet har bara två kolumner i analysarkivet id och contactDetails. Alla andra data, email och phone, kräver extra bearbetning via SQL-funktioner för att läsas individuellt.


{
    "id": "1",
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555"}
    ]
}

Dokumentet har tre kolumner i analysarkivet , id, emailoch phone. Alla data är direkt åtkomliga som kolumner.


{
    "id": "1",
    "email": "thomas@andersen.com",
    "phone": "+1 555 555-5555"
}

Datanivåindelning

Med Azure Synapse Link kan du minska kostnaderna ur följande perspektiv:

  • Färre frågor som körs i transaktionsdatabasen.
  • Ett PK som är optimerat för datainmatning och punktläsningar, vilket minskar datafotavtrycket, scenarier med frekvent partition och partitionsdelningar.
  • Datanivåindelning eftersom analystid till live (attl) är oberoende av transaktionell time-to-live (tttl). Du kan behålla dina transaktionsdata i transaktionslagret i några dagar, veckor, månader och lagra data i analysarkivet i flera år eller för alltid. Kolumnformatet för analysarkiv ger en naturlig datakomprimering, från 50 % upp till 90 %. Och kostnaden per GB är ~10 % av transaktionsarkivets faktiska pris. Mer information om de aktuella begränsningarna för säkerhetskopiering finns i översikten över analysarkivet.
  • Inga ETL-jobb körs i din miljö, vilket innebär att du inte behöver etablera enheter för begäranden för dem.

Kontrollerad redundans

Det här är ett bra alternativ för situationer då en datamodell redan finns och inte kan ändras. Och den befintliga datamodellen passar inte bra in i analysarkivet på grund av regler för automatisk schemainferens som gränsen för kapslade nivåer eller det maximala antalet egenskaper. Om så är fallet kan du använda Azure Cosmos DB-ändringsflödet för att replikera dina data till en annan container och tillämpa nödvändiga transformeringar för en azure Synapse Link-vänlig datamodell. Nu ska vi se ett exempel:

Scenario

Containern CustomersOrdersAndItems används för att lagra onlinebeställningar, inklusive kund- och objektinformation: faktureringsadress, leveransadress, leveransmetod, leveransstatus, artikelpris osv. Endast de första 1 000 egenskaperna representeras och viktig information ingår inte i analysarkivet, vilket blockerar Azure Synapse Link-användning. Containern har PB:er med poster, det går inte att ändra programmet och bygga om data.

Ett annat perspektiv på problemet är stordatavolymen. Miljarder rader används ständigt av analysavdelningen, vilket hindrar dem från att använda tttl för gammal databorttagning. Att underhålla hela datahistoriken i transaktionsdatabasen på grund av analysbehov tvingar dem att ständigt öka etableringen av enheter för begäranden, vilket påverkar kostnaderna. Transaktions- och analysarbetsbelastningar konkurrerar om samma resurser samtidigt.

Vad bör jag göra?

Lösning med ändringsflöde

  • Teknikteamet bestämde sig för att använda ändringsflöde för att fylla i tre nya containrar: Customers, Ordersoch Items. Med Ändringsflöde normaliserar och plattas data ut. Onödig information tas bort från datamodellen och varje container har nära 100 egenskaper, vilket förhindrar dataförlust på grund av automatiska schemainferensgränser.
  • Dessa nya containrar har analysarkiv aktiverat och nu använder analysavdelningen Synapse Analytics för att läsa data, vilket minskar användningen av enheter för begäran eftersom analysfrågorna sker i Synapse Apache Spark- och serverlösa SQL-pooler.
  • Containern CustomersOrdersAndItems har nu tttl inställd på att endast behålla data i sex månader, vilket möjliggör en annan minskning av användningen av begärandeenheter, eftersom det finns minst en enhet för begäranden per GB i Azure Cosmos DB. Mindre data, färre enheter för begäranden.

Lärdomar

De största lärdomarna från den här artikeln är att förstå att datamodellering i en värld som är schemafri är lika viktig som någonsin.

Precis som det inte finns något enda sätt att representera data på en skärm finns det inget enda sätt att modellera dina data. Du måste förstå ditt program och hur det producerar, förbrukar och bearbetar data. Genom att tillämpa några av de riktlinjer som presenteras här kan du sedan ange om att skapa en modell som tillgodoser programmets omedelbara behov. När dina program behöver ändras kan du använda flexibiliteten i en schemafri databas för att enkelt omfamna den ändringen och utveckla datamodellen.