Så här modellerar och partitionerar du data i Azure Cosmos DB med ett verkligt exempel
GÄLLER FÖR:
SQL API
Den här artikeln bygger på Azure Cosmos DB begrepp som datamodellering, partitioneringoch etablerat dataflöde för att demonstrera hur du kan hantera en verklig datadesignövning.
Om du vanligtvis arbetar med relationsdatabaser har du förmodligen skapat vanor och intuitioner för hur du utformar en datamodell. På grund av de specifika begränsningarna, men även de unika Azure Cosmos DB, översätts de flesta av dessa metodtips inte så bra och kan dra dig till icke-optimala lösningar. Målet med den här artikeln är att vägleda dig genom hela processen med att modellera ett verkligt användningsfall på Azure Cosmos DB, från objektmodellering till entitetsdelning och containerpartitionering.
Ladda ned eller visa en community-genererad källkod som illustrerar begreppen från den här artikeln. Det här kodexe exemplet har bidragit av en community-deltagare och Azure Cosmos DB inte stöder underhållet.
Scenariot
I den här övningen ska vi titta på domänen för en bloggplattform där användare kan skapa inlägg. Användare kan också gilla och lägga till kommentarer i dessa inlägg.
Tips
Vi har markerat några ord i kursivt; de här orden identifierar vilken typ av "saker" som vår modell måste manipulera.
Lägga till fler krav i vår specifikation:
- En förstasida visar ett flöde med nyligen skapade inlägg,
- Vi kan hämta alla inlägg för en användare, alla kommentarer för ett inlägg och alla likes för ett inlägg,
- Inlägg returneras med användarnamnet för deras författare och antalet kommentarer och gilla-meddelanden som de har,
- Kommentarer och gilla-kommentarer returneras också med användarnamnet för de användare som har skapat dem,
- När de visas som listor behöver inlägg bara visa en trunkerad sammanfattning av innehållet.
Identifiera de viktigaste åtkomstmönstren
Till att börja med ger vi viss struktur till vår inledande specifikation genom att identifiera vår lösnings åtkomstmönster. När du utformar en datamodell för Azure Cosmos DB är det viktigt att förstå vilka begäranden vår modell måste hantera för att säkerställa att modellen kan hantera dessa begäranden effektivt.
För att göra den övergripande processen enklare att följa kategoriserar vi de olika begärandena som kommandon eller frågor, lånar en del vokabulär från CQRS där kommandon är skrivbegäranden (dvs. avsikter för att uppdatera systemet) och frågorna är skrivskyddade begäranden.
Här är listan över begäranden som vår plattform måste exponera:
- [C1] Skapa/redigera en användare
- [Q1] Hämta en användare
- [C2] Skapa/redigera ett inlägg
- [Q2] Hämta ett inlägg
- [Q3] Visa en användares inlägg i kortform
- [C3] Skapa en kommentar
- [Q4] Visa en lista med kommentarer i ett inlägg
- [C4] Som ett inlägg
- [Q5] Lista ett inläggs likes
- [Q6] Lista de x senaste inläggen som skapats i kortform (feed)
I det här skedet har vi inte funderat på vad varje entitet (användare, post osv.) kommer att innehålla. Det här steget är vanligtvis bland de första som ska hanteras när du utformar mot ett relationslager, eftersom vi måste ta reda på hur dessa entiteter kommer att översättas i termer av tabeller, kolumner, främmande nycklar osv. Det är mycket mindre viktigt med en dokumentdatabas som inte framtvingar något schema vid skrivning.
Den främsta anledningen till att det är viktigt att identifiera våra åtkomstmönster från början är att den här listan över begäranden kommer att bli vår testsvit. Varje gång vi itererar över vår datamodell går vi igenom var och en av begärandena och kontrollerar dess prestanda och skalbarhet. Vi beräknar de enheter för begäran som förbrukas i varje modell och optimerar dem. Alla dessa modeller använder standardprincipen för indexering och du kan åsidosätta den genom att indexera specifika egenskaper, vilket kan förbättra ru-förbrukning och svarstid ytterligare.
V1: En första version
Vi börjar med två containrar: users och posts .
Användarcontainer
Den här containern lagrar endast användarobjekt:
{
"id": "<user-id>",
"username": "<username>"
}
Vi partitionera containern id med , vilket innebär att varje logisk partition i containern bara innehåller ett objekt.
Inläggscontainer
Den här containern är värd för inlägg, kommentarer och gilla-objekt:
{
"id": "<post-id>",
"type": "post",
"postId": "<post-id>",
"userId": "<post-author-id>",
"title": "<post-title>",
"content": "<post-content>",
"creationDate": "<post-creation-date>"
}
{
"id": "<comment-id>",
"type": "comment",
"postId": "<post-id>",
"userId": "<comment-author-id>",
"content": "<comment-content>",
"creationDate": "<comment-creation-date>"
}
{
"id": "<like-id>",
"type": "like",
"postId": "<post-id>",
"userId": "<liker-id>",
"creationDate": "<like-creation-date>"
}
Vi partitionera den här containern med , vilket innebär att varje logisk partition i containern innehåller ett inlägg, alla kommentarer för det inlägget och alla postId likes för det inlägget.
Observera att vi har introducerat en egenskap i objekten som lagras i den här containern för att skilja mellan de type tre typerna av entiteter som den här containern är värd för.
Vi har också valt att referera till relaterade data i stället för att bädda in dem (se det här avsnittet för information om dessa begrepp) eftersom:
- det finns ingen övre gräns för hur många inlägg en användare kan skapa,
- inlägg kan vara godtyckligt långa,
- det finns ingen övre gräns för hur många kommentarer och gilla-kommentarer ett inlägg kan ha,
- vi vill kunna lägga till en kommentar eller en like i ett inlägg utan att behöva uppdatera själva inlägget.
Hur bra presterar vår modell?
Nu är det dags att utvärdera prestanda och skalbarhet för vår första version. För var och en av de begäranden som har identifierats tidigare mäter vi svarstiden och hur många enheter för begäran som förbrukas. Det här måttet görs mot en dummydatauppsättning som innehåller 100 000 användare med 5 till 50 inlägg per användare och upp till 25 kommentarer och 100 gilla-filer per inlägg.
[C1] Skapa/redigera en användare
Den här begäran är enkel att implementera eftersom vi bara skapar eller uppdaterar ett objekt i users containern. Begärandena sprids smidigt över alla partitioner tack vare id partitionsnyckeln.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 7 ms | 5.71 RU | ✅ |
[Q1] Hämta en användare
Du hämtar en användare genom att läsa motsvarande objekt från users containern.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 2 ms | 1 RU | ✅ |
[C2] Skapa/redigera ett inlägg
På samma sätt som med [C1] behöver vi bara skriva till posts containern.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 9 ms | 8,76 RU | ✅ |
[Q2] Hämta ett inlägg
Vi börjar med att hämta motsvarande dokument från posts containern. Men det räcker inte. Enligt vår specifikation måste vi också aggregera användarnamnet för inläggets författare och antalet kommentarer och hur många likes det här inlägget har, vilket kräver att ytterligare tre SQL frågor utfärdas.
Var och en av de ytterligare frågorna filtrerar på partitionsnyckeln för respektive container, vilket är exakt vad vi vill maximera prestanda och skalbarhet. Men vi måste så småningom utföra fyra åtgärder för att returnera ett enda inlägg, så vi förbättrar det i en nästa iteration.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 9 ms | 19.54 RU | ⚠ |
[Q3] Visa en användares inlägg i kortform
Först måste vi hämta önskade inlägg med en SQL som hämtar inläggen som motsvarar den specifika användaren. Men vi måste också utfärda ytterligare frågor för att aggregera författarens användarnamn och antalet kommentarer och likes.
Den här implementeringen medför många nackdelar:
- frågorna som aggregerar antalet kommentarer och likes måste utfärdas för varje inlägg som returneras av den första frågan,
- Huvudfrågan filtrerar inte på partitionsnyckeln för containern, vilket leder till en förfjärning
postsoch en partitionssökning i containern.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 130 ms | 619.41 RU | ⚠ |
[C3] Skapa en kommentar
En kommentar skapas genom att skriva motsvarande objekt i posts containern.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 7 ms | 8,57 RU | ✅ |
[Q4] Visa en lista med kommentarer i ett inlägg
Vi börjar med en fråga som hämtar alla kommentarer för det inlägget och återigen måste vi också aggregera användarnamn separat för varje kommentar.
Även om huvudfrågan filtrerar på containerns partitionsnyckel, så kommer den övergripande prestandan att bli bättre om användarnamnen aggregeras separat. Vi förbättrar det senare.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 23 ms | 27.72 RU | ⚠ |
[C4] Som ett inlägg
Precis som [C3] skapar vi motsvarande objekt i posts containern.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 6 ms | 7.05 RU | ✅ |
[Q5] Lista ett inläggs likes
Precis som [Q4] frågar vi likes efter det inlägget och aggregerar sedan deras användarnamn.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 59 ms | 58.92 RU | ⚠ |
[Q6] Lista de x senaste inläggen som skapats i kortform (feed)
Vi hämtar de senaste inläggen genom att fråga containern sorterade efter fallande skapandedatum och aggregera sedan användarnamn och antal kommentarer och gilla-objekt för vart och posts ett av inläggen.
Återigen filtrerar vår första fråga inte på posts partitionsnyckeln för containern, vilket utlöser en kostsam förfjärrrning. Det här är ännu värre eftersom vi riktar in oss på en mycket större resultatuppsättning och sorterar resultatet med en -sats, vilket gör det dyrare när det ORDER BY gäller enheter för begärande.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 306 ms | 2063.54 RU | ⚠ |
Återspeglar prestanda för V1
När vi tittar på de prestandaproblem som vi stötte på i föregående avsnitt kan vi identifiera två huvudklasser av problem:
- vissa begäranden kräver att flera frågor utfärdas för att samla in alla data som vi behöver returnera,
- Vissa frågor filtrerar inte på partitionsnyckeln för de containrar som de är mål för, vilket leder till en för fan-out som hindrar vår skalbarhet.
Vi löser vart och ett av dessa problem och börjar med det första.
V2: Introduktion till avmalisering för att optimera läsfrågor
Anledningen till att vi måste utfärda ytterligare begäranden i vissa fall är att resultatet av den första begäran inte innehåller alla data som vi behöver returnera. När du arbetar med ett icke-relationellt datalager som Azure Cosmos DB, löses den här typen av problem ofta genom att avmalisera data i hela datauppsättningen.
I vårt exempel ändrar vi postobjekt för att lägga till användarnamnet för inläggets författare, antalet kommentarer och antal gilla-objekt:
{
"id": "<post-id>",
"type": "post",
"postId": "<post-id>",
"userId": "<post-author-id>",
"userUsername": "<post-author-username>",
"title": "<post-title>",
"content": "<post-content>",
"commentCount": <count-of-comments>,
"likeCount": <count-of-likes>,
"creationDate": "<post-creation-date>"
}
Vi ändrar också kommentars- och like-objekt för att lägga till användarnamnet för den användare som har skapat dem:
{
"id": "<comment-id>",
"type": "comment",
"postId": "<post-id>",
"userId": "<comment-author-id>",
"userUsername": "<comment-author-username>",
"content": "<comment-content>",
"creationDate": "<comment-creation-date>"
}
{
"id": "<like-id>",
"type": "like",
"postId": "<post-id>",
"userId": "<liker-id>",
"userUsername": "<liker-username>",
"creationDate": "<like-creation-date>"
}
Avormalisera kommentarer och gilla antal
Det vi vill uppnå är att varje gång vi lägger till en kommentar eller en liknande, ökar vi också commentCount eller likeCount i motsvarande inlägg. Eftersom posts containern partitioneras av finns det nya objektet (kommentar eller liknande) och postId dess motsvarande inlägg i samma logiska partition. Därför kan vi använda en lagrad procedur för att utföra den åtgärden.
Nu när du skapar en kommentar ([C3]), i stället för att bara lägga till ett nytt objekt i containern anropar vi följande lagrade posts procedur för containern:
function createComment(postId, comment) {
var collection = getContext().getCollection();
collection.readDocument(
`${collection.getAltLink()}/docs/${postId}`,
function (err, post) {
if (err) throw err;
post.commentCount++;
collection.replaceDocument(
post._self,
post,
function (err) {
if (err) throw err;
comment.postId = postId;
collection.createDocument(
collection.getSelfLink(),
comment
);
}
);
})
}
Den här lagrade proceduren tar ID:t för inlägget och brödtexten i den nya kommentaren som parametrar och gör sedan följande:
- hämtar posten
- ökar
commentCount - ersätter inlägget
- lägger till den nya kommentaren
När lagrade procedurer körs som atomiska transaktioner förblir värdet för och det faktiska antalet kommentarer commentCount alltid synkroniserat.
Vi anropar naturligtvis en liknande lagrad procedur när vi lägger till nya gilla-filer för att öka likeCount .
Avormalisera användarnamn
Användarnamn kräver en annan metod eftersom användarna inte bara finns i olika partitioner, utan i en annan container. När vi måste avmalisera data över partitioner och containrar kan vi använda källcontainerns ändringsflöde.
I vårt exempel använder vi ändringsflödet för users containern för att reagera när användare uppdaterar sina användarnamn. När det händer sprider vi ändringen genom att anropa en annan lagrad procedur i posts containern:
function updateUsernames(userId, username) {
var collection = getContext().getCollection();
collection.queryDocuments(
collection.getSelfLink(),
`SELECT * FROM p WHERE p.userId = '${userId}'`,
function (err, results) {
if (err) throw err;
for (var i in results) {
var doc = results[i];
doc.userUsername = username;
collection.upsertDocument(
collection.getSelfLink(),
doc);
}
});
}
Den här lagrade proceduren tar användarens ID och användarens nya användarnamn som parametrar och gör sedan följande:
- hämtar alla objekt som matchar
userId(som kan vara inlägg, kommentarer eller gilla-objekt) - för vart och ett av dessa objekt
- ersätter
userUsername - ersätter objektet
- ersätter
Viktigt
Den här åtgärden är dyr eftersom den här lagrade proceduren måste köras på varje partition i posts containern. Vi förutsätter att de flesta användare väljer ett lämpligt användarnamn under registrering och aldrig ändrar det, så den här uppdateringen körs mycket sällan.
Vilka är prestandaförbättringarna med V2?
[Q2] Hämta en post
Nu när vår avmalisering är på plats behöver vi bara hämta ett enskilt objekt för att hantera den begäran.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 2 ms | 1 RU | ✅ |
[Q4] Visa en lista med kommentarer i ett inlägg
Här kan vi återigen spara de extra begäranden som hämtade användarnamnen och få en enskild fråga som filtrerar partitionsnyckeln.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 4 ms | 7.72 RU | ✅ |
[Q5] Lista ett inläggs likes
Exakt samma situation när du visar en lista över gilla-objekt.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 4 ms | 8.92 RU | ✅ |
V3: Se till att alla begäranden är skalbara
När vi tittar på våra övergripande prestandaförbättringar finns det fortfarande två begäranden som vi inte har optimerat fullständigt: [Q3] och [Q6]. Det är begäranden som rör frågor som inte filtrerar på partitionsnyckeln för de containrar som de är mål för.
[Q3] Visa en användares inlägg i kortform
Den här begäran drar redan nytta av de förbättringar som introducerades i V2, vilket gör att ytterligare frågor kan besvaras.
Men den återstående frågan filtrerar fortfarande inte på partitionsnyckeln för posts containern.
Sättet att tänka på den här situationen är faktiskt enkelt:
- Den här begäran måste filtrera på
userIdeftersom vi vill hämta alla inlägg för en viss användare - Det fungerar inte bra eftersom det körs mot
postscontainern, som inte är partitionerad avuserId - Om vi säger det uppenbara löser vi vårt prestandaproblem genom att köra den här begäran mot en container som är partitionerad av
userId - Det visar sig att vi redan har en sådan container:
userscontainern!
Därför introducerar vi en andra nivå av av denormalisering genom att duplicera hela inlägg till users containern. Genom att göra det får vi i praktiken en kopia av våra inlägg, som endast är partitionerade längs olika dimensioner, vilket gör dem mer effektiva att hämta genom sina userId .
Containern users innehåller nu två typer av objekt:
{
"id": "<user-id>",
"type": "user",
"userId": "<user-id>",
"username": "<username>"
}
{
"id": "<post-id>",
"type": "post",
"postId": "<post-id>",
"userId": "<post-author-id>",
"userUsername": "<post-author-username>",
"title": "<post-title>",
"content": "<post-content>",
"commentCount": <count-of-comments>,
"likeCount": <count-of-likes>,
"creationDate": "<post-creation-date>"
}
Tänk på följande:
- vi har introducerat
typeett fält i användarobjektet för att skilja användare från inlägg, - vi har också lagt till ett fält i användarobjektet, som är redundant med fältet men krävs eftersom
userIdiduserscontainern nu är partitionerad av (och inteuserIdsomidtidigare)
För att uppnå denna avisering använder vi återigen ändringsflödet. Den här gången reagerar vi på ändringsflödet för posts containern för att skicka nya eller uppdaterade inlägg till users containern. Och eftersom inlägg inte kräver att deras fullständiga innehåll returneras kan vi trunkera dem i processen.
Nu kan vi dirigera frågan till users containern och filtrera på containerns partitionsnyckel.
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 4 ms | 6.46 RU | ✅ |
[Q6] Lista de x senaste inläggen som skapats i kortform (feed)
Vi måste hantera en liknande situation här: även om de ytterligare frågor som lämnats i onödan av den avormalisering som introducerades i V2 inte filtreras i den återstående frågan på containerns partitionsnyckel:
Enligt samma metod kräver maximerad prestanda och skalbarhet för den här begäran att den bara når en partition. Detta kan vara möjligt eftersom vi bara behöver returnera ett begränsat antal objekt. För att kunna fylla vår bloggplattforms startsida behöver vi bara hämta de 100 senaste inläggen, utan att behöva sidbryta genom hela datauppsättningen.
För att optimera den senaste begäran introducerar vi därför en tredje container i vår design, som är helt dedikerad för att betjäna den här begäran. Vi avjämför våra inlägg till den nya feed containern:
{
"id": "<post-id>",
"type": "post",
"postId": "<post-id>",
"userId": "<post-author-id>",
"userUsername": "<post-author-username>",
"title": "<post-title>",
"content": "<post-content>",
"commentCount": <count-of-comments>,
"likeCount": <count-of-likes>,
"creationDate": "<post-creation-date>"
}
Den här containern är partitionerad type av , som alltid finns i våra post objekt. Detta säkerställer att alla objekt i den här containern finns i samma partition.
För att uppnå denormalisering behöver vi bara koppla pipelinen för ändringsflöde som vi tidigare har introducerat för att skicka inläggen till den nya containern. En viktig sak att tänka på är att vi måste se till att vi bara lagrar de 100 senaste inläggen. Annars kan innehållet i containern bli större än den maximala storleken på en partition. Detta görs genom att anropa en efterutlösare varje gång ett dokument läggs till i containern:
Här är brödtexten i den efterutlösare som trunkerar samlingen:
function truncateFeed() {
const maxDocs = 100;
var context = getContext();
var collection = context.getCollection();
collection.queryDocuments(
collection.getSelfLink(),
"SELECT VALUE COUNT(1) FROM f",
function (err, results) {
if (err) throw err;
processCountResults(results);
});
function processCountResults(results) {
// + 1 because the query didn't count the newly inserted doc
if ((results[0] + 1) > maxDocs) {
var docsToRemove = results[0] + 1 - maxDocs;
collection.queryDocuments(
collection.getSelfLink(),
`SELECT TOP ${docsToRemove} * FROM f ORDER BY f.creationDate`,
function (err, results) {
if (err) throw err;
processDocsToRemove(results, 0);
});
}
}
function processDocsToRemove(results, index) {
var doc = results[index];
if (doc) {
collection.deleteDocument(
doc._self,
function (err) {
if (err) throw err;
processDocsToRemove(results, index + 1);
});
}
}
}
Det sista steget är att omdirigera frågan till vår nya feed container:
| Svarstid | RU-avgift | Prestanda |
|---|---|---|
| 9 ms | 16.97 RU | ✅ |
Slutsats
Låt oss ta en titt på de övergripande förbättringarna av prestanda och skalbarhet som vi har infört för de olika versionerna av vår design.
| V1 | V2 | V3 | |
|---|---|---|---|
| [C1] | 7 ms/5.71 RU | 7 ms/5.71 RU | 7 ms/5.71 RU |
| [Q1] | 2 ms/1 RU | 2 ms/1 RU | 2 ms/1 RU |
| [C2] | 9 ms/8,76 RU | 9 ms/8,76 RU | 9 ms/8,76 RU |
| [Q2] | 9 ms/ 19,54 RU | 2 ms/1 RU | 2 ms/1 RU |
| [Q3] | 130 ms /619.41 RU | 28 ms/201.54 RU | 4 ms/6.46 RU |
| [C3] | 7 ms/8,57 RU | 7 ms/15.27 RU | 7 ms/15.27 RU |
| [Q4] | 23 ms /27.72 RU | 4 ms/ 7,72 RU | 4 ms/ 7,72 RU |
| [C4] | 6 ms/ 7,05 RU | 7 ms /14.67 RU | 7 ms /14.67 RU |
| [Q5] | 59 ms/58.92 RU | 4 ms/8.92 RU | 4 ms/8.92 RU |
| [Q6] | 306 ms / 2063.54 RU | 83 ms/532.33 RU | 9 ms / 16,97 RU |
Vi har optimerat ett lästrollsscenario
Du kanske har märkt att vi har koncentrerat oss på att förbättra prestanda för läsbegäranden (frågor) på bekostnad av skrivbegäranden (kommandon). I många fall utlöser skrivåtgärder nu efterföljande denormalisering via ändringsflöden, vilket gör dem beräkningsmässigt dyrare och längre att materialisera.
Detta motiveras av det faktum att en bloggplattform (som de flesta sociala appar) är lästrollen, vilket innebär att mängden läsförfrågningar som den måste skicka vanligtvis är mycket högre än mängden skrivbegäranden. Därför är det klokt att göra skrivbegäranden dyrare att köra för att låta läsbegäranden vara billigare och bättre presterande.
Om vi tittar på den mest extrema optimeringen vi har gjort har [Q6] gått från över 2 000 RU:er till bara 17 RU:er. Vi har gjort det genom att avmalisera inlägg till en kostnad på cirka 10 RU:er per objekt. Eftersom vi skulle skicka många fler flödesbegäranden än att skapa eller uppdatera inlägg är kostnaden för den här denormaliseringen försumbar med tanke på de övergripande besparingarna.
Avormalisering kan tillämpas inkrementellt
De skalbarhetsförbättringar som vi har utforskat i den här artikeln omfattar avormalisering och duplicering av data i hela datauppsättningen. Det bör noteras att dessa optimeringar inte behöver göras på dag 1. Frågor som filtrerar på partitionsnycklar presterar bättre i stor skala, men frågor mellan partitioner kan vara helt godtagbara om de anropas sällan eller mot en begränsad datauppsättning. Om du bara skapar en prototyp eller lanserar en produkt med en liten och kontrollerad användarbas kan du förmodligen spara dessa förbättringar till senare. Det viktiga är sedan att övervaka modellens prestanda så att du kan avgöra om och när det är dags att ta in dem.
Ändringsflödet som vi använder för att distribuera uppdateringar till andra containrar lagrar alla dessa uppdateringar beständigt. Detta gör det möjligt att begära alla uppdateringar sedan containern skapades och de avokermaliserade vyerna startas som en ikappåtgärd en gång, även om systemet redan har stora data.
Nästa steg
Efter den här introduktionen till praktisk datamodellering och partitionering kan du läsa följande artiklar för att granska de begrepp som vi har gått igenom:
- Arbeta med databaser, containrar och objekt
- Partitionering i Azure Cosmos DB
- Ändringsflöde i Azure Cosmos DB
Försöker du göra kapacitetsplanering för en migrering till Azure Cosmos DB? Du kan använda information om ditt befintliga databaskluster för kapacitetsplanering.
- Om allt du vet är antalet virtuella kärnor och servrar i ditt befintliga databaskluster kan du läsa om att uppskatta enheter för programbegäran med hjälp av virtuella kärnor eller virtuella processorer
- Om du känner till typiska begärandefrekvenser för din aktuella databasarbetsbelastning kan du läsa om att uppskatta enheter för programbegäran Azure Cosmos DB kapacitetsplaneraren