Modelování a dělení dat ve službě Azure Cosmos DB s využitím příkladu z reálného světa

PLATÍ PRO: SQL API

Tento článek vychází z několika konceptů Azure Cosmos DB, jako je modelování dat, dělení na oddíly a zřízená propustnost, a ukazuje, jak řešit cvičení návrhu dat z reálného světa.

Pokud obvykle pracujete s relačními databázemi, pravděpodobně jste vytvořili návyky a citlivosti, jak navrhnout datový model. Vzhledem k určitým omezením, ale také jedinečným silným stránkám Azure Cosmos DB, většina těchto osvědčených postupů se dobře nepřekládá a může vás přetáhnout do neoptimálních řešení. Cílem tohoto článku je provést kompletní proces modelování skutečného případu použití v Azure Cosmos DB od modelování položek až po kolokace entit a dělení kontejnerů.

Stáhněte nebo si prohlédněte zdrojový kód vygenerovaný komunitou , který ilustruje koncepty z tohoto článku. Tento ukázkový kód přispěl přispěvatel komunity a tým Azure Cosmos DB nepodporuje údržbu.

Scénář

V tomto cvičení budeme uvažovat o doméně platformy pro blogování, kde můžou uživatelé vytvářet příspěvky. Uživatelé můžou tyto příspěvky také lajkovat a přidávat do nich komentáře.

Tip

Zvýraznili jsme některá slova kurzívou; tato slova identifikují druh "věcí", které náš model bude muset manipulovat.

Přidání dalších požadavků do naší specifikace:

  • Na úvodní stránce se zobrazí informační kanál naposledy vytvořených příspěvků.
  • Můžeme načíst všechny příspěvky pro uživatele, všechny komentáře pro příspěvek a všechny lajky pro příspěvek,
  • Příspěvky se vrátí pomocí uživatelského jména autorů a počtu komentářů a jejich lajků.
  • Komentáře a lajky se také vrátí s uživatelským jménem uživatelů, kteří je vytvořili,
  • Když se zobrazí jako seznamy, příspěvky musí prezentovat jenom zkrácený souhrn jejich obsahu.

Identifikace hlavních vzorů přístupu

Abychom mohli začít, dáme naší počáteční specifikaci určitou strukturu tím, že identifikujeme vzory přístupu našeho řešení. Při navrhování datového modelu pro Azure Cosmos DB je důležité pochopit, které požadavky musí náš model obsluhovat, aby se zajistilo, že model bude tyto požadavky efektivně obsluhovat.

Aby byl celkový proces jednodušší, kategorizujeme tyto různé požadavky jako příkazy nebo dotazy, půjčíme si slovní zásobu z CQRS , kde příkazy jsou požadavky na zápis (tj. záměry aktualizovat systém) a dotazy jsou požadavky jen pro čtení.

Tady je seznam požadavků, které bude naše platforma muset zveřejnit:

  • [C1] Vytvoření nebo úprava uživatele
  • [Q1] Načtení uživatele
  • [C2] Vytvoření nebo úprava příspěvku
  • [Q2] Načtení příspěvku
  • [Q3] Výpis příspěvků uživatele ve krátkém formátu
  • [C3] Vytvoření komentáře
  • [Q4] Výpis komentářů k příspěvku
  • [C4] Líbí se příspěvku
  • [Q5] Zobrazení seznamu lajků příspěvku
  • [Q6] Zobrazení seznamu x nejnovějších příspěvků vytvořených v krátkém formátu (informační kanál)

V této fázi jsme nepřemýšleli o podrobnostech o tom, co každá entita (uživatel, příspěvek atd.) bude obsahovat. Tento krok je obvykle mezi prvními, které je potřeba řešit při navrhování proti relačnímu úložišti, protože musíme zjistit, jak se tyto entity přeloží z hlediska tabulek, sloupců, cizích klíčů atd. Je mnohem méně starostí s dokumentovou databází, která nevynucuje žádné schéma při zápisu.

Hlavním důvodem, proč je důležité identifikovat vzory přístupu od začátku, je to proto, že tento seznam požadavků bude naší testovací sadou. Pokaždé, když iterujeme datový model, projdeme jednotlivé požadavky a zkontrolujeme jeho výkon a škálovatelnost. Vypočítáme jednotky žádostí spotřebované v jednotlivých modelech a optimalizujeme je. Všechny tyto modely používají výchozí zásady indexování a můžete je přepsat indexováním konkrétních vlastností, což může dále zlepšit spotřebu a latenci RU.

V1: První verze

Začneme se dvěma kontejnery: users a posts.

Kontejner uživatelů

Tento kontejner ukládá pouze položky uživatelů:

{
    "id": "<user-id>",
    "username": "<username>"
}

Tento kontejner rozdělíme podle id, což znamená, že každý logický oddíl v tomto kontejneru bude obsahovat pouze jednu položku.

Příspěvky kontejneru

Tento kontejner hostuje příspěvky, komentáře a lajky:

{
    "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>"
}

Tento kontejner rozdělíme podle postId, což znamená, že každý logický oddíl v tomto kontejneru bude obsahovat jeden příspěvek, všechny komentáře pro tento příspěvek a všechny lajky pro tento příspěvek.

Všimněte si, že jsme zavedli type vlastnost v položkách uložených v tomto kontejneru, abychom odlišili tři typy entit, které tento kontejner hostuje.

Místo vložení jsme se rozhodli odkazovat na související data (podrobnosti o těchto konceptech najdete v této části ), protože:

  • neexistuje žádný horní limit počtu příspěvků, které může uživatel vytvořit,
  • příspěvky mohou být libovolně dlouhé,
  • neexistuje žádný horní limit počtu komentářů a lajků, které by příspěvek mohl mít,
  • Chceme být schopni přidat komentář nebo lajk do příspěvku, aniž bychom museli aktualizovat samotný příspěvek.

Jak dobře náš model funguje?

Teď je čas posoudit výkon a škálovatelnost naší první verze. U každého z dříve identifikovaných požadavků měříme jeho latenci a počet jednotek požadavků, které spotřebuje. Toto měření se provádí u fiktivní datové sady obsahující 100 000 uživatelů s 5 až 50 příspěvky na uživatele a až 25 komentářů a 100 lajků na příspěvek.

[C1] Vytvoření nebo úprava uživatele

Tento požadavek je jednoduchý k implementaci, protože právě vytváříme nebo aktualizujeme položku v kontejneru users . Požadavky se pěkně rozprostře mezi všechny oddíly díky klíči oddílu id .

Writing a single item to the users container

Latence Poplatek za RU Výkon
7 ms 5.71 RU

[Q1] Načtení uživatele

Načtení uživatele se provádí čtením odpovídající položky z kontejneru users .

Retrieving a single item from the users container

Latence Poplatek za RU Výkon
2 ms 1 RU

[C2] Vytvoření nebo úprava příspěvku

Podobně jako [C1] stačí do kontejneru posts zapisovat.

Writing a single item to the posts container

Latence Poplatek za RU Výkon
9 ms 8,76 RU

[Q2] Načtení příspěvku

Začneme načtením odpovídajícího dokumentu z kontejneru posts . To ale nestačí, podle naší specifikace musíme také agregovat uživatelské jméno autora příspěvku a počty, kolik komentářů a kolik lajků má tento příspěvek, což vyžaduje vydání 3 dalších SQL dotazů.

Retrieving a post and aggregating additional data

Každý z dalších dotazů filtruje klíč oddílu příslušného kontejneru, což je přesně to, co chceme maximalizovat výkon a škálovatelnost. Ale nakonec musíme provést čtyři operace, abychom vrátili jeden příspěvek, takže to v další iteraci vylepšíme.

Latence Poplatek za RU Výkon
9 ms 19,54 RU

[Q3] Výpis příspěvků uživatele ve krátkém formátu

Nejprve musíme načíst požadované příspěvky s dotazem SQL, který načte příspěvky odpovídající danému uživateli. Musíme ale také vydat další dotazy k agregaci uživatelského jména autora a počtu komentářů a lajků.

Retrieving all posts for a user and aggregating their additional data

Tato implementace představuje mnoho nevýhod:

  • dotazy agregující počty komentářů a lajky musí být vydány pro každý příspěvek vrácený prvním dotazem,
  • hlavní dotaz nefiltruje klíč oddílu posts kontejneru, což vede ke kontrole ventilátoru a oddílu v kontejneru.
Latence Poplatek za RU Výkon
130 ms 619.41 RU

[C3] Vytvoření komentáře

Komentář se vytvoří tak, že napíšete odpovídající položku v kontejneru posts .

Writing a single item to the posts container

Latence Poplatek za RU Výkon
7 ms 8,57 RU

[Q4] Výpis komentářů k příspěvku

Začneme dotazem, který načte všechny komentáře pro tento příspěvek a znovu, musíme také agregovat uživatelská jména samostatně pro každý komentář.

Retrieving all comments for a post and aggregating their additional data

I když hlavní dotaz filtruje klíč oddílu kontejneru, agregace uživatelských jmen samostatně penalizuje celkový výkon. Později to vylepšíme.

Latence Poplatek za RU Výkon
23 ms 27,72 RU

[C4] To se mi líbí příspěvek

Stejně jako [C3] vytvoříme odpovídající položku v kontejneru posts .

Writing a single item to the posts container

Latence Poplatek za RU Výkon
6 ms 7.05 RU

[Q5] Výpis lajků příspěvku

Stejně jako [Q4] se dotazujeme na to, co se mi líbí pro tento příspěvek, a pak agregujeme svoje uživatelské jméno.

Retrieving all likes for a post and aggregating their additional data

Latence Poplatek za RU Výkon
59 ms 58,92 RU

[Q6] Výpis nejnovějších příspěvků vytvořených v krátkém formátu (informační kanál)

Nejnovější příspěvky načteme dotazováním kontejneru posts seřazeného sestupným datem vytvoření, agregací uživatelských jmen a počty komentářů a to se mi líbí pro jednotlivé příspěvky.

Retrieving most recent posts and aggregating their additional data

Znovu se náš počáteční dotaz nefiltruje na klíč oddílu posts kontejneru, který aktivuje nákladný ventilátor. Tato sada výsledků je ještě horší, protože cílíme na mnohem větší sadu výsledků a výsledky seřadíme pomocí ORDER BY klauzule, která z hlediska jednotek požadavků zdražuje.

Latence Poplatek za RU Výkon
306 ms 2063.54 RU

Reflektoring na výkon V1

Při pohledu na problémy s výkonem, kterým jsme čelí v předchozí části, můžeme identifikovat dvě hlavní třídy problémů:

  • některé požadavky vyžadují vydání více dotazů, aby bylo možné shromáždit všechna data, která potřebujeme vrátit,
  • Některé dotazy nefiltrují klíč oddílu kontejnerů, na které cílí, což vede k fanoušku, který brání naší škálovatelnosti.

Pojďme všechny tyto problémy vyřešit, počínaje prvním.

V2: Představujeme denormalizaci pro optimalizaci dotazů pro čtení

Důvodem, proč musíme v některých případech vydávat další žádosti, je to, že výsledky počátečního požadavku neobsahují všechna data, která potřebujeme vrátit. Při práci s nerelačním úložištěm dat, jako je Azure Cosmos DB, se tento druh problému běžně řeší denormalizací dat napříč naší sadou dat.

V našem příkladu upravíme položky příspěvku tak, aby se přidalo uživatelské jméno autora příspěvku, počet komentářů a počet lajků:

{
    "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>"
}

Upravíme také komentář a podobně jako položky a přidáme uživatelské jméno uživatele, který je vytvořil:

{
    "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>"
}

Denormalizace komentáře a počet podobných položek

To, co chceme dosáhnout, je to, že pokaždé, když přidáme komentář nebo podobné, zvýšíme také commentCount nebo likeCount v příslušném příspěvku. Vzhledem k tomu, že je náš posts kontejner rozdělený na postIdoddíly, nová položka (komentář nebo like) a odpovídající příspěvek se nachází ve stejném logickém oddílu. V důsledku toho můžeme k provedení této operace použít uloženou proceduru .

Teď při vytváření komentáře ([C3]) místo pouhého přidání nové položky do posts kontejneru zavoláme následující uloženou proceduru v tomto kontejneru:

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
          );
        }
      );
    })
}

Tato uložená procedura přebírá ID příspěvku a text nového komentáře jako parametry a pak:

  • načte příspěvek.
  • zvýší počet commentCount
  • nahradí příspěvek.
  • přidá nový komentář.

Při provádění uložených procedur jako atomických transakcí zůstane hodnota commentCount a skutečný počet komentářů vždy synchronizovaný.

Samozřejmě voláme podobnou uloženou proceduru při přidávání nových lajků ke zvýšení likeCount.

Denormalizace uživatelských jmen

Uživatelské jména vyžadují jiný přístup, protože uživatelé se nacházejí nejen v různých oddílech, ale v jiném kontejneru. Když musíme denormalizovat data napříč oddíly a kontejnery, můžeme použít kanál změn zdrojového kontejneru.

V našem příkladu použijeme informační kanál změn kontejneru users k reakci při každé aktualizaci uživatelských jmen uživatelů. V takovém případě změnu rozšíříme voláním jiné uložené procedury v kontejneru posts :

Denormalizing usernames into the 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);
      }
    });
}

Tato uložená procedura přebírá ID uživatele a nové uživatelské jméno uživatele jako parametry a pak:

  • Načte všechny položky odpovídající userId (které můžou být příspěvky, komentáře nebo lajky)
  • pro každou z těchto položek
    • nahradí userUsername
    • nahradí položku.

Důležité

Tato operace je nákladná, protože vyžaduje, aby se tato uložená procedura spustila v každém oddílu kontejneru posts . Předpokládáme, že většina uživatelů při registraci zvolí vhodné uživatelské jméno a nikdy ho nezmění, takže tato aktualizace se spustí velmi zřídka.

Jaké jsou zisky výkonu V2?

[Q2] Načtení příspěvku

Teď, když je naše denormalizace na místě, musíme načíst pouze jednu položku pro zpracování této žádosti.

Retrieving a single item from the posts container

Latence Poplatek za RU Výkon
2 ms 1 RU

[Q4] Výpis komentářů k příspěvku

Tady znovu můžeme ušetřit další požadavky, které načítá uživatelská jména a končí jediným dotazem, který filtruje klíč oddílu.

Retrieving all comments for a post

Latence Poplatek za RU Výkon
4 ms 7,72 RU

[Q5] Výpis lajků příspěvku

Stejná situace při výpisu lajků.

Retrieving all likes for a post

Latence Poplatek za RU Výkon
4 ms 8,92 RU

V3: Zajištění škálovatelnosti všech požadavků

Při pohledu na naše celkové vylepšení výkonu jsou stále dva požadavky, které jsme plně neoptimalizovali: [Q3] a [Q6]. Jedná se o požadavky zahrnující dotazy, které nefiltrují klíč oddílu kontejnerů, na které cílí.

[Q3] Výpis příspěvků uživatele ve krátkém formátu

Tato žádost již přináší výhody vylepšení zavedených ve verzi 2, která šetří další dotazy.

Diagram that shows the query to list a user's posts in short form.

Zbývající dotaz ale stále nefiltruje klíč oddílu kontejneru posts .

Způsob, jak si o této situaci představit, je ve skutečnosti jednoduchý:

  1. Tento požadavek musí filtrovat userId podle toho, že chceme načíst všechny příspěvky pro konkrétního uživatele.
  2. Neprovádí se dobře, protože se provádí v kontejneru posts , který není rozdělený na oddíly. userId
  3. Hlásíme, že bychom vyřešili náš problém s výkonem spuštěním této žádosti proti kontejneru, který je rozdělený na oddíly. userId
  4. Ukázalo se, že už máme takový kontejner: users kontejner!

Proto zavádíme druhou úroveň denormalizace tím, že duplikujeme celé příspěvky do kontejneru users . Díky tomu efektivně získáme kopii našich příspěvků, pouze rozdělenou podle různých dimenzí, což je způsob, jak efektivněji načíst jejich userId.

Kontejner users teď obsahuje 2 druhy položek:

{
    "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>"
}

Poznámky:

  • zavedli type jsme pole v položce uživatele, abychom odlišili uživatele od příspěvků,
  • Přidali userId jsme také pole do položky uživatele, které je redundantní s id polem, ale vyžaduje se, protože users kontejner je teď dělený ( userId a ne id jako dříve)

Abychom toho denormalizace dosáhli, znovu použijeme informační kanál změn. Tentokrát reagujeme na kanál posts změn kontejneru a odešleme do kontejneru users všechny nové nebo aktualizované příspěvky. A protože výpis příspěvků nevyžaduje vrácení celého obsahu, můžeme je v procesu zkrátit.

Denormalizing posts into the users container

Teď můžeme směrovat dotaz do kontejneru users a filtrovat klíč oddílu kontejneru.

Retrieving all posts for a user

Latence Poplatek za RU Výkon
4 ms 6.46 RU

[Q6] Výpis nejnovějších příspěvků vytvořených v krátkém formátu (informační kanál)

Musíme se zde zabývat podobnou situací: i po uvolnění dalších dotazů nepotřebných denormalizací zavedeným ve V2 zbývající dotaz nefiltruje klíč oddílu kontejneru:

Diagram that shows the query to list the x most recent posts created in short form.

Pokud chcete dosáhnout stejného přístupu, maximalizace výkonu a škálovatelnosti požadavku vyžaduje, aby dosáhl pouze jednoho oddílu. To je možné, protože musíme vrátit pouze omezený počet položek; Abychom mohli naplnit domovskou stránku naší blogovací platformy, stačí získat 100 nejnovějších příspěvků, aniž by bylo nutné stránkovat celou datovou sadu.

Proto pro optimalizaci tohoto posledního požadavku zavádíme třetí kontejner našeho návrhu, který je zcela vyhrazený pro obsluhu této žádosti. Naše příspěvky do nového feed kontejneru denormalizujeme:

{
    "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>"
}

Tento kontejner je rozdělený na oddíly type, které budou vždy post v našich položkách. Tím zajistíte, že všechny položky v tomto kontejneru budou sedět ve stejném oddílu.

Abychom dosáhli denormalizace, stačí připojit kanál kanálu změn, který jsme dříve zavedli k odeslání příspěvků do tohoto nového kontejneru. Je důležité mít na paměti, že musíme zajistit, aby se ukládaly pouze 100 nejnovějších příspěvků; jinak se obsah kontejneru může zvětšit nad maximální velikost oddílu. Uděláte to tak, že zavoláte po triggeru pokaždé, když se dokument přidá do kontejneru:

Denormalizing posts into the feed container

Tady je text post-triggeru, který zkrátí kolekci:

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);
        });
    }
  }
}

Posledním krokem je přesměrování dotazu na nový feed kontejner:

Retrieving most recent posts

Latence Poplatek za RU Výkon
9 ms 16,97 RU

Závěr

Pojďme se podívat na celkové vylepšení výkonu a škálovatelnosti, která jsme zavedli v různých verzích našeho návrhu.

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

Optimalizovali jsme scénář náročný na čtení.

Možná jste si všimli, že jsme se zaměřili na zlepšení výkonu požadavků na čtení (dotazy) na úkor požadavků na zápis (příkazy). V mnoha případech operace zápisu nyní aktivují následnou denormalizaci prostřednictvím kanálů změn, což z nich zvyšuje výpočetní náklady a déle materializovat.

Důvodem je skutečnost, že platforma pro blogování (podobně jako většina sociálních aplikací) je těžká pro čtení, což znamená, že množství žádostí o čtení, které musí obsluhovat, je obvykle vyšší než množství žádostí o zápis. Proto je vhodné, aby žádosti o zápis byly dražší, aby bylo možné žádosti o čtení levnější a lépe provádět.

Pokud se podíváme na nejvýkonnější optimalizaci, která jsme udělali, [Q6] přešla z roku 2000+ na pouhých 17 RU; dosáhli jsme toho díky denormalizaci příspěvků za cenu přibližně 10 RU na položku. Stejně jako bychom sloužili mnohem více žádostí o kanály než vytváření nebo aktualizace příspěvků, náklady na tuto denormalizaci jsou zanedbatelné vzhledem k celkové úsporě.

Denormalizace lze použít přírůstkově

Vylepšení škálovatelnosti, která jsme prozkoumali v tomto článku, zahrnují denormalizaci a duplikaci dat napříč sadou dat. Je třeba poznamenat, že tyto optimalizace nemusí být zavedeny v den 1. Dotazy, které filtrují klíče oddílů, fungují lépe ve velkém měřítku, ale dotazy napříč oddíly mohou být zcela přijatelné, pokud se volají zřídka nebo proti omezené sadě dat. Pokud právě vytváříte prototyp nebo spouštíte produkt s malou a řízenou uživatelskou základnou, můžete tato vylepšení pravděpodobně ušetřit pro pozdější použití; Co je důležité, je pak monitorovat výkon modelu, abyste se mohli rozhodnout, jestli a kdy je čas je přenést.

Kanál změn, který používáme k distribuci aktualizací do jiných kontejnerů, ukládá všechny tyto aktualizace trvale. Díky tomu je možné vyžádat všechny aktualizace od vytvoření kontejneru a denormalizovaných zobrazení bootstrap jako jednorázovou operaci dochytávání i v případě, že váš systém už má hodně dat.

Další kroky

Po tomto úvodu do praktického modelování dat a dělení můžete zkontrolovat následující články a projít si koncepty, které jsme probrali:

Pokoušíte se naplánovat kapacitu migrace do azure Cosmos DB? Informace o stávajícím databázovém clusteru můžete použít k plánování kapacity.