Adatok modellezése és particionálása az Azure Cosmos DB-ben való életből vett példa használatával
A KÖVETKEZŐKRE VONATKOZIK: NoSQL
Ez a cikk számos Azure Cosmos DB-fogalomra épül, például az adatmodellezésre, a particionálásra és a kiosztott átviteli sebességre , hogy bemutassa, hogyan lehet kezelni egy valós adattervezési gyakorlatot.
Ha általában relációs adatbázisokkal dolgozik, valószínűleg szokásokat és intuíciókat épített ki az adatmodellek tervezésére. Az Azure Cosmos DB konkrét megkötései, de egyedi erősségei miatt a legtöbb ajánlott eljárás nem fordítható le megfelelően, és az optimálisnál rosszabb megoldásokba húzhatja. A cikk célja, hogy végigvezesse Önt egy valós használati eset Azure Cosmos DB-n való modellezésének teljes folyamatán, az elemmodellezéstől az entitások közös elhelyezésén és a tárolóparticionáláson át.
Töltse le vagy tekintse meg a közösség által létrehozott forráskódot , amely a cikk fogalmait szemlélteti.
Fontos
Egy közösségi közreműködő hozzájárult ehhez a kódmintához, és az Azure Cosmos DB csapata nem támogatja annak karbantartását.
Esetleírás
Ebben a gyakorlatban egy blogplatform tartományát fogjuk figyelembe venni, ahol a felhasználókbejegyzéseket hozhatnak létre. A felhasználók is kedvelhetik és megjegyzéseket fűzhetnek ezekhez a bejegyzésekhez.
Tipp
Néhány szót dőlt betűvel emeltünk ki; ezek a szavak azonosítják, hogy milyen típusú "dolgokat" a modellünknek manipulálnia kell.
További követelmények hozzáadása a specifikációhoz:
- A címlap megjeleníti a nemrég létrehozott bejegyzések hírcsatornáját,
- Lekérhetjük egy felhasználó összes bejegyzését, egy bejegyzéshez fűzött összes megjegyzést és egy bejegyzéshez tartozó összes tetszésnyilvánítást,
- A bejegyzések a szerzők felhasználónevével és a hozzájuk fűzött megjegyzések és kedvelések számával jelennek meg.
- A megjegyzéseket és a kedveléseket a rendszer az őket létrehozó felhasználók felhasználónevével is visszaadja,
- Ha listákként jelennek meg, a bejegyzéseknek csak a tartalmuk csonkolt összegzését kell bemutatniuk.
A fő hozzáférési minták azonosítása
Első lépésként a megoldás hozzáférési mintáinak azonosításával némi struktúrát adunk a kezdeti specifikációnak. Az Azure Cosmos DB-hez készült adatmodell tervezésekor fontos tisztában lenni azzal, hogy a modellnek mely kéréseket kell kiszolgálnia, hogy a modell hatékonyan kiszolgálja ezeket a kéréseket.
Az általános folyamat könnyebb követése érdekében ezeket a különböző kéréseket parancsok vagy lekérdezésekként kategorizáljuk, és a CQRS-től némi szókincset kölcsönözünk. A CQRS-ben a parancsok írási kérések (azaz a rendszer frissítésének szándékai), a lekérdezések pedig írásvédett kérések.
Íme a platform által elérhetővé tenni kívánt kérések listája:
- [C1] Felhasználó létrehozása/szerkesztése
- [1. negyedév] Felhasználó lekérése
- [C2] Bejegyzés létrehozása/szerkesztése
- [2. negyedév] Bejegyzés lekérése
- [3. negyedév] Felhasználó bejegyzéseinek listázása rövid formában
- [C3] Megjegyzés létrehozása
- [4. negyedév] Bejegyzés megjegyzésének listázása
- [C4] Bejegyzés kedvelés
- [5. negyedév] Bejegyzés kedvelőinek listázása
- [6. negyedév] Sorolja fel az x legutóbbi, rövid formátumban létrehozott bejegyzést (hírcsatornát)
Ebben a szakaszban nem gondoltunk az egyes entitások (felhasználó, bejegyzés stb.) részleteire. Ezt a lépést általában az elsők között kell kezelni, amikor egy relációs tárolóra terveznek. Először ezt a lépést kezdjük, mert ki kell találnunk, hogyan fordítják le ezeket az entitásokat táblák, oszlopok, idegen kulcsok stb. szempontjából. Ez sokkal kevésbé aggodalomra ad okot egy olyan dokumentum-adatbázissal kapcsolatban, amely nem kényszeríti ki a sémát íráskor.
A hozzáférési minták kezdettől fogva történő azonosításának fő oka az, hogy a kérések listája lesz a tesztcsomagunk. Minden alkalommal, amikor iteráljuk az adatmodellt, végigmegyünk az egyes kéréseken, és ellenőrizzük annak teljesítményét és méretezhetőségét. Kiszámítjuk az egyes modellekben felhasznált kérelemegységeket, és optimalizáljuk őket. Ezek a modellek az alapértelmezett indexelési szabályzatot használják, és felülbírálhatja adott tulajdonságok indexelésével, ami tovább javíthatja a kérelemegység-használatot és a késést.
V1: Az első verzió
Két tárolóval kezdjük: users
és posts
.
Felhasználók tárolója
Ez a tároló csak a felhasználói elemeket tárolja:
{
"id": "<user-id>",
"username": "<username>"
}
Ezt a tárolót a használatával id
particionáljuk, ami azt jelenti, hogy a tárolón belül minden logikai partíció csak egy elemet tartalmaz.
Bejegyzések tárolója
Ez a tároló entitásokat, például bejegyzéseket, megjegyzéseket és kedveléseket tárol:
{
"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>"
}
Ezt a tárolót a használatával postId
particionáljuk, ami azt jelenti, hogy a tárolón belül minden logikai partíció tartalmaz egy bejegyzést, az adott bejegyzéshez tartozó összes megjegyzést és a bejegyzéshez tartozó összes tetszésnyilvánítást.
Bevezettünk egy type
tulajdonságot a tárolóban tárolt elemekben, hogy megkülönböztessük a tároló által üzemeltetett három entitástípust.
Emellett úgy döntöttünk, hogy beágyazás helyett kapcsolódó adatokra hivatkozunk (a fogalmakkal kapcsolatos részletekért tekintse meg ezt a szakaszt ), mert:
- nincs felső korlátja annak, hogy egy felhasználó hány bejegyzést hozhat létre,
- a bejegyzések tetszőlegesen hosszúak lehetnek,
- nincs felső korlátja annak, hogy egy bejegyzés hány megjegyzést és kedvelőt tartalmazhat,
- azt szeretnénk, hogy hozzá tudjunk adni egy megjegyzést vagy hasonlót egy bejegyzéshez anélkül, hogy frissítenie kellene magát a bejegyzést.
Mennyire teljesít jól a modell?
Itt az ideje, hogy felmérjük az első verzió teljesítményét és méretezhetőségét. A korábban azonosított kérések mindegyikéhez megmérjük a késését, és azt, hogy hány kérelemegységet használ fel. Ezt a mérést egy 100 000 felhasználót tartalmazó, felhasználónként 5–50 bejegyzést tartalmazó, legfeljebb 25 megjegyzést és 100 kedvelést tartalmazó üres adatkészleten végezzük.
[C1] Felhasználó létrehozása/szerkesztése
Ez a kérés egyszerűen implementálható, mivel csak létrehozunk vagy frissítünk egy elemet a users
tárolóban. A kérések a partíciókulcsnak köszönhetően szépen oszlanak el az id
összes partíción.
Késés | KÉRELEMEGYSÉG-díj | Teljesítmény |
---|---|---|
7 Ms |
5.71 RU |
✅ |
[1. negyedév] Felhasználó lekérése
A felhasználó lekéréséhez olvassa be a megfelelő elemet a users
tárolóból.
Késés | KÉRELEMEGYSÉG-díj | Teljesítmény |
---|---|---|
2 Ms |
1 RU |
✅ |
[C2] Bejegyzés létrehozása/szerkesztése
A [C1] fájlhoz hasonlóan csak a posts
tárolóba kell írnunk.
Késés | KÉRELEMEGYSÉG-díj | Teljesítmény |
---|---|---|
9 Ms |
8.76 RU |
✅ |
[2. negyedév] Bejegyzés lekérése
Először lekértük a megfelelő dokumentumot a posts
tárolóból. De ez nem elég, a specifikációnknak megfelelően a bejegyzés szerzőjének felhasználónevét, a megjegyzések számát és a bejegyzés kedveléseinek számát is összesíteni kell. A felsorolt összesítésekhez további 3 SQL-lekérdezést kell kiadni.
A további lekérdezések mindegyike a megfelelő tároló partíciókulcsára szűr, és pontosan ezt szeretnénk maximalizálni a teljesítmény és a méretezhetőség szempontjából. De végül négy műveletet kell végrehajtanunk, hogy egyetlen bejegyzést küldhessünk vissza, így ezt egy következő iterációban javítani fogjuk.
Késés | KÉRELEMEGYSÉG-díj | Teljesítmény |
---|---|---|
9 Ms |
19.54 RU |
⚠ |
[3. negyedév] Felhasználó bejegyzéseinek listázása rövid formában
Először le kell kérnünk a kívánt bejegyzéseket egy SQL-lekérdezéssel, amely lekéri az adott felhasználónak megfelelő bejegyzéseket. De több lekérdezést is ki kell adnunk, hogy összesítsük a szerző felhasználónevét, valamint a megjegyzések és kedvelések számát.
Ez a megvalósítás számos hátrányt jelent:
- a megjegyzések és kedvelések számát összesítő lekérdezéseket minden egyes, az első lekérdezés által visszaadott bejegyzéshez ki kell adni,
- A fő lekérdezés nem szűr a tároló partíciókulcsára
posts
, ami ki-kivezetéshez és partícióvizsgálathoz vezet a tárolóban.
Késés | RU-díj | Teljesítmény |
---|---|---|
130 Ms |
619.41 RU |
⚠ |
[C3] Megjegyzés létrehozása
A rendszer úgy hoz létre megjegyzést, hogy megírja a megfelelő elemet a posts
tárolóban.
Késés | RU-díj | Teljesítmény |
---|---|---|
7 Ms |
8.57 RU |
✅ |
[4. negyedév] Bejegyzés megjegyzésének listázása
Egy lekérdezéssel kezdjük, amely lekéri az adott bejegyzés összes megjegyzését, és ismét össze kell adnunk a felhasználóneveket minden megjegyzéshez.
Bár a fő lekérdezés nem szűr a tároló partíciókulcsára, a felhasználónevek összesítése külön-külön bünteti a teljes teljesítményt. Ezt később továbbfejlesztjük.
Késés | RU-díj | Teljesítmény |
---|---|---|
23 Ms |
27.72 RU |
⚠ |
[C4] Bejegyzés kedvelve
A(z) [C3] elemhez hasonlóan a megfelelő elemet is létrehozzuk a posts
tárolóban.
Késés | RU-díj | Teljesítmény |
---|---|---|
6 Ms |
7.05 RU |
✅ |
[5. negyedév] Bejegyzések tetszésének listázása
A (4. negyedévhez) hasonlóan lekérdezzük a bejegyzéshez tartozó kedveléseket, majd összesítjük a felhasználóneveket.
Késés | RU-díj | Teljesítmény |
---|---|---|
59 Ms |
58.92 RU |
⚠ |
[6. negyedév] Az x legutóbbi, rövid formában létrehozott bejegyzés listázása (hírcsatorna)
A legutóbbi bejegyzések lekéréséhez lekérdezzük a tárolót csökkenő posts
létrehozási dátum szerint rendezve, majd összesítjük a felhasználóneveket és a megjegyzések és kedvelések számát az egyes bejegyzésekhez.
A kezdeti lekérdezés ismételten nem szűr a tároló partíciókulcsára posts
, ami költséges ki-kifúvatáshoz vezet. Ez még rosszabb, mivel nagyobb eredményhalmazt célozunk meg, és egy ORDER BY
záradékkal rendezzük az eredményeket, ami drágábbá teszi a kérelemegységeket.
Késés | RU-díj | Teljesítmény |
---|---|---|
306 Ms |
2063.54 RU |
⚠ |
Tükrözés a V1 teljesítményéről
Az előző szakaszban tapasztalt teljesítményproblémákat megvizsgálva két fő problémaosztályt ismerhetünk fel:
- egyes kérések esetén több lekérdezést kell kiadni a visszaadni kívánt adatok összegyűjtése érdekében,
- Egyes lekérdezések nem szűrnek az általuk megcélzott tárolók partíciókulcsára, ami olyan kivezetéshez vezet, amely akadályozza a méretezhetőséget.
Az elsővel kezdődően oldjuk meg ezeket a problémákat.
V2: Denormalizálás bevezetése az olvasási lekérdezések optimalizálásához
Bizonyos esetekben azért kell több kérést kiadnunk, mert a kezdeti kérés eredménye nem tartalmazza az összes visszaadni kívánt adatot. Az adatok denormalizálása megoldja ezt a problémát az adatkészletünkben, amikor nem relációs adattárral, például az Azure Cosmos DB-vel dolgozik.
A példában a bejegyzéselemeket úgy módosítjuk, hogy hozzáadjuk a bejegyzés szerzőjének felhasználónevét, a megjegyzések számát és a kedvelések számát:
{
"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>"
}
A megjegyzéseket és az elemeket is módosítjuk, hogy hozzáadjuk annak a felhasználónak a felhasználónevét, aki létrehozta őket:
{
"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>"
}
Megjegyzés denormalizálása és a hasonlók száma
Azt szeretnénk elérni, hogy minden alkalommal, amikor hozzáadunk egy megjegyzést vagy hasonlót, a vagy a likeCount
megfelelő bejegyzésben is növeljükcommentCount
. A tároló particionálása posts
során postId
az új elem (megjegyzés vagy hasonló) és a hozzá tartozó bejegyzés ugyanabban a logikai partícióban található. Ennek eredményeképpen egy tárolt eljárást használhatunk a művelet végrehajtásához.
Megjegyzés létrehozásakor ([C3]) ahelyett, hogy új elemet adnánk hozzá a posts
tárolóhoz, a következő tárolt eljárást hívjuk meg a tárolón:
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
);
}
);
})
}
Ez a tárolt eljárás a bejegyzés azonosítóját és az új megjegyzés törzsét veszi paraméterként, majd:
- lekéri a bejegyzést
- növekményes
commentCount
- lecseréli a bejegyzést
- hozzáadja az új megjegyzést
Mivel a tárolt eljárások atomi tranzakciókként vannak végrehajtva, a megjegyzések értéke commentCount
és tényleges száma mindig szinkronban marad.
Nyilvánvalóan hasonló tárolt eljárást hívunk, amikor új kedveléseket adunk hozzá a növekményhez likeCount
.
Felhasználónevek denormalizálása
A felhasználónevek eltérő megközelítést igényelnek, mivel a felhasználók nem csak különböző partíciókban ülnek, hanem egy másik tárolóban is. Amikor denormalizálnunk kell az adatokat a partíciók és tárolók között, használhatjuk a forrástároló változáscsatornáját.
Példánkban a tároló változáscsatornáját használjuk arra, hogy reagáljon, amikor a users
felhasználók frissítik a felhasználóneveket. Ha ez történik, propagáljuk a módosítást úgy, hogy meghívunk egy másik tárolt eljárást a posts
tárolón:
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);
}
});
}
Ez a tárolt eljárás a felhasználó azonosítóját és a felhasználó új felhasználónevét veszi fel paraméterekként, majd:
- lekéri az összes olyan elemet, amely megfelel a
userId
(amelyek lehetnek bejegyzések, megjegyzések vagy kedvelések) - minden egyes elemhez
- lecseréli a
userUsername
- lecseréli az elemet
- lecseréli a
Fontos
Ez a művelet költséges, mert megköveteli, hogy ezt a tárolt eljárást a posts
tároló minden partícióján végrehajtsa. Feltételezzük, hogy a felhasználók többsége megfelelő felhasználónevet választ a regisztráció során, és soha nem módosítja, ezért ez a frissítés nagyon ritkán fog futni.
Milyen teljesítménynövekedést jelent a V2?
Beszéljünk a V2 teljesítménynövekedéséről.
[2. negyedév] Bejegyzés lekérése
Most, hogy megtörtént a denormalizálás, csak egyetlen elemet kell lekérnünk a kérés kezeléséhez.
Késés | RU-díj | Teljesítmény |
---|---|---|
2 Ms |
1 RU |
✅ |
[4. negyedév] Bejegyzés megjegyzésének listázása
Itt ismét megkímélhetjük a további kéréseket, amelyek beolvasták a felhasználóneveket, és egyetlen lekérdezéssel végződnek, amely a partíciókulcsra szűr.
Késés | RU-díj | Teljesítmény |
---|---|---|
4 Ms |
7.72 RU |
✅ |
[5. negyedév] Bejegyzések tetszésének listázása
Pontosan ugyanaz a helyzet, amikor felsorolja a kedveléseket.
Késés | RU-díj | Teljesítmény |
---|---|---|
4 Ms |
8.92 RU |
✅ |
V3: Annak ellenőrzése, hogy az összes kérés méretezhető-e
Még mindig két kérés van, amelyeket még nem optimalizáltunk teljes mértékben a teljesítmény általános fejlesztései során. Ezek a kérések a következők: [Q3] és [Q6]. Ezek olyan lekérdezéseket tartalmazó kérések, amelyek nem szűrnek a megcélzott tárolók partíciókulcsára.
[3. negyedév] Felhasználó bejegyzéseinek listázása rövid formában
Ez a kérés már a V2-ben bevezetett fejlesztésekből is hasznot hoz, ami további lekérdezéseket takarít meg.
A fennmaradó lekérdezés azonban továbbra sem szűr a tároló partíciókulcsára posts
.
A helyzetre való gondolkodás egyszerű:
- Ennek a kérésnek a szűrőre
userId
kell szűrnie, mert egy adott felhasználó összes bejegyzését le szeretnénk kérni. - Nem működik megfelelően, mert a
posts
tárolón fut, és nincsuserId
particionálása. - A nyilvánvalót jelezve a teljesítménnyel kapcsolatos problémát úgy oldjuk meg, hogy végrehajtjuk ezt a kérést egy, a következővel particionált tárolón
userId
: . - Kiderült, hogy már van ilyen tárolónk: a
users
tároló!
Ezért bevezetünk egy második szintű denormalizálást úgy, hogy a teljes bejegyzéseket a tárolóba duplikáljuk users
. Ezzel hatékonyan megkapjuk a bejegyzéseink másolatát, csak egy másik dimenzió mentén particionálva, így sokkal hatékonyabban lekérhetők az általuk userId
.
A users
tároló mostantól kétféle elemet tartalmaz:
{
"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>"
}
Ebben a példában:
- Bevezettünk egy
type
mezőt a felhasználói elemben, amely megkülönbözteti a felhasználókat a bejegyzésektől, - Hozzáadtunk egy
userId
mezőt is a felhasználói elemhez, amely redundáns aid
mezővel, de kötelező, mivel ausers
tároló most már particionáltuserId
(és nemid
a korábbiak szerint)
A denormalizálás eléréséhez ismét a változáscsatornát használjuk. Ezúttal a tároló változáscsatornájára posts
reagálunk, hogy elküldjük az új vagy frissített bejegyzéseket a users
tárolónak. Mivel a bejegyzések listázásához nem szükséges a teljes tartalom visszaadása, a folyamat során csonkíthatjuk őket.
Most már átirányíthatjuk a lekérdezést a users
tárolóhoz, és szűrhetünk a tároló partíciókulcsára.
Késés | RU-díj | Teljesítmény |
---|---|---|
4 Ms |
6.46 RU |
✅ |
[6. negyedév] Az x legutóbbi, rövid formában létrehozott bejegyzés listázása (hírcsatorna)
Itt is hasonló helyzettel kell foglalkoznunk: még ha a V2-ben bevezetett denormalizálás miatt feleslegesen hagyott lekérdezéseket is megkíméljük, a fennmaradó lekérdezés nem szűr a tároló partíciókulcsára:
Ugyanezt a megközelítést követve a kérés teljesítményének és méretezhetőségének maximalizálásához csak egy partícióra van szükség. Csak egyetlen partíciót kell elérnünk, mert csak korlátozott számú elemet kell visszaadnunk. Ahhoz, hogy feltöltsük a blogolási platform kezdőlapját, csak le kell szereznünk a 100 legutóbbi bejegyzést, anélkül, hogy a teljes adatkészleten keresztül kellene lapoznunk.
Az utolsó kérés optimalizálása érdekében bevezetünk egy harmadik tárolót a tervünkbe, amely teljes mértékben a kérés kiszolgálására szolgál. Denormalizáljuk a bejegyzéseinket az új feed
tárolóban:
{
"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>"
}
A type
mező particionálja ezt a tárolót, amely mindig post
szerepel az elemekben. Ezzel biztosítja, hogy a tároló összes eleme ugyanabban a partícióban üljön.
A denormalizálás eléréséhez csak a korábban bevezetett változáscsatorna-folyamathoz kell csatlakoznunk, hogy elküldjük a bejegyzéseket az új tárolóba. Az egyik fontos dolog, amit szem előtt kell tartanunk, hogy meg kell győződnünk arról, hogy csak a 100 legutóbbi bejegyzést tároljuk; ellenkező esetben a tároló tartalma meghaladhatja a partíció maximális méretét. Ez a korlátozás úgy valósítható meg, hogy minden alkalommal meghív egy eseményindítót , amikor egy dokumentumot hozzáadnak a tárolóhoz:
Íme a gyűjtemény csonkítását okozó post-trigger törzse:
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);
});
}
}
}
Az utolsó lépés a lekérdezés átirányítása az új feed
tárolóba:
Késés | RU-díj | Teljesítmény |
---|---|---|
9 Ms |
16.97 RU |
✅ |
Összegzés
Vessünk egy pillantást a kialakításunk különböző verzióiban bevezetett általános teljesítmény- és méretezhetőségi fejlesztésekre.
1. verzió | 2. verzió | V3 | |
---|---|---|---|
[C1] | 7 ms / 5.71 RU |
7 ms / 5.71 RU |
7 ms / 5.71 RU |
[1. negyedév] | 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 |
[2. negyedév] | 9 ms / 19.54 RU |
2 ms / 1 RU |
2 ms / 1 RU |
[3. negyedév] | 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 |
[4. negyedév] | 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 |
[5. negyedév] | 59 ms / 58.92 RU |
4 ms / 8.92 RU |
4 ms / 8.92 RU |
[6. negyedév] | 306 ms / 2063.54 RU |
83 ms / 532.33 RU |
9 ms / 16.97 RU |
Optimalizáltunk egy írásvédett forgatókönyvet
Észrevehette, hogy az írási kérések (parancsok) rovására összpontosítottuk az olvasási kérések (lekérdezések) teljesítményének javítására irányuló erőfeszítéseinket. Az írási műveletek sok esetben a változáscsatornákon keresztül aktiválják az ezt követő denormalizálást, ami számítási szempontból költségesebb és hosszabb időt tesz lehetővé.
Ezt az olvasási teljesítményre való összpontosítást azzal indokoljuk, hogy a blogolási platform (mint a legtöbb közösségi alkalmazás) írásvédett. Az olvasási terhelés azt jelzi, hogy a kiszolgálandó olvasási kérelmek mennyisége általában nagyságrendekkel magasabb, mint az írási kérelmek száma. Ezért érdemes drágábban végrehajtani az írási kéréseket, hogy az olvasási kérések olcsóbbak és hatékonyabbak legyenek.
Ha a legszélsőségesebb optimalizálást nézzük, a [Q6] 2000-ről 17 kérelemegységre váltott; Ezt úgy értük el, hogy a bejegyzések denormalizálása tételenként körülbelül 10 KÉRELEM költséggel jár. Mivel sokkal több hírcsatorna-kérést szolgálnánk ki, mint a bejegyzések létrehozása vagy frissítése, a denormalizálás költsége elhanyagolható a teljes megtakarítás figyelembevételével.
A denormalizálás növekményesen alkalmazható
A cikkben ismertetett méretezhetőségi fejlesztések közé tartozik az adatok denormalizálása és duplikálása az adatkészletben. Meg kell jegyezni, hogy ezeket az optimalizálásokat nem kell az 1. napon üzembe helyezni. A partíciókulcsokra szűrt lekérdezések nagyobb léptékben jobban teljesítenek, de a partíciók közötti lekérdezések elfogadhatók lehetnek, ha ritkán vagy korlátozott adathalmazon keresztül hívják őket. Ha csak egy prototípust készít, vagy egy kis és ellenőrzött felhasználói bázissal rendelkező terméket indít el, akkor ezeket a fejlesztéseket valószínűleg későbbre is megkímélheti. A legfontosabb tehát a modell teljesítményének monitorozása , hogy eldönthesse, mikor és mikor érdemes behozni őket.
A frissítések más tárolókba való terjesztéséhez használt változáscsatorna folyamatosan tárolja ezeket a frissítéseket. Ez az adatmegőrzés lehetővé teszi, hogy a tároló létrehozása óta minden frissítést lekérjen, és a bootstrap denormalizált nézeteit egyszeri felzárkózási műveletként kérje, még akkor is, ha a rendszer már sok adattal rendelkezik.
Következő lépések
A gyakorlati adatmodellezés és -particionálás bemutatása után érdemes lehet áttekinteni az alábbi cikkeket az általunk tárgyalt fogalmak áttekintéséhez: