Orta5 dakikalık okuma

DynamoDB Referans Sayaçları

Bir referans sayacı, kaç çocuk öğenin ona işaret ettiğini izleyen, parent öğede sakladığın bir sayıdır — bir posttaki like'lar, bir workspace'teki üyeler, bir yorumdaki yanıtlar. Tutarsın çünkü çocukları her okumada saymak çok pahalıdır.

DynamoDB'de bir sayacı nasıl tutarsın?

Çalışan toplamı parent öğede bir sayı olarak sakla ve çocuğu oluşturan yazmayla aynı yazmada güncelle. Bir ikisinin de inmesini veya hiçbirinin inmemesini sağlar ve çocuk yazmasındaki bir koşul retry'ların çift saymasını durdurur — böylece tek bir GetItem doğru bir sayı döner.

  • Çocukları okuma zamanında sayma. Like'ları saymak için bir Query, taradığı her like öğesi için öder. Toplamı postta sakla ve bunun yerine bir öğe oku.
  • Sayacı çocuğun yazıldığı yerde tut, sonra değil. Çocuğu oluşturan aynı işlemde artır ki ikisi asla sapmasın.
  • Yazma ve artırma farklı öğelere dokunuyorsa bir kullan. Bir like bir öğedir, sayı başka birinde yaşar — TransactWriteItems ikisinin de inmesini veya hiçbirinin inmemesini sağlar.
  • Tuzak çift saymadır. Artırmayı yeniden koşan retry edilmiş veya çoğaltılmış bir like sayıyı şişirir. Çocuk yazmasını bir koşulla koru.

Neden hiç say

SQL'den geliyorsan asla bir like-count saklamazdın — SELECT COUNT(*) FROM likes WHERE post_id = ? yazıp bir indeksin bunu ucuz yapmasına bırakırdın. DynamoDB'de öğeleri okumayı atlayan bir COUNT(*) yoktur.

Bir postun like'ları üzerinde bir Query, yalnızca sayıyı istesen bile o partition'daki her like öğesini okur — ve faturalandırır. Viral bir postta "kaç like?" yanıtlamak binlerce RCU'dur. Referans sayaçlarının öldürmek için var olduğu okuma tuzağı budur.

Bu yüzden edersin: çalışan toplamı postun kendisinde sakla. Sayacı okumak tek bir GetItem olur. Maliyet, onu doğru tutmanın artık sana ait olmasıdır.

Öğeleri modelle

İki öğe tipi bir partition paylaşır ki post ve like'ları bir item collection içinde otursun. Uydurulmuş anahtarlar:

Post item
PKSKattributes
POST#a91fMETAlikeTally (Number), body, authorId, createdAt
Like item
PKSKattributes
POST#a91fLIKE#USER#7c20likedAt

META öğesindeki likeTally attribute'u referans sayacıdır. Her LIKE# öğesi bir çocuktur. İkisini de PK = "POST#a91f" altına koymak, listeyi istediğinde tek bir Query'nin postu ve beğenenleri birlikte getirmesi demektir.

Sayacı atomik artır

DynamoDB bir sayıyı bir ADD (veya SET x = x + :n) ile artırır — bu bir atomik sayaçtır: DynamoDB delta'yı sunucu tarafında uygular, önce mevcut değeri okumadan, böylece eşzamanlı artırmalar birbirini ezmez. (AWS: atomik sayaçlar)

Bir postu beğenmek iki öğeye iki yazmadır — LIKE# öğesini oluştur ve META üzerindeki likeTally'ye 1 ekle. Like iner ama artırma düşerse tally sonsuza dek yanlıştır. İkisini de veya hiçbirini istersin.

TransactWriteItems'ın garanti ettiği budur — birden fazla öğe boyunca hepsi ya hiçbiri, ve herhangi bir öğe eşzamanlı değiştirilirse tüm transaction'ı iptal eder (AWS: işlemlerle kötümser kilitleme):

{
  "TransactItems": [
    {
      "Put": {
        "TableName": "Social",
        "Item": {
          "PK": {"S": "POST#a91f"},
          "SK": {"S": "LIKE#USER#7c20"},
          "likedAt": {"N": "1750636800"}
        },
        "ConditionExpression": "attribute_not_exists(SK)"
      }
    },
    {
      "Update": {
        "TableName": "Social",
        "Key": {
          "PK": {"S": "POST#a91f"},
          "SK": {"S": "META"}
        },
        "UpdateExpression": "ADD likeTally :one",
        "ExpressionAttributeValues": {":one": {"N": "1"}}
      }
    }
  ]
}

Put ve Update birlikte commit olur. Biri düşerse DynamoDB ikisini de geri alır ve bir TransactionCanceledException döner.

Çift saymaya karşı koru

Asıl bug yarı yazılmış bir like değildir — transaction bunu önler. Aynı kullanıcının iki kez beğenmesi veya bir istemci retry'sinin isteği yeniden oynamasıdır. Her replay başka bir 1 ekler ve likeTally sessizce gerçek sayının üstüne sapar.

Put üzerindeki ConditionExpression: attribute_not_exists(SK) korumadır. O kullanıcının LIKE# öğesi zaten varsa Put'un koşulu düşer, tüm transaction iptal edilir ve — kritik olarak — ADD asla çalışmaz. Kullanıcı başına bir like, anahtarla zorlanır.

Bu update ve condition expression'ları — doğru ExpressionAttributeValues ve attribute_not_exists korumasıyla — JSON'u elle birleştirmek yerine DynamoDB Expression Builder'da kur ve kopyala.

Unlike ve maliyet

Bir like'ı kaldırmak ayna görüntüsüdür: aynı transaction'da ConditionExpression: attribute_exists(SK) ile LIKE# öğesini Delete et ve ADD likeTally :minusOne. Koşul, çift-unlike'ın tally'yi negatife çekmesini durdurur.

Fiyatı bil. 1 KB'a kadar öğeler için işlemsel bir yazma öğe başına 2 WCU tutar — biri hazırlık, biri commit — düz bir yazma için 1 WCU'ya karşı. Bir like iki öğedir, yani her like kabaca dört WCU'dur. Eylem başına ucuz ama bir ünlü postu like fırtınası almadan önce bilmeye değer.

DynoTable'da görüntüleyin

Bir tally'nin saptığından şüphelenince, saklanan likeTally'yi gerçek LIKE# çocuk sayısıyla karşılaştırmak istersin — prod'da bir count sorgusu çalıştırmadan.

Post META öğesi, LIKE# çocuklarıyla aynı item collection'da yan yana; saklanan tally'yi gerçek çocuk sayısına gözle karşılaştırabilirsin.
Post META öğesi, LIKE# çocuklarıyla aynı item collection'da yan yana; saklanan tally'yi gerçek çocuk sayısına gözle karşılaştırabilirsin.

Sınırlı bir post kümesi boyunca gerçek bir mutabakat için — "hangi tally'ler çocuk sayılarına uymuyor?" — DynoTable'ın SQL Workbench'i düz PartiQL'in ifade edemediği GROUP BY ve join'i yüklediğin satırlar üzerinde istemci tarafında çalıştırır.

Tuzaklar ve sonraki adımlar

  • Sayacı bant dışında tutma (geceleri yeniden sayan bir Lambda). Bu, baştan işlemsel olması gereken bir yazma yolu üzerinde yara bandıdır.
  • 'lara dikkat. Tek bir vahşi popüler post her like'ı — ve her tally artırmasını — bir partition key'de yoğunlaştırır. Sayı doğrudur; partition yine de kısıtlayabilir.
  • Nadiren mutabakat et, cerrahi onar. Her mutation koşulluysa drift neredeyse sıfır olmalıdır. Uyuşmazlığı üzerine yazılacak bir sayı değil, bulunacak bir bug olarak ele al.

İlgili okuma: post ve like'ların neden bir partition paylaştığı için single-table design, ve çocukları okuma zamanında saymanın kaçındığın desen olduğu için Query vs Scan.

Sonra bu item collection'ları incelemek ve tally'lerini kendi tablolarına karşı doğrulamak için DynoTable'ı indir.

Güncellendi