Gerçek dünya örneği kullanarak verileri modelleme ve bölümleme

Bu makale, gerçek dünya veri tasarımı alıştırmasını nasıl çözeceklerini göstermek için veri modelleme, bölümleme ve sağlanan aktarım hızı gibi çeşitli Azure Cosmos DB kavramlarını temel alır.

Genellikle ilişkisel veritabanlarıyla çalışıyorsanız, büyük olasılıkla veri modelleri tasarlamak için alışkanlıklar geliştirmişsinizdir. Belirli kısıtlamalar ve Azure Cosmos DB'nin benzersiz güçlü yönleri nedeniyle bu en iyi yöntemlerin çoğu iyi sonuç vermez ve sizi en iyi olmayan çözümlere sürükleyebilir. Bu makalenin amacı, öğe modellemeden varlık birlikte bulundurma ve kapsayıcı bölümlemeye kadar Azure Cosmos DB'de gerçek dünya kullanım örneğini modelleme işleminin tamamından size yol göstermektir.

Bu makaledeki kavramları gösteren bir örnek için topluluk tarafından oluşturulan bu kaynak kodunu indirin veya görüntüleyin.

Important

Bir topluluk katkıda bulunanı bu kod örneğine katkıda bulundu. Azure Cosmos DB ekibi bakımlarını desteklemez.

Senaryo

Bu alıştırmada kullanıcıların gönderioluşturabileceği bir blog platformunun etki alanını ele aacağız. Kullanıcılar ayrıca bu gönderileri beğenebilir ve bu gönderilere yorum ekleyebilir.

Tip

Bazı sözcükler italik olarak vurgulanır ve modelimizin manipülasyon yaptığı "şeyler" türünü tanımlar.

Belirtimimize daha fazla gereksinim ekleme:

  • Ön sayfada son oluşturulan gönderilerin akışı görüntülenir.
  • Bir kullanıcının tüm gönderilerini, gönderinin tüm yorumlarını ve gönderinin tüm beğenilerini getirebiliriz.
  • Gönderiler, yazarlarının kullanıcı adı ve sahip oldukları yorum ve beğenilerin sayısıyla birlikte döndürülür.
  • Açıklamalar ve beğeniler, bunları oluşturan kullanıcıların kullanıcı adıyla da döndürülür.
  • Listeler olarak görüntülendiğinde, gönderilerin yalnızca içeriklerinin kesilmiş bir özetini sunmaları gerekir.

Ana erişim desenlerini tanımlama

Başlamak için çözümümüzün erişim desenlerini tanımlayarak ilk belirtimimize bir yapı vereceğiz. Azure Cosmos DB için bir veri modeli tasarlarken, modelin bu isteklere verimli bir şekilde hizmet ettiğinden emin olmak için modelimizin hangi isteklere hizmet ettiğini anlamak önemlidir.

Genel sürecin daha kolay takip edilmesi için, bu farklı istekleri komutlar veya sorgular olarak kategorilere ayırarak komut sorgusu sorumluluk ayrımından (CQRS) bazı sözcük dağarcığını kullanırız. CQRS'de komutlar yazma istekleri (sistemi güncelleştirme amaçları) ve sorgular salt okunur isteklerdir.

Platformumuzun kullanıma sunduğumuz isteklerin listesi aşağıdadır:

  • [C1] Kullanıcı oluşturma veya düzenleme
  • [S1] Kullanıcı alma
  • [C2] Gönderi oluşturma veya düzenleme
  • [Q2] Gönderi alma
  • [Q3] Kullanıcının gönderilerini kısa formda listeleme
  • [C3] Açıklama oluşturma
  • [Q4] Gönderinin açıklamalarını listeleme
  • [C4] Gönderiyi beğenme
  • [Q5] Gönderinin beğenilerini listeleme
  • [Q6] Kısa formda (akış) oluşturulan en son x gönderiyi listeleme

Bu aşamada, her varlığın (kullanıcı, gönderi vb.) ne içerdiğinin ayrıntılarını düşünmedik. Bu adım genellikle ilişkisel bir mağaza tasarlanırken ilk ele alınanlar arasındadır. Öncelikle bu adımla başlayacağız çünkü bu varlıkların tablolar, sütunlar, yabancı anahtarlar vb. açısından nasıl çevrildiği hakkında bilgi edinmeliyiz. Yazma sırasında herhangi bir şemayı zorlamayan bir belge veritabanı, çok daha az endişe kaynağı olur.

Bu istek listesi test paketimiz olacağı için erişim desenlerimizi en baştan tanımlamak önemlidir. Veri modelimizi her yinelediğimizde isteklerin her birini gözden geçiriyor ve performans ile ölçeklenebilirliğini kontrol ediyoruz. Her modelde kullanılan istek birimlerini (RU) hesaplayıp iyileştiriyoruz. Tüm bu modeller varsayılan dizin oluşturma ilkesini kullanır ve ru tüketimini ve gecikme süresini daha da geliştirebilecek belirli özellikleri dizinleyerek bu ilkeyi geçersiz kılabilirsiniz.

V1: İlk sürüm

İki kapsayıcıyla başlıyoruz: users ve posts.

Kullanıcılar konteyneri

Bu kapsayıcı yalnızca kullanıcı öğelerini depolar:

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

Bu kapsayıcıyı ile idbölümleyeceğiz; bu da kapsayıcıdaki her mantıksal bölümün yalnızca bir öğe içerdiği anlamına gelir.

Gönderi kabı

Bu kapsayıcı gönderiler, açıklamalar ve beğeniler gibi varlıkları barındırır:

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

Bu kapsayıcıyı ile postIdbölümleyeceğiz, bu da kapsayıcıdaki her mantıksal bölümün bir gönderi, o gönderiye yönelik tüm açıklamaları ve bu gönderinin tüm beğenilerini içerdiği anlamına gelir.

Bu kapsayıcının barındırmış olduğu üç varlık türünü ayırt etmek için bu kapsayıcıda depolanan öğelerde bir type özellik kullanıma sunulmuştur.

Ayrıca, aşağıdakilerden dolayı bunları eklemek yerine ilgili verilere başvurmayı seçtik:

  • Kullanıcının oluşturabileceği gönderilerin üst sınırı yoktur.
  • Gönderiler rastgele uzun olabilir.
  • Bir gönderinin kaç yorum ve beğeniye sahip olabileceğinin üst sınırı yoktur.
  • Gönderinin kendisini güncelleştirmek zorunda kalmadan gönderiye yorum veya beğeni ekleyebilmek istiyoruz.

Bu kavramlar hakkında daha fazla bilgi edinmek için bkz. Azure Cosmos DB'de veri modelleme.

Modelimiz ne kadar iyi performans gösterir?

Şimdi ilk sürümümüzün performansını ve ölçeklenebilirliğini değerlendirme zamanı geldi. Daha önce tanımlanan isteklerin her biri için gecikme süresini ve kaç istek birimi tükettiği ölçüyoruz. Bu ölçüm, kullanıcı başına 5 ila 50 gönderi içeren 100.000 kullanıcı ve gönderi başına en fazla 25 yorum ve 100 beğeni içeren sahte bir veri kümesine karşı yapılır.

[C1] Kullanıcı oluşturma veya düzenleme

Bu isteğin uygulanması basittir çünkü users kapsayıcısında sadece bir öğe oluşturur veya güncelleriz. İstekler, bölüm anahtarı sayesinde tüm bölümlere id eşit bir şekilde yayılır.

Kullanıcılar kapsayıcısına tek bir öğe yazma diyagramı.

Gecikme süresi Talep Birimleri Performans
7 milisaniye 5.71 RU

[S1] Kullanıcı alma

Kullanıcı alma işlemi, users içindeki ilgili öğenin okunmasıyla gerçekleştirilir.

Kullanıcılar kapsayıcısından tek bir öğe alma diyagramı.

Gecikme süresi Talep Birimleri Performans
2 milisaniye 1 RU

[C2] Gönderi oluşturma veya düzenleme

[C1] ile benzer şekilde kapsayıcıya posts yazmamız gerekir.

Gönderiler kapsayıcısına tek bir gönderi öğesi yazma diyagramı.

Gecikme süresi Talep Birimleri Performans
9 milisaniye 8.76 RU

[Q2] Gönderiyi geri al

Kapsayıcıdan posts ilgili belgeyi alarak başlayacağız. Ancak bu yeterli değildir, belirtimimize göre, gönderinin yazarının kullanıcı adını, yorum sayısını ve gönderi için beğeni sayısını da toplamamız gerekir. Listelenen toplamalar için üç SQL sorgusunun daha verilmesi gerekir.

Bir gönderiyi alma ve ek verileri bir araya getirme diyagramı.

Sorguların her biri, kendi kapsayıcısının bölüm anahtarına göre filtreleme yapar, bu da performansı ve ölçeklenebilirliği en üst düzeye çıkarmamızı sağlar. Ancak sonunda tek bir gönderi döndürmek için dört işlem yapmamız gerekir, bu nedenle bunu bir sonraki yinelemede iyileştireceğiz.

Gecikme süresi Talep Birimleri Performans
9 milisaniye 19.54 RU

[Q3] Kullanıcının gönderilerini kısa formda listeleme

İlk olarak, belirli bir kullanıcıya karşılık gelen gönderileri getiren bir SQL sorgusuyla istenen gönderileri almak zorundayız. Ancak yazarın kullanıcı adını ve açıklama ve beğeni sayısını toplamak için daha fazla sorgu da yapmamız gerekir.

Bir kullanıcı için tüm gönderileri alma ve ek verilerini toplama diyagramı.

Bu uygulama birçok dezavantaj sunar:

  • Açıklama ve beğeni sayılarını toplayan sorgular, ilk sorgu tarafından döndürülen her gönderi için gönderilmelidir.
  • Ana sorgu kapsayıcının bölüm anahtarına posts göre filtre uygulamaz ve kapsayıcıda bir yayma ve bölüm taramasına yol açar.
Gecikme süresi Talep Birimleri Performans
130 milisaniye 619.41 RU

[C3] Açıklama oluşturma

Kapsayıcıya karşılık gelen öğe posts yazılarak bir açıklama oluşturulur.

Gönderiler kapsayıcısına tek bir açıklama öğesi yazma diyagramı.

Gecikme süresi Talep Birimleri Performans
7 milisaniye 8.57 RU

[Q4] Gönderinin açıklamalarını listeleme

Bu gönderinin tüm açıklamalarını getiren bir sorguyla başlıyoruz ve bir kez daha, her açıklama için kullanıcı adlarını ayrı olarak toplamamız gerekiyor.

Bir gönderi için tüm açıklamaları alma ve ek verilerini toplama diyagramı.

Ana sorgu kapsayıcının bölüm anahtarına göre filtre uygulasa da, kullanıcı adlarının ayrı ayrı biriktirilmesi genel performansı olumsuz etkiler. Bunu daha sonra geliştireceğiz.

Gecikme süresi Talep Birimleri Performans
23 milisaniye 27.72 RU

[C4] Gönderiyi beğenme

[C3] gibi kapsayıcıda ilgili öğeyi posts de oluştururuz.

Gönderiler kapsayıcısına tek bir (beğeni) öğesi yazma diyagramı.

Gecikme süresi Talep Birimleri Performans
6 milisaniye 7.05 RU

[Q5] Gönderinin beğenilerini listeleme

[Q4] gibi, bu gönderinin beğenilerini sorgular ve kullanıcı adlarını toplarız.

Bir gönderi için tüm beğenileri alma ve ek verilerini toplama diyagramı.

Gecikme süresi Talep Birimleri Performans
59 milisaniye 58.92 RU

[Q6] Kısa formda (akış) oluşturulan en son x gönderiyi listeleme

Kapsayıcıyı azalan oluşturma tarihine göre sıralanmış olarak sorgulayarak posts en son gönderileri getiriyoruz, ardından gönderilerin her biri için kullanıcı adlarını ve açıklama ve beğeni sayısını bir araya getiriyoruz.

En son gönderileri alma ve ek verilerini toplama diyagramı.

İlk sorgumuz bir kez daha kapsayıcının bölüm anahtarına posts göre filtre uygulamaz ve bu da yüksek maliyetli bir çıkış tetikler. Daha büyük bir sonuç kümesini hedeflediğimiz ve sonuçları bir ORDER BY yan tümcesiyle sıraladığımız için bu durum daha da kötüdür ve bu da istek birimleri açısından daha pahalı olmasını sağlar.

Gecikme süresi Talep Birimleri Performans
306 milisaniye 2063.54 RU

V1'in performansını değerlendirme

Önceki bölümde karşılaştığımız performans sorunlarına baktığımızda iki ana sorun sınıfı belirleyebiliriz:

  • Bazı istekler, döndürmemiz gereken tüm verileri toplamak için birden çok sorgunun verilmesini gerektirir.
  • Bazı sorgular hedefledikleri kapsayıcıların bölüm anahtarına filtre uygulamaz ve bu da ölçeklenebilirliğimizi engelleyen bir yaymayla sonuçlanır.

İlk sorundan başlayarak bu sorunların her birini çözelim.

V2: Okuma sorgularını iyileştirmek için denormalizasyonu tanıtma

Bazı durumlarda daha fazla istek göndermemiz gerektiğinin nedeni, ilk isteğin sonuçlarının döndürmemiz gereken tüm verileri içermemesidir. Verileri normalden çıkarma, Azure Cosmos DB gibi bir ilişkisel olmayan veri deposuyla çalışırken veri kümemiz genelinde bu tür sorunları çözer.

Örneğimizde, gönderi öğelerini gönderinin yazarının kullanıcı adını, açıklama sayısını ve beğeni sayısını eklemek için değiştiririz:

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

Ayrıca açıklamayı ve beğeni öğelerini değiştirerek bunları oluşturan kullanıcının kullanıcı adını ekleriz:

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

Yorumu ve beğeni sayılarını denormalize etme

Başarmak istediğimiz şey, her yorum veya beğeni eklediğimizde, ilgili gönderideki commentCount veya likeCount değerini de artırmamızdır. Kapsayıcımızı postId bölümlere ayırırkenposts, yeni öğe (açıklama veya beğenme) ve buna karşılık gelen gönderi aynı mantıksal bölümde yer alır. Sonuç olarak, bu işlemi gerçekleştirmek için bir saklı yordam kullanabiliriz.

Açıklama ([C3]) oluşturduğunuzda, kapsayıcıya sadece yeni bir öğe posts eklemek yerine bu kapsayıcıda aşağıdaki depolanmış prosedürü çağırırız:

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

Bu saklı yordam, gönderinin kimliğini ve yeni yorumun gövdesini parametre olarak alır, ardından:

  • gönderiyi alır.
  • commentCountdeğerini artırır.
  • gönderinin yerini alır.
  • yeni yorumu ekler.

Saklı yordamlar atomik işlemler olarak yürütüldüğünde, commentCount değeri ile gerçek yorum sayısı her zaman uyumlu kalır.

Açıkça benzer bir saklı yordam çağırırız, likeCount değerini artırmak için yeni beğeniler eklerken.

Kullanıcı adlarını denormalize et

Kullanıcı adları farklı bir yaklaşım gerektirir çünkü kullanıcılar yalnızca farklı bölümlerde değil, farklı bir kapsayıcıda da yer alır. Bölümler ve kapsayıcılar arasında verileri normalden çıkarmak zorunda olduğumuzda, kaynak kapsayıcının değişiklik akışını kullanabiliriz.

Örneğimizde, kullanıcılar kullanıcı adlarını güncelleştirdiğinde tepki vermek için kapsayıcının users değişiklik akışını kullanırız. Böyle bir durumda, kapsayıcıda posts başka bir saklı yordam çağırarak değişikliği yayacağız:

Post kapsayıcısına kullanıcı adlarının normal dışılaştırılması diyagramı.

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

Bu prosedür, kullanıcının kimliğini ve yeni kullanıcı adını parametre olarak alır, sonra:

  • ile eşleşen userId tüm öğeleri getirir (gönderiler, açıklamalar veya beğeniler olabilir).
  • bu öğelerin her biri için:
    • userUsername öğesinin yerini alır.
    • öğesini değiştirir.

Important

Bu saklı yordamın kapsayıcının her bölümünde yürütülmesini gerektirdiğinden bu işlem maliyetlidir posts . Kullanıcıların çoğunun kaydolma sırasında uygun bir kullanıcı adı seçtiğini ve bunu asla değiştirmeyeceğini varsayıyoruz, bu nedenle bu güncelleştirme çok nadir çalışır.

V2'nin performans kazanımları nelerdir?

V2'nin bazı performans kazançlarından bahsedelim.

[Q2] Gönderiyi geri al

Artık normal dışıleştirmemiz uygulandığına göre, bu isteği işlemek için yalnızca tek bir öğe getirmemiz gerekir.

Denormalize edilmiş gönderiler kapsayıcısından tek bir öğe alma diyagramı.

Gecikme süresi Talep Birimleri Performans
2 milisaniye 1 RU

[Q4] Gönderinin açıklamalarını listeleme

Burada da kullanıcı adlarını getiren ek isteklerden kaçınabilir ve yalnızca bölüm anahtarında filtreleme yapan tek bir sorgu ile sonuçlanabilir.

Normal dışı bir gönderi için tüm açıklamaları alma diyagramı.

Gecikme süresi Talep Birimleri Performans
4 milisaniye 7.72 RU

[Q5] Gönderinin beğenilerini listeleme

Beğenileri listelerken de tam olarak aynı durum.

Normal dışı bir gönderi için tüm beğenileri alma diyagramı.

Gecikme süresi Talep Birimleri Performans
4 milisaniye 8.92 RU

V3: Tüm isteklerin ölçeklenebilir olduğundan emin olun

Genel performans geliştirmelerimize bakarken henüz tam olarak iyileştirilmemiş iki istek var. Bu istekler [Q3] ve [Q6] şeklindedir. Bu istekler, hedefledikleri kapsayıcıların bölüm anahtarına göre filtrelemeyen sorgular içeren isteklerdir.

[Q3] Kullanıcının gönderilerini kısa formda listeleme

Bu istek, V2'de sunulan ve daha fazla sorguda tasarruf sağlayan geliştirmelerden faydalanmaktadır.

Kullanıcının denormalize gönderilerini kısa formda listelemek için sorguyu gösteren diyagram.

Ancak, kalan sorgu yine de posts kapsayıcısının bölüm anahtarına göre filtreleme yapmıyor.

Bu durumu düşünmenin yolu basittir:

  • Belirli bir kullanıcı için tüm gönderileri getirmek istediğimiz için bu isteğin filtrelemesi userId.
  • İyi performans göstermez çünkü posts kapsayıcısı üzerinde yürütülür ve userId tarafından bölümlenmemiştir.
  • Açıktır ki, userId ile bölümlenmiş bir kapsayıcıda bu isteği yürüterek performans sorununu çözeceğiz.
  • Zaten böyle bir kapsayıcımız olduğu ortaya çıktı: users kapsayıcı!

Bu nedenle, tüm gönderileri users kapsayıcıya çoğaltarak ikinci bir normal dışıleştirme düzeyi sunacağız. Bunu yaparak, gönderilerimizin yalnızca farklı bir boyut boyunca bölünmüş bir kopyasını etkili bir şekilde elde ederiz, bu da onların userId tarafından daha verimli bir şekilde alınmasını sağlar.

Kapsayıcı users artık iki tür öğe içeriyor:

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

Bu örnekte:

  • Kullanıcı öğesinde, kullanıcıları gönderilerden ayırmak için bir type alan kullanıma sunulmuştur.
  • Kullanıcı öğesine, userId alanıyla yedekli olan ancak id kapsayıcısı artık users yerine userId ile bölümlendiği için gerekli olan bir id alanı ekledik.

Bu normalden çıkarma işlemini gerçekleştirmek için değişiklik akışını bir kez daha kullanırız. Bu kez, posts kapsayıcısının değişiklik akışına tepki göstererek yeni veya güncellenmiş her gönderiyi users kapsayıcısına gönderiyoruz. Gönderileri listelemek için içeriklerinin tamamının döndürülmesi gerekmediğinden, bunları listeleme sürecinde kısaltabiliriz.

Gönderileri kullanıcı kapsayıcısına denormalize etme diyagramı.

Artık sorgumuzu kapsayıcının users bölüm anahtarına göre filtreleyerek kapsayıcıya yönlendirebiliriz.

Normal dışı bir kullanıcı için tüm gönderileri alma diyagramı.

Gecikme süresi Talep Birimleri Performans
4 milisaniye 6.46 RU

[Q6] Kısa formda (akış) oluşturulan en son x gönderiyi listeleme

Burada benzer bir durumla karşılaşmamız gerekiyor: V2'de tanıtılan denormalizasyon ile gereksiz hale getirilen sorguların sayısı azaldıktan sonra bile, kalan sorgu kapsayıcının bölüm anahtarı üzerinde filtreleme yapmaz.

Kısa formda oluşturulan en son x gönderiyi listeleme sorgusunu gösteren diyagram.

Aynı yaklaşımı izleyerek bu isteğin performansını ve ölçeklenebilirliğini en üst düzeye çıkarmak için yalnızca bir bölümü hedeflemesi gerekmektedir. Yalnızca sınırlı sayıda öğe döndürmemiz gerektiğinden tek bir bölümü hedeflemek mümkündür. Blog platformumuzun giriş sayfasını doldurmak için, veri kümesinin tamamında sayfalandırmaya gerek kalmadan en son 100 gönderiyi almalıyız.

Bu nedenle, bu son isteği iyileştirmek için tasarımımıza tamamen bu isteği sunmayı adanmış üçüncü bir kapsayıcı tanıtacağız. Gönderilerimizi bu yeni feed kapsayıcıya denormalize ediyoruz.

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

Alan type, her zaman post olan bu kapsayıcıyı öğelerimizde bölümlendirir. Bunu yapmak, bu kapsayıcıdaki tüm öğelerin aynı bölümde olmasını sağlar.

Normal dışıleştirmeyi başarmak için, gönderileri bu yeni kapsayıcıya göndermek için daha önce sunduğumuz değişiklik akışı işlem hattına bağlanmamız gerekir. Aklınızda bulundurmanız gereken önemli noktalardan biri, yalnızca en son 100 gönderiyi sakladığımızdan emin olmamız gerektiğidir; aksi takdirde, kapsayıcının içeriği bir bölümün boyutu üst sınırını aşabilir. Bu sınırlama, kapsayıcıya her belge eklendiğinde bir son tetikleyici çağrılarak uygulanabilir:

Gönderilerin akış kapsayıcısına denormalizasyonunun diyagramı.

Koleksiyonu kesen son tetikleyicinin gövdesi aşağıdadır:

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

Son adım sorgumuzu yeni feed kapsayıcımıza yeniden yönlendirmektir:

En son gönderileri alma diyagramı.

Gecikme süresi Talep Birimleri Performans
9 milisaniye 16.97 RU

Sonuç

Şimdi tasarımımızın farklı sürümleri üzerinde sunduğumuz genel performans ve ölçeklenebilirlik geliştirmelerine göz atalım.

V1 V2 V3
[C1] 7 ms / 5.71 RU 7 ms / 5.71 RU 7 ms / 5.71 RU
[S1] 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

Yoğun okuma senaryolarını iyileştirdik

Okuma isteklerinin (sorguların) performansını artırmaya yönelik çabalarımızı, yazma isteklerinin (komutların) performansı pahasına yoğunlaştırdığımızı fark edebilirsiniz. Çoğu durumda, yazma işlemleri artık değişiklik akışları aracılığıyla sonraki normalleştirmeyi tetikler ve bu da işlem açısından daha pahalı ve gerçekleştirilmesi daha uzun sürer.

Çoğu sosyal uygulama gibi bir bloglama platformunun okuma performansına odaklanmasını gerekçelendiriyoruz. Okumaların yoğun olduğu bir iş yükü, genel olarak hizmet vermesi gereken okuma isteği sayısının yazma isteği sayısından kat kat fazla olduğunu gösterir. Bu nedenle, okuma isteklerinin daha ucuz ve daha iyi performans göstermesini sağlamak için yazma isteklerinin yürütülmesini daha pahalı hale getirmek mantıklıdır.

Yaptığımız en aşırı iyileştirmeye bakarsak [Q6] 2000'den fazla RU'dan yalnızca 17 RU'ya geçti; bunu, gönderileri her bir öğe başına yaklaşık 10 RU'lık bir maliyetle denormalize ederek elde ettik. Gönderilerin oluşturulmasından veya güncelleştirilmesinden çok daha fazla akış isteğinde bulunacağımızdan, genel tasarruf dikkate alındığında bu normal dışıleştirmenin maliyeti göz ardı edilebilir.

Denormalizasyon artımlı olarak uygulanabilir

Bu makalede incelediğimiz ölçeklenebilirlik iyileştirmeleri, verilerin veri kümesi genelinde normal dışı bırakma ve yinelenenleri kaldırmayı içerir. Bu iyileştirmelerin ilk günde yerine getirilmesi gerekmediği belirtilmelidir. Bölüm anahtarları üzerinde filtre uygulayan sorgular büyük ölçekte daha iyi performans gösterir, ancak bölümler arası sorgular nadiren veya sınırlı bir veri kümesine karşı çağrılırsa kabul edilebilir. Yalnızca bir prototip oluşturuyorsanız veya küçük ve denetimli bir kullanıcı tabanına sahip bir ürün piyasaya sürülüyorsanız, büyük olasılıkla bu iyileştirmeleri daha sonraya bırakabilirsiniz. O zaman önemli olan modelinizin performansını izlemektir ; böylece bunları ne zaman ve ne zaman getirebileceğinize karar verebilirsiniz.

Güncelleştirmeleri diğer kapsayıcılara dağıtmak için kullandığımız değişiklik akışı, tüm bu güncelleştirmeleri kalıcı olarak depolar. Bu kalıcılık, sisteminizde zaten çok fazla veri olsa bile kapsayıcının oluşturulmasından bu yana tüm güncellemeleri istemeyi ve denormalize edilmiş görünümleri tek seferlik bir güncelleme işlemi olarak başlatmayı mümkün kılar.

Sonraki Adımlar

Pratik veri modellemeye ve bölümlemeye giriş yaptıktan sonra, kavramları gözden geçirmek için aşağıdaki makaleleri gözden geçirmek isteyebilirsiniz: