Cara memodelkan dan mempartisi data di Azure Cosmos DB menggunakan contoh dunia nyata

BERLAKU UNTUK: SQL API

Artikel ini dibuat pada beberapa konsep Azure Cosmos DB seperti pemodelan data, pemartisian, dan throughput yang diprovisi untuk mendemostrasikan cara mengatasi penggunaan desain dunia nyata.

Jika Anda biasanya menggunakan database relasional, Anda mungkin telah membangun kebiasaan dan intuisi tentang cara mendesain model data. Karena batasan spesifik, tetapi juga kekuatan unik Azure Cosmos DB, sebagian besar praktik terbaik ini tidak diterjemahkan dengan baik dan dapat menyeret Anda ke dalam solusi suboptimal. Tujuan artikel ini adalah untuk memandu Anda melalui proses lengkap pemodelan kasus penggunaan dunia nyata di Azure Cosmos DB, dari pemodelan item hingga kolokasi entitas dan partisi kontainer.

Unduh atau lihat kode sumber yang dihasilkan komunitas yang mengilustrasikan konsep dari artikel ini. Sampel kode ini diberikan oleh kontributor komunitas dan tim Azure Cosmos DB tidak mendukung pemeliharaannya.

Skenario

Untuk latihan ini, kami akan mempertimbangkan domain platform blogging di mana pengguna dapat membuat postingan. Pengguna juga dapat menyukai dan menambahkan komentar ke postingan tersebut.

Tip

Kami telah menyoroti beberapa kata dalam garis miring; kata-kata ini mengidentifikasi jenis "hal-hal" yang harus dimanipulasi oleh model kami.

Menambahkan lebih banyak persyaratan ke spesifikasi kami:

  • Halaman depan menampilkan umpan postingan yang baru dibuat,
  • Kami dapat mengambil semua postingan untuk pengguna, semua komentar untuk postingan, dan semua tanda sukai untuk postingan,
  • Postingan ditampilkan dengan nama pengguna penulisnya dan hitungan berapa banyak komentar dan suka yang mereka miliki,
  • Komentar dan suka juga ditampilkan dengan nama pengguna dari pengguna yang telah membuatnya,
  • Saat ditampilkan sebagai daftar, postingan hanya perlu menyajikan ringkasan kontennya yang terpotong.

Identifikasi pola akses utama

Untuk memulai, kami memberikan beberapa struktur pada spesifikasi awal kami dengan mengidentifikasi pola akses solusi kami. Saat merancang model data untuk Azure Cosmos DB, penting untuk memahami permintaan mana yang harus dilayani model kami guna memastikan model akan melayani permintaan tersebut secara efisien.

Untuk mempermudah mengikuti proses keseluruhan, kami mengategorikan permintaan yang berbeda sebagai perintah atau pertanyaan, meminjam beberapa kosakata dari CQRS di mana perintah adalah permintaan tulis (yaitu, niat untuk memperbarui sistem) dan kueri adalah permintaan baca-saja.

Berikut adalah daftar permintaan yang harus diekspos platform kami:

  • [C1] Membuat/mengedit pengguna
  • [Q1] Mengambil pengguna
  • [C2] Membuat/mengedit postingan
  • [Q2] Mengambil postingan
  • [Q3] Mencantumkan postingan pengguna dalam bentuk singkat
  • [C3] Membuat komentar
  • [Q4] Mencantumkan komentar postingan
  • [C4] Menyukai postingan
  • [Q5] Mencantumkan suka postingan
  • [Q6] Cantumkan postingan x terbaru yang dibuat dalam bentuk pendek (umpan)

Pada tahap ini, kami belum memikirkan detail apa yang akan dimuat oleh setiap entitas (pengguna, postingan, dll.). Langkah ini biasanya di antara yang pertama ditangani saat merancang terhadap toko relasional, karena kami harus mencari tahu bagaimana entitas tersebut akan diterjemahkan dalam hal tabel, kolom, kunci asing dll. Ini tidak begitu mengkhawatirkan dengan database dokumen yang tidak memberlakukan skema apa pun saat menulis.

Alasan utama mengapa penting untuk mengidentifikasi pola akses kami dari awal, adalah karena daftar permintaan ini akan menjadi rangkaian pengujian kami. Setiap kali kami melakukan iterasi melalui model data kami, kami akan memeriksa setiap permintaan dan memeriksa kinerja dan skalabilitasnya. Kami menghitung unit permintaan yang digunakan di setiap model dan mengoptimalkannya. Semua model ini menggunakan kebijakan pengindeksan default dan Anda dapat menimpanya dengan mengindeks properti tertentu, yang selanjutnya dapat meningkatkan konsumsi dan latensi RU.

V1: Versi pertama

Kita mulai dengan dua kontainer: users dan posts.

Kontainer pengguna

Kontainer ini hanya menyimpan item pengguna:

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

Kami mempartisi kontainer ini dengan id, yang berarti setiap partisi logis dalam kontainer tersebut hanya akan berisi satu item.

Kontainer postingan

Kontainer ini menghosting postingan, komentar, dan suka:

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

Kami mempartisi kontainer ini dengan postId, yang berarti setiap partisi logis dalam kontainer tersebut akan berisi satu postingan, semua komentar untuk postingan tersebut dan semua suka untuk postingan tersebut.

Perhatikan bahwa kami telah memperkenalkan type properti dalam item yang disimpan dalam kontainer ini untuk membedakan antara tiga jenis entitas yang dihosting kontainer ini.

Juga, kami telah memilih untuk mereferensikan data terkait, bukan menyematkannya (periksa bagian ini untuk detail tentang konsep-konsep ini) karena:

  • tidak ada batas atas berapa banyak postingan yang dapat dibuat pengguna,
  • postingan dapat sangat panjang,
  • tidak ada batas atas berapa banyak komentar dan suka yang bisa didapat oleh sebuah postingan,
  • kami ingin dapat menambahkan komentar atau suka ke postingan tanpa harus memperbarui postingan tersebut.

Seberapa baik performa model kami?

Sekarang saatnya untuk menilai kinerja dan skalabilitas versi pertama kami. Untuk setiap permintaan yang diidentifikasi sebelumnya, kami mengukur latensinya dan berapa banyak unit permintaan yang dikonsumsinya. Pengukuran ini dilakukan terhadap kumpulan data dummy yang berisi 100.000 pengguna dengan 5 hingga 50 postingan per pengguna, dan hingga 25 komentar dan 100 suka per postingan.

[C1] Membuat/mengedit pengguna

Permintaan ini mudah diterapkan karena kami hanya membuat atau memperbarui item dalam users kontainer. Permintaan akan tersebar dengan baik di semua partisi berkat id kunci partisi.

Writing a single item to the users container

Latensi Biaya RU Performa
7 mdtk 5,71 RU

[Q1] Mengambil pengguna

Mengambil pengguna dilakukan dengan membaca item yang sesuai dari users kontainer.

Retrieving a single item from the users container

Latensi Biaya RU Performa
2 mdtk 1 RU

[C2] Membuat/mengedit postingan

Demikian pula dengan [C1] , kami hanya perlu menulis ke posts kontainer.

Writing a single item to the posts container

Latensi Biaya RU Performa
9 mdtk 8,76 RU

[Q2] Mengambil postingan

Kita mulai dengan mengambil dokumen yang sesuai dari posts kontainer. Tapi itu tidak cukup, sesuai spesifikasi kami, kami juga harus mengumpulkan nama pengguna penulis postingan dan hitungan berapa banyak komentar dan berapa banyak suka postingan ini, yang membutuhkan 3 kueri SQL tambahan untuk dikeluarkan.

Retrieving a post and aggregating additional data

Masing-masing kueri tambahan memfilter pada kunci partisi dari kontainernya masing-masing, yang persis seperti yang kami inginkan guna memaksimalkan performa dan skalabilitas. Tapi kami akhirnya harus melakukan empat operasi untuk mengembalikan satu postingan, jadi kami akan meningkatkannya dalam iterasi berikutnya.

Latensi Biaya RU Performa
9 mdtk 19,54 RU

[Q3] Mencantumkan postingan pengguna dalam bentuk pendek

Pertama, kita harus mengambil postingan yang diinginkan dengan kueri SQL yang mengambil postingan yang sesuai dengan pengguna tertentu. Tetapi kita juga harus mengeluarkan kueri tambahan untuk menggabungkan nama pengguna penulis dan hitungan komentar dan suka.

Retrieving all posts for a user and aggregating their additional data

Implementasi ini menyajikan banyak kelemahan:

  • kueri yang menggabungkan hitungan komentar dan suka harus dikeluarkan untuk setiap postingan yang ditampilkan oleh kueri pertama,
  • kueri utama tidak memfilter pada kunci partisi posts kontainer, yang menghasilkan fan-out dan pemindaian partisi di seluruh kontainer.
Latensi Biaya RU Performa
130 mdtk 619,41 RU

[C3] Membuat komentar

Komentar dibuat dengan menulis item yang sesuai dalam posts kontainer.

Writing a single item to the posts container

Latensi Biaya RU Performa
7 mdtk 8,57 RU

[Q4] Mencantumkan komentar postingan

Kita mulai dengan kueri yang mengambil semua komentar untuk postingan tersebut dan sekali lagi, kita juga perlu mengumpulkan nama pengguna secara terpisah untuk setiap komentar.

Retrieving all comments for a post and aggregating their additional data

Meskipun kueri utama memang memfilter pada kunci partisi kontainer, menggabungkan nama pengguna secara terpisah menghukum performa keseluruhan. Kita akan perbaiki nanti.

Latensi Biaya RU Performa
23 mdtk 27,72 RU

[C4] Menyukai postingan

Sama seperti [C3] , kami membuat item yang sesuai dalam posts kontainer.

Writing a single item to the posts container

Latensi Biaya RU Performa
6 mdtk 7,05 RU

[Q5] Mencantumkan sukai postingan

Sama seperti [Q4] , kami meminta sukai untuk postingan tersebut, kemudian mengumpulkan nama penggunanya.

Retrieving all likes for a post and aggregating their additional data

Latensi Biaya RU Performa
59 mdtk 58,92 RU

[Q6] Cantumkan postingan x terbaru yang dibuat dalam bentuk pendek (umpan)

Kami mengambil postingan terbaru dengan mengkueri posts kontainer yang diurutkan berdasarkan tanggal pembuatan turun, lalu mengagregasi nama pengguna dan jumlah komentar dan suka untuk setiap postingan.

Retrieving most recent posts and aggregating their additional data

Sekali lagi, kueri awal kami tidak memfilter pada kunci partisi posts kontainer, yang memicu fan-out yang mahal. Yang satu ini bahkan lebih buruk karena kami menargetkan hasil yang jauh lebih besar yang ditetapkan dan mengurutkan hasil dengan ORDER BY klausa, yang membuatnya lebih mahal dalam hal unit permintaan.

Latensi Biaya RU Performa
306 mdtk 2063,54 RU

Merefleksikan performa V1

Melihat masalah performa yang kita hadapi di bagian sebelumnya, kita dapat mengidentifikasi dua kelas utama masalah:

  • beberapa permintaan mengharuskan beberapa kueri dikeluarkan untuk mengumpulkan semua data yang perlu kita tampilkan,
  • beberapa kueri tidak memfilter pada kunci partisi kontainer yang mereka targetkan, yang memicu fan-out yang menghambat skalabilitas kami.

Kita selesaikan masing-masing masalah tersebut, dimulai dengan yang pertama.

V2: Memperkenalkan denormalisasi untuk mengoptimalkan kueri baca

Alasan kita harus mengeluarkan permintaan tambahan dalam beberapa kasus adalah karena hasil permintaan awal tidak berisi semua data yang perlu kita tampilkan. Saat menggunakan penyimpanan data non-relasional seperti Azure Cosmos DB, masalah semacam ini biasanya diselesaikan dengan mendenormalisasi data di seluruh kumpulan data kami.

Dalam contoh kami, kami memodifikasi item postingan untuk menambahkan nama pengguna penulis postingan, jumlah komentar, dan jumlah suka:

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

Kami juga memodifikasi komentar dan menyukai item untuk menambahkan nama pengguna dari pengguna yang telah membuatnya:

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

Mendenormalisasi jumlah komentar dan suka

Apa yang ingin kami capai adalah bahwa setiap kali kami menambahkan komentar atau semacamnya, kami juga menambah commentCount atau likeCount dalam postingan yang sesuai. Karena kontainer kami posts dipartisi oleh postId, item baru (komentar atau suka) dan postingan yang sesuai berada di partisi logis yang sama. Akibatnya, kita dapat menggunakan prosedur yang disimpan untuk melakukan operasi itu.

Saat membuat komentar ( [C3] ), bukannya hanya menambahkan item baru dalam posts kontainer, kami memanggil prosedur tersimpan berikut pada kontainer tersebut:

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

Prosedur yang disimpan ini mengambil ID postingan dan isi komentar baru sebagai parameter, kemudian:

  • mengambil postingan
  • menaikkan commentCount
  • menggantikan postingan
  • menambahkan komentar baru

Karena prosedur yang disimpan dijalankan sebagai transaksi atom, nilai commentCount dan jumlah komentar aktual akan selalu tetap sinkron.

Kami jelas menyebut prosedur yang disimpan serupa saat menambahkan suka baru untuk menaikkan likeCount.

Mendenormalisasi nama pengguna

Nama pengguna memerlukan pendekatan yang berbeda karena pengguna tidak hanya berada di partisi yang berbeda, tetapi juga berada dalam kontainer yang berbeda. Saat harus mendenormalisasi data di seluruh partisi dan kontainer, kita dapat menggunakan umpan perubahan kontainer sumber.

Dalam contoh kami, kami menggunakan umpan perubahan users kontainer untuk bereaksi setiap kali pengguna memperbarui nama penggunanya. Saat itu terjadi, kami menyebarkan perubahan dengan memanggil prosedur lain yang disimpan pada posts kontainer:

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

Prosedur yang tersimpan ini mengambil ID pengguna dan nama pengguna baru pengguna sebagai parameter, lalu:

  • mengambil semua item yang cocok dengan userId (yang dapat menjadi postingan, komentar, atau suka)
  • untuk setiap item tersebut
    • menggantikan userUsername
    • menggantikan item

Penting

Operasi ini mahal karena memerlukan prosedur tersimpan ini untuk dijalankan pada setiap partisi posts kontainer. Kami berasumsi bahwa sebagian besar pengguna memilih nama pengguna yang cocok selama pendaftaran dan tidak akan pernah mengubahnya, sehingga pembaruan ini akan jarang dijalankan.

Berapa perolehan performa V2?

[Q2] Mengambil postingan

Setelah denormalisasi kami telah siap, kami hanya perlu mengambil satu item untuk menangani permintaan tersebut.

Retrieving a single item from the posts container

Latensi Biaya RU Performa
2 mdtk 1 RU

[Q4] Mencantumkan komentar postingan

Di sini lagi, kita dapat menyisihkan permintaan tambahan yang mengambil nama pengguna dan berakhir dengan satu kueri yang memfilter pada kunci partisi.

Retrieving all comments for a post

Latensi Biaya RU Performa
4 mdtk 7,72 RU

[Q5] Mencantumkan sukai postingan

Situasi yang sama persis saat mencantumkan suka.

Retrieving all likes for a post

Latensi Biaya RU Performa
4 mdtk 8,92 RU

V3: Memastikan semua permintaan dapat diskalakan

Melihat peningkatan performa kami secara keseluruhan, masih ada dua permintaan yang belum sepenuhnya kami optimalkan: [Q3] dan [Q6] . Permintaan ini adalah permintaan yang melibatkan kueri yang tidak memfilter pada kunci partisi kontainer yang ditargetkan.

[Q3] Mencantumkan postingan pengguna dalam bentuk pendek

Permintaan ini sudah mendapat manfaat dari peningkatan yang diperkenalkan di V2, yang menyisihkan kueri tambahan.

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

Tetapi kueri yang tersisa masih belum memfilter pada kunci partisi posts kontainer.

Cara memikirkan situasi ini sebenarnya sederhana:

  1. Permintaan ini harus difilter pada userId karena kami ingin mengambil semua postingan untuk pengguna tertentu
  2. Permintaan ini tidak bekerja dengan baik karena dijalankan terhadap posts kontainer, yang tidak dipartisi oleh userId
  3. Menekankan hal ini, kami akan menyelesaikan masalah performa kami dengan mengeksekusi permintaan ini terhadap kontainer yang dipartisi oleh userId
  4. Ternyata kami sudah memiliki kontainer ini: users kontainernya!

Jadi kami memperkenalkan denormalisasi tingkat kedua dengan menduplikasi seluruh postingan ke users kontainer. Dengan melakukannya, kami secara efektif mendapatkan salinan postingan kami, hanya dipartisi sepanjang dimensi yang berbeda, membuatnya jauh lebih efisien untuk diambil oleh userId mereka.

Kontainer users sekarang berisi 2 jenis item:

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

Perhatikan bahwa:

  • kami telah memperkenalkan type bidang dalam item pengguna untuk membedakan pengguna dari postingan,
  • kami juga telah menambahkan bidang userId dalam item pengguna, yang redundan dengan bidang id tetapi diperlukan karena users kontainer sekarang dipartisi oleh userId (dan tidak id seperti sebelumnya)

Untuk mencapai denormalisasi itu, kami sekali lagi menggunakan umpan perubahan. Kali ini, kami bereaksi pada umpan perubahan posts kontainer untuk mengirimkan postingan baru atau diperbarui ke users kontainer. Dan karena mencantumkan postingan tidak diperlukan untuk menampilkan konten lengkapnya, kami dapat memotongnya dalam prosesnya.

Denormalizing posts into the users container

Kita sekarang dapat merutekan kueri kita ke users kontainer, memfilter pada kunci partisi kontainer.

Retrieving all posts for a user

Latensi Biaya RU Performa
4 mdtk 6,46 RU

[Q6] Cantumkan postingan x terbaru yang dibuat dalam bentuk pendek (umpan)

Kita harus menangani situasi serupa di sini: bahkan setelah menyisihkan kueri tambahan yang dibiarkan tidak perlu oleh denormalisasi yang diperkenalkan di V2, kueri yang tersisa tidak memfilter pada kunci partisi kontainer:

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

Mengikuti pendekatan yang sama, memaksimalkan performa dan skalabilitas permintaan ini mengharuskan hanya mengenai satu partisi. Ini dapat dibayangkan karena kami hanya perlu menampilkan sejumlah item yang terbatas; untuk mengisi halaman beranda platform blogging kami, kita hanya perlu mendapatkan 100 postingan terbaru, tanpa perlu melakukan paginasi melalui seluruh himpunan data.

Jadi untuk mengoptimalkan permintaan terakhir ini, kami memperkenalkan kontainer ketiga untuk desain kami, sepenuhnya didedikasikan untuk melayani permintaan ini. Kami mendenormalisasi postingan kami ke kontainer feed baru tersebut:

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

Wadah ini dipartisi oleh type, yang akan selalu ada di item post kami. Melakukannya memastikan bahwa semua item dalam kontainer ini akan berada di partisi yang sama.

Untuk mencapai denormalisasi, kami hanya perlu mengaitkan pada pipa umpan perubahan yang sebelumnya telah kami perkenalkan untuk mengirim postingan ke kontainer baru tersebut. Satu hal penting yang perlu diingat adalah bahwa kita perlu memastikan bahwa kita hanya menyimpan 100 postingan terbaru; jika tidak, konten kontainer dapat tumbuh melebihi ukuran maksimum partisi. Ini dilakukan dengan memanggil pemicu postingan setiap kali dokumen ditambahkan dalam kontainer:

Denormalizing posts into the feed container

Berikut adalah inti pemicu postingan yang memangkas koleksi:

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

Langkah terakhir adalah mengalihkan kueri kami ke kontainer baru feed kami:

Retrieving most recent posts

Latensi Biaya RU Performa
9 mdtk 16,97 RU

Kesimpulan

Mari kita lihat peningkatan performa dan skalabilitas keseluruhan yang telah kami perkenalkan melalui berbagai versi desain kami.

V1 V2 V3
[C1] 7 mdtk/5,71 RU 7 mdtk/5,71 RU 7 mdtk/5,71 RU
[Q1] 2 mdtk/1 RU 2 mdtk/1 RU 2 mdtk/1 RU
[C2] 9 mdtk/8,76 RU 9 mdtk/8,76 RU 9 mdtk/8,76 RU
[Q2] 9 mdtk/19,54 RU 2 mdtk/1 RU 2 mdtk/1 RU
[Q3] 130 mdtk/619,41 RU 28 mdtk/201,54 RU 4 mdtk/6,46 RU
[C3] 7 mdtk/8,57 RU 7 mdtk/15,27 RU 7 mdtk/15,27 RU
[Q4] 23 mdtk/27,72 RU 4 mdtk/7,72 RU 4 mdtk/7,72 RU
[C4] 6 mdtk/7,05 RU 7 mdtk/14,67 RU 7 mdtk/14,67 RU
[Q5] 59 mdtk/58,92 RU 4 mdtk/8,92 RU 4 mdtk/8,92 RU
[Q6] 306 mdtk/2063,54 RU 83 mdtk/532,33 RU 9 mdtk/16,97 RU

Kami telah mengoptimalkan skenario read-heavy

Anda mungkin telah memperhatikan bahwa kami telah memusatkan upaya kami untuk meningkatkan performa permintaan baca (kueri) dengan mengorbankan permintaan tulis (perintah). Dalam banyak kasus, operasi tulis sekarang memicu denormalisasi berikutnya melalui umpan perubahan, yang membuatnya lebih mahal secara komputasi dan lebih lama untuk diwujudkan.

Hal ini benar mengingat fakta bahwa platform blogging (seperti kebanyakan aplikasi sosial) adalah read-heavy, yang berarti bahwa jumlah permintaan baca yang harus dilayani biasanya pesanan besarnya lebih tinggi dari jumlah permintaan tulis. Jadi masuk akal untuk membuat permintaan tulis lebih mahal untuk dieksekusi agar permintaan baca menjadi lebih murah dan bekerja lebih baik.

Jika kita melihat optimasi paling ekstrem yang telah kami lakukan, [Q6] berubah dari 2000 + RU menjadi hanya 17 RU; kami telah mencapainya dengan mendenormalisasi postingan dengan biaya sekitar 10 RU per item. Karena kami akan melayani lebih banyak permintaan umpan dibandingkan pembuatan atau pembaruan postingan, biaya denormalisasi ini dapat diabaikan mengingat penghematan keseluruhan.

Denormalisasi dapat diterapkan secara bertahap

Peningkatan skalabilitas yang telah kami jelajahi dalam artikel ini melibatkan denormalisasi dan duplikasi data di seluruh himpunan data. Perlu dicatat bahwa optimasi ini tidak harus diberlakukan pada hari ke-1. Kueri yang memfilter tombol partisi bekerja lebih baik dalam skala besar, tetapi kueri lintas-partisi dapat diterima sepenuhnya jika jarang dipanggil atau berlawanan dengan himpunan data terbatas. Jika Anda hanya membangun prototipe, atau meluncurkan produk dengan basis pengguna kecil dan terkontrol, Anda mungkin dapat menyisihkan peningkatan tersebut untuk nanti; yang penting kemudian adalah memantau performa model Anda sehingga Anda dapat memutuskan apakah dan kapan saatnya menghadirkannya.

Umpan perubahan yang kami gunakan untuk mendistribusikan pembaruan ke kontainer lain menyimpan semua pembaruan tersebut secara terus-menerus. Hal ini memungkinkan untuk meminta semua pembaruan sejak pembuatan tampilan denormalisasi kontainer dan bootstrap sebagai operasi pengejaran satu kali meski sistem Anda sudah memiliki banyak data.

Langkah berikutnya

Setelah pengenalan pemodelan dan partisi data praktis ini, Anda mungkin ingin melihat artikel berikut untuk meninjau konsep yang telah kami bahas:

Apakah Anda sedang mencoba melakukan perencanaan kapasitas untuk migrasi ke Azure Cosmos DB? Anda dapat menggunakan informasi tentang kluster database Anda yang ada saat ini untuk perencanaan kapasitas.