Meer informatie over het modelleren en partitioneren van gegevens in Azure Cosmos DB aan de hand van een praktijkvoorbeeld
VAN TOEPASSING OP:
SQL-API
Dit artikel bouwt voort op verschillende Azure Cosmos DB zoals gegevensmodelleren, partitioneringen inrichten van doorvoer om te laten zien hoe u een echte oefening voor gegevensontwerp kunt aanpakken.
Als u meestal met relationele databases werkt, hebt u waarschijnlijk gewoonten en intuïtieven ontwikkeld voor het ontwerpen van een gegevensmodel. Vanwege de specifieke beperkingen, maar ook de unieke sterke punten van Azure Cosmos DB, worden de meeste van deze best practices niet goed vertaald en kunt u naar suboptimale oplossingen slepen. Het doel van dit artikel is u te begeleiden bij het volledige proces van het modelleren van een echte use-case op Azure Cosmos DB, van itemmodelleren tot entiteitskomma's en containerpartities.
Download of bekijk een door de community gegenereerde broncode die de concepten uit dit artikel illustreert. Dit codevoorbeeld is bijgedragen door een communitybijdrager en Azure Cosmos DB het onderhoud ervan wordt niet ondersteund.
Het scenario
Voor deze oefening gaan we kijken naar het domein van een platform waarop gebruikers berichten kunnen maken. Gebruikers kunnen deze berichten ook leuk vinden en opmerkingen toevoegen.
Tip
We hebben een aantal woorden in italic gemarkeerd; deze woorden identificeren het soort 'dingen' dat ons model moet manipuleren.
Meer vereisten toevoegen aan onze specificatie:
- Op een voorpagina wordt een feed weergegeven met onlangs gemaakte berichten,
- We kunnen alle berichten voor een gebruiker, alle opmerkingen voor een bericht en alle vind-ik-leuks voor een bericht ophalen.
- Berichten worden geretourneerd met de gebruikersnaam van hun auteurs en een telling van het aantal opmerkingen en vind-ik-leuks dat ze hebben,
- Opmerkingen en vind-ik-leuks worden ook geretourneerd met de gebruikersnaam van de gebruikers die ze hebben gemaakt,
- Wanneer berichten worden weergegeven als lijsten, hoeft alleen een afgekapte samenvatting van de inhoud ervan te worden weergegeven.
De belangrijkste toegangspatronen identificeren
Om te beginnen geven we enige structuur aan onze eerste specificatie door de toegangspatronen van onze oplossing te identificeren. Bij het ontwerpen van een gegevensmodel voor Azure Cosmos DB, is het belangrijk om te begrijpen welke aanvragen ons model moet verwerken om ervoor te zorgen dat het model deze aanvragen efficiënt kan verwerken.
Om het algehele proces gemakkelijker te volgen te maken, categoriseren we die verschillende aanvragen als opdrachten of query's, nemen we een vocabulaire op van CQRS, waarbij opdrachten schrijfaanvragen zijn (dat wil zeggen intenties om het systeem bij te werken) en query's alleen-lezen aanvragen zijn.
Dit is de lijst met aanvragen die door ons platform moeten worden weergegeven:
- [C1] Een gebruiker maken/bewerken
- [Q1] Een gebruiker ophalen
- [C2] Een bericht maken/bewerken
- [Q2] Een bericht ophalen
- [Q3] Een korte lijst met berichten van een gebruiker maken
- [C3] Een opmerking maken
- [Q4] De opmerkingen van een bericht in een lijst plaatsen
- [C4] Een bericht liken
- [Q5] Vind-ik-leuks van een bericht
- [Q6] De x meest recente berichten in korte vorm (feed)
In deze fase hebben we nog niet nagedacht over de details van wat elke entiteit (gebruiker, post, enzovoort) zal bevatten. Deze stap is meestal een van de eerste die moet worden aangepakt bij het ontwerpen op een relationele opslag, omdat we moeten na te gaan hoe deze entiteiten worden vertaald in termen van tabellen, kolommen, vreemde sleutels, enzovoort. Het is veel minder een probleem met een documentdatabase die geen schema afdwingt tijdens het schrijven.
De belangrijkste reden waarom het belangrijk is om onze toegangspatronen vanaf het begin te identificeren, is omdat deze lijst met aanvragen onze testsuite wordt. Elke keer dat we ons gegevensmodel itereren, nemen we elk van de aanvragen door en controleren we de prestaties en schaalbaarheid. We berekenen de aanvraageenheden die in elk model worden verbruikt en optimaliseren deze. Al deze modellen gebruiken het standaard indexeringsbeleid en u kunt dit overschrijven door specifieke eigenschappen te indexeren, waardoor het RU-verbruik en de latentie verder kunnen worden verbeterd.
V1: een eerste versie
We beginnen met twee containers: users en posts .
Gebruikerscontainer
Deze container slaat alleen gebruikersitems op:
{
"id": "<user-id>",
"username": "<username>"
}
We partitioneren deze container op , wat betekent dat elke logische partitie in die id container slechts één item bevat.
Berichtencontainer
Deze container host berichten, opmerkingen en vind-ik-leuks:
{
"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>"
}
We partitioneren deze container op , wat betekent dat elke logische partitie in die container één bericht bevat, alle opmerkingen voor dat bericht en alle postId vind-ik-leuks voor dat bericht.
Houd er rekening mee dat we een eigenschap hebben geïntroduceerd in de items die in deze container zijn opgeslagen om onderscheid te maken tussen de drie typen entiteiten die door type deze container worden host.
We hebben er ook voor gekozen om te verwijzen naar gerelateerde gegevens in plaats van deze in tesluiten (raadpleeg deze sectie voor meer informatie over deze concepten) omdat:
- er is geen bovengrens voor het aantal berichten dat een gebruiker kan maken.
- berichten kunnen willekeurig lang zijn,
- er is geen bovengrens voor het aantal opmerkingen en vind-ik-leuks dat een bericht kan hebben,
- we willen een opmerking of een like aan een bericht kunnen toevoegen zonder dat het bericht zelf moet worden bijgewerkt.
Hoe goed presteert ons model?
Het is nu tijd om de prestaties en schaalbaarheid van onze eerste versie te beoordelen. Voor elk van de eerder geïdentificeerde aanvragen meten we de latentie en het aantal aanvraageenheden dat wordt verbruikt. Deze meting wordt uitgevoerd op een dummygegevensset met 100.000 gebruikers met 5 tot 50 berichten per gebruiker en maximaal 25 opmerkingen en 100 likes per bericht.
[C1] Een gebruiker maken/bewerken
Deze aanvraag is eenvoudig te implementeren omdat we alleen een item in de container maken of users bijwerken. De aanvragen worden mooi verdeeld over alle partities dankzij de id partitiesleutel.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 7 ms | 5.71 RU | ✅ |
[Q1] Een gebruiker ophalen
U kunt een gebruiker ophalen door het bijbehorende item uit de container te users lezen.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 2 ms | 1 RU | ✅ |
[C2] Een bericht maken/bewerken
Net als bij [C1] moeten we alleen naar de posts container schrijven.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 9 ms | 8,76 RU | ✅ |
[Q2] Een bericht ophalen
We beginnen met het ophalen van het bijbehorende document uit de posts container. Maar dat is niet voldoende. Volgens onze specificatie moeten we ook de gebruikersnaam van de auteur van de post en de tellingen van het aantal opmerkingen en het aantal likes in dit bericht aggregeren, waarvoor 3 extra SQL-query's moeten worden uitgegeven.
Elk van de extra query's filtert op de partitiesleutel van de respectieve container. Dit is precies wat we willen maximaliseren voor prestaties en schaalbaarheid. Maar uiteindelijk moeten we vier bewerkingen uitvoeren om één bericht te retourneren, dus dat gaan we in een volgende iteratie verbeteren.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 9 ms | 19,54 RU | ⚠ |
[Q3] Een korte lijst met berichten van een gebruiker maken
Eerst moeten we de gewenste berichten ophalen met een SQL query die de berichten opvraagt die overeenkomen met die specifieke gebruiker. Maar we moeten ook aanvullende query's uitvoeren om de gebruikersnaam van de auteur en het aantal opmerkingen en vind-ik-leuks samen te tellen.
Deze implementatie biedt veel nadelen:
- de query's die het aantal opmerkingen en vind-ik-leuks aggregeren, moeten worden uitgegeven voor elk bericht dat wordt geretourneerd door de eerste query,
- De hoofdquery filtert niet op de partitiesleutel van de container, wat leidt tot een fan-out en een
postspartitiescan in de container.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 130 ms | 619.41 RU | ⚠ |
[C3] Een opmerking maken
Er wordt een opmerking gemaakt door het bijbehorende item in de container te posts schrijven.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 7 ms | 8,57 RU | ✅ |
[Q4] De opmerkingen van een bericht in een lijst plaatsen
We beginnen met een query die alle opmerkingen voor dat bericht opvraagt en nogmaals, we moeten gebruikersnamen ook afzonderlijk aggregeren voor elke opmerking.
Hoewel de hoofdquery wel filtert op de partitiesleutel van de container, worden de algehele prestaties afzonderlijk verdeeld door de gebruikersnamen samen te stellen. Dat gaan we later verbeteren.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 23 ms | 27.72 RU | ⚠ |
[C4] Een bericht liken
Net als bij [C3] maken we het bijbehorende item in de posts container.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 6 ms | 7,05 RU | ✅ |
[Q5] Vind-ik-leuks van een bericht
Net als bij [Q4] vragen we de vind-ik-leuks voor dat bericht op en aggregeren we vervolgens hun gebruikersnamen.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 59 ms | 58.92 RU | ⚠ |
[Q6] De x meest recente berichten in korte vorm (feed)
We halen de meest recente berichten op door een query uit te voeren op de container, gesorteerd op de aflopende aanmaakdatum, en vervolgens gebruikersnamen en tellingen van opmerkingen en vind-ik-leuks voor elk van de posts berichten samen te tellen.
Nogmaals, onze eerste query filtert niet op de partitiesleutel van de container, waardoor een dure posts fan-out wordt veroorzaakt. Dit is nog erger omdat we ons richten op een veel grotere resultatenset en de resultaten sorteren met een -component, waardoor het duurder wordt wat betreft ORDER BY aanvraageenheden.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 306 ms | 2063.54 RU | ⚠ |
Reflecteren op de prestaties van V1
Als we kijken naar de prestatieproblemen waarmee we in de vorige sectie te maken hebben gehad, kunnen we twee hoofdklassen van problemen identificeren:
- Voor sommige aanvragen moeten meerdere query's worden uitgegeven om alle gegevens te verzamelen die we moeten retourneren.
- Sommige query's filteren niet op de partitiesleutel van de containers die ze als doel hebben, waardoor de schaalbaarheid wordt belemmerd.
Laten we elk van deze problemen oplossen, beginnend bij de eerste.
V2: Introductie van denormalisatie om leesquery's te optimaliseren
De reden waarom we in sommige gevallen extra aanvragen moeten indienen, is omdat de resultaten van de eerste aanvraag niet alle gegevens bevatten die we moeten retourneren. Wanneer u werkt met een niet-relationeel gegevensopslag zoals Azure Cosmos DB, wordt dit soort probleem meestal opgelost door gegevens in onze gegevensset te denormaliseren.
In ons voorbeeld wijzigen we post-items om de gebruikersnaam van de auteur van het bericht, het aantal opmerkingen en het aantal vind-ik-leuks toe te voegen:
{
"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>"
}
We wijzigen ook opmerkingen en like-items om de gebruikersnaam toe te voegen van de gebruiker die ze heeft gemaakt:
{
"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>"
}
Denormalizing comment and like counts (Opmerkingen en like counts denormaliseren)
Wat we willen bereiken, is dat we elke keer dat we een opmerking of een like toevoegen, ook de of in het bijbehorende commentCount likeCount bericht verhogen. Omdat onze container is gepartitied door , worden het nieuwe item (opmerking of like) en de bijbehorende posts post in dezelfde logische postId partitie geplaatst. Als gevolg hiervan kunnen we een opgeslagen procedure gebruiken om die bewerking uit te voeren.
Wanneer u nu een opmerking maakt ([C3] ), in plaats van alleen een nieuw item toe te voegen in de container, roepen we de volgende opgeslagen posts procedure voor die container aan:
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
);
}
);
})
}
Deze opgeslagen procedure gebruikt de id van het bericht en de body van de nieuwe opmerking als parameters, en vervolgens:
- haalt het bericht op
- verhoogt de
commentCount - vervangt het bericht
- voegt de nieuwe opmerking toe
Wanneer opgeslagen procedures worden uitgevoerd als atomische transacties, blijven de waarde van en het werkelijke aantal opmerkingen commentCount altijd synchroon.
We roepen natuurlijk een vergelijkbare opgeslagen procedure aan bij het toevoegen van nieuwe vind-ik-leuks om de te likeCount verhogen.
Gebruikersnamen denormaliseren
Gebruikersnamen vereisen een andere benadering omdat gebruikers niet alleen in verschillende partities, maar in een andere container zitten. Wanneer we gegevens over partities en containers moeten denormaliseren, kunnen we de wijzigingsfeed van de broncontainer gebruiken.
In ons voorbeeld gebruiken we de wijzigingsfeed van de users container om te reageren wanneer gebruikers hun gebruikersnamen bijwerken. Als dat gebeurt, wordt de wijziging doorgegeven door een andere opgeslagen procedure aan te roepen in de posts container:
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);
}
});
}
Deze opgeslagen procedure gebruikt de id van de gebruiker en de nieuwe gebruikersnaam van de gebruiker als parameters, en vervolgens:
- haalt alle items op die overeenkomen
userIdmet de (dit kunnen berichten, opmerkingen of vind-ik-leuks zijn) - voor elk van deze items
- vervangt de
userUsername - vervangt het item
- vervangt de
Belangrijk
Deze bewerking is kostbaar omdat deze opgeslagen procedure moet worden uitgevoerd op elke partitie van de posts container. We gaan ervan uit dat de meeste gebruikers een geschikte gebruikersnaam kiezen tijdens de aanmelding en deze nooit zullen wijzigen, dus deze update wordt zeer zelden uitgevoerd.
Wat zijn de prestatieverbeteringen van V2?
[Q2] Een bericht ophalen
Nu de denormalisatie is verwerkt, hoeft er slechts één item te worden opgehaald om die aanvraag te verwerken.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 2 ms | 1 RU | ✅ |
[Q4] De opmerkingen van een bericht in een lijst plaatsen
Ook hier kunnen we de extra aanvragen besparen die de gebruikersnamen hebben opgehaald en eindigen met één query die filtert op de partitiesleutel.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 4 ms | 7,72 RU | ✅ |
[Q5] Vind-ik-leuks van een bericht
Exact dezelfde situatie bij het in een lijst met vind-ik-leuks.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 4 ms | 8,92 RU | ✅ |
V3: Ervoor zorgen dat alle aanvragen schaalbaar zijn
Als we naar onze algehele prestatieverbeteringen kijken, zijn er nog twee aanvragen die we nog niet volledig hebben geoptimaliseerd: [Q3] en [Q6]. Dit zijn de aanvragen met query's die niet filteren op de partitiesleutel van de containers die ze als doel hebben.
[Q3] Een korte lijst met berichten van een gebruiker maken
Deze aanvraag profiteert al van de verbeteringen die zijn geïntroduceerd in V2, waardoor extra query's worden bespaard.
Maar de resterende query filtert nog steeds niet op de partitiesleutel van de posts container.
De manier om over deze situatie na te denken is eigenlijk eenvoudig:
- Deze aanvraag moet worden gefilterd op de omdat we alle berichten voor een bepaalde gebruiker
userIdwillen ophalen - Het werkt niet goed omdat deze wordt uitgevoerd op de
postscontainer, die niet is gepart partitioneerd dooruserId - Als we het voor de hand liggende zeggen, zouden we ons prestatieprobleem oplossen door deze aanvraag uit te voeren op een container die is gepart partitioneerd door
userId - Het blijkt dat we al een dergelijke container hebben: de
userscontainer!
Daarom introduceren we een tweede niveau van denormalisatie door hele berichten in de container te users dupliceren. Op die manier krijgen we effectief een kopie van onze berichten, alleen gepart partitioneerd langs verschillende dimensies, waardoor ze veel efficiënter kunnen worden opgehaald door hun userId .
De users container bevat nu twee soorten items:
{
"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>"
}
Opmerking:
- we hebben een veld
typein het gebruikersitem geïntroduceerd om gebruikers te onderscheiden van berichten, - We hebben ook een veld toegevoegd aan het gebruikersitem, dat redundant is met het veld , maar vereist is omdat de container nu is gepartities door (en niet zoals
userIdidusersuserIdidvoorheen)
Om die denormalisatie te bereiken, gebruiken we opnieuw de wijzigingsfeed. Deze keer reageren we op de wijzigingsfeed van de container om nieuwe of bijgewerkte berichten naar de posts container te users verzenden. En omdat het niet nodig is om de volledige inhoud van berichten weer te geven, kunnen we ze in het proces afkapsen.
We kunnen nu onze query naar de users container doorslingeren en filteren op de partitiesleutel van de container.
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 4 ms | 6.46 RU | ✅ |
[Q6] De x meest recente berichten in korte vorm (feed)
We hebben hier te maken met een vergelijkbare situatie: zelfs nadat de extra query's overbodig zijn gebleven door de in V2 geïntroduceerde denormalisatie, filtert de resterende query niet op de partitiesleutel van de container:
Voor het maximaliseren van de prestaties en schaalbaarheid van deze aanvraag is het volgens dezelfde benadering vereist dat deze slechts één partitie raakt. Dit komt omdat we slechts een beperkt aantal items moeten retourneren; Om de startpagina van ons platform te vullen, hoeven we alleen de 100 meest recente berichten op te halen, zonder dat we de hele gegevensset hoeven te pagineren.
Om deze laatste aanvraag te optimaliseren, introduceren we een derde container in ons ontwerp, volledig toegewezen aan het verwerken van deze aanvraag. We denormaliseren onze berichten naar die nieuwe feed container:
{
"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>"
}
Deze container wordt gepartitied door type , die altijd in onze items post staat. Dit zorgt ervoor dat alle items in deze container zich in dezelfde partitie zullen blijven.
Om de denormalisering te bereiken, moeten we alleen de pijplijn voor de wijzigingsfeed vasthaken die we eerder hebben geïntroduceerd om de berichten naar die nieuwe container te verzenden. Een belangrijk punt om rekening mee te houden is dat we ervoor moeten zorgen dat we alleen de 100 meest recente berichten opslaan; Anders kan de inhoud van de container groter worden dan de maximale grootte van een partitie. Dit wordt gedaan door elke keer dat een document wordt toegevoegd aan de container een post-trigger aan te roepen:
Hier is de body van de natrigger die de verzameling afkapt:
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);
});
}
}
}
De laatste stap is het omleiden van onze query naar de nieuwe feed container:
| Latentie | RU-kosten | Prestaties |
|---|---|---|
| 9 ms | 16,97 RU | ✅ |
Conclusie
Laten we eens kijken naar de algemene prestatie- en schaalbaarheidsverbeteringen die we hebben geïntroduceerd in de verschillende versies van ons ontwerp.
| 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 |
We hebben een scenario met veel leeservaring geoptimaliseerd
U hebt wellicht opgemerkt dat we ons hebben gericht op het verbeteren van de prestaties van leesaanvragen (query's) ten koste van schrijfaanvragen (opdrachten). In veel gevallen activeren schrijfbewerkingen nu volgende denormalisatie via wijzigingenfeeds, waardoor ze rekenkracht duurder en langer worden om te materialiseren.
Dit wordt gerechtvaardigd door het feit dat een platform voor platformen (zoals de meeste sociale apps) veel leeswerk heeft, wat betekent dat het aantal leesaanvragen meestal aanzienlijk hoger is dan het aantal schrijfaanvragen. Het is dus zinvol om schrijfaanvragen duurder te maken om uit te voeren, zodat leesaanvragen goedkoper en beter presteren.
Als we kijken naar de meest extreme optimalisatie die we hebben gedaan, is [Q6] van 2000 naar meer dan 17 RUs gegaan; We hebben dit bereikt door berichten te denormaliseren tegen een prijs van ongeveer 10 RUs per item. Omdat we veel meer feedaanvragen zouden verwerken dan het maken of updaten van berichten, zijn de kosten van deze denormalisatie te verwaarloosbaar gezien de algehele besparingen.
Denormalisatie kan incrementeel worden toegepast
De schaalbaarheidsverbeteringen die we in dit artikel hebben verkend, hebben betrekking op denormalisatie en duplicatie van gegevens in de gegevensset. Deze optimalisaties hoeven niet op dag 1 te worden gebruikt. Query's die filteren op partitiesleutels presteren beter op schaal, maar partitieoverschrijdende query's kunnen volledig acceptabel zijn als ze zelden of tegen een beperkte gegevensset worden aangeroepen. Als u alleen een prototype bouwt of een product met een kleine en gecontroleerde gebruikersbasis start, kunt u deze verbeteringen waarschijnlijk voor later gebruik besparen; Het is dan belangrijk om de prestaties van uw model te bewaken, zodat u kunt bepalen of en wanneer het tijd is om ze binnen te halen.
In de wijzigingenfeed die we gebruiken om updates naar andere containers te distribueren, worden al deze updates permanent opgeslagen. Dit maakt het mogelijk om alle updates aan te vragen sinds het maken van de container en genormaliseerde weergaven bootstrapten als een een time-inhaalbewerking, zelfs als uw systeem al veel gegevens heeft.
Volgende stappen
Na deze inleiding tot praktische gegevensmodelleren en partitionering kunt u de volgende artikelen lezen om de concepten te bekijken die we hebben behandeld:
- Werken met databases, containers en items
- Partitionering in Azure Cosmos DB
- Feed wijzigen in Azure Cosmos DB
Probeert u capaciteitsplanning uit te Azure Cosmos DB? U kunt informatie over uw bestaande databasecluster gebruiken voor capaciteitsplanning.
- Als u alleen het aantal vcores en servers in uw bestaande databasecluster weet, leest u over het schatten van aanvraageenheden met behulp van vCores of vCCPUs
- Als u de gebruikelijke aanvraagsnelheden voor uw huidige databaseworkload kent, leest u over het schatten van aanvraageenheden met behulp van Azure Cosmos DB capacity planner