Orta5 dakikalık okuma

DynamoDB'de Sıralama Anahtarlarını Sıfırla Doldurmak

DynamoDB'de string bir sayısal olarak değil, sözlük düzeninde — soldan sağa, karakter karakter — sıralanır. Yani "10", "2"den önce gelir; çünkü "1", "2"den önce gelir. String sırasını sayısal sırayla örtüştürmenin yolu, sabit bir genişliğe kadar sıfırla doldurmaktır.

Bir DynamoDB sıralama anahtarında "10" neden "2"den önce sıralanır?

Çünkü DynamoDB'de string bir sayısal olarak değil, UTF-8 bayt sırasına göre sözlük düzeninde karşılaştırılır. "1" baytı "2"den önce gelir, dolayısıyla "10" önce düşer. Her sayıyı başına sıfır ekleyerek sabit bir genişliğe doldurun — "2" artık "0000000002" olur — ve string sırası tam olarak sayısal sırayla örtüşür.

  • Sözlük sırası: string olarak saklanan sayılar kelime gibi sıralanır. DynamoDB size "100", "11", "2" sırasını verir — kastettiğinizi değil.
  • Çözüm: her sayıyı başına sıfır ekleyerek sabit bir genişliğe doldurun, böylece "2" artık "0000000002" olur. Artık sözlük sırası ile sayısal sıra aynı fikirdedir.
  • Genişliği bir kez seçin: onu saklayacağınız en büyük değere göre boyutlandırın, sonra birkaç hane daha ekleyin. Genişliği sonradan değiştirmek her anahtarı yeniden yazmak demektir.
  • Azalan sıra bedava: yüksekten alçağa sıralamak için (lider tablosu durumu) maxValue - value değerini, yine sıfırla doldurarak saklayın — DynamoDB'de attribute başına sıralama yönü yoktur.

String sıralama anahtarları sizi neden yarı yolda bırakır

SQL'den gelirken bir tamsayı sütunu üzerindeki ORDER BY score DESC "kendiliğinden çalışır" — motor sütunun sayısal olduğunu bilir. DynamoDB'de Number türünde olmayan bir sıralama anahtarı için böyle bir lüks yoktur.

DynamoDB, string (S) sıralama anahtarlarını AWS sıralama anahtarı belgelerine göre UTF-8 bayt sırasıyla karşılaştırır. Büyüklükle değil, baytlarla. "9" (0x39), ilk baytı "1"i (0x31) yendiği için "10"u geçer. Uzunluk önemsizdir — kararı yalnızca ilk farklı bayt verir.

Tuzak da budur: bir sayı string bir sıralama anahtarının içine girdiği anda, aralığı gezen her Query satırları karmakarışık görünen bir sırayla döndürür.

Bir lider tablosu sıralama anahtarı kurun

Sezonluk bir atari lider tablosu düşünün. Sezon başına bir her oyuncunun turunu tutuyor ve siz en yüksek skorların önce gelmesini istiyorsunuz.

Bunu tek bir öğe koleksiyonu içinde bir modelleyin:

  • leaderboardId (bölüm anahtarı) — örneğin SEASON#2026-SPRING.
  • rankKey (sıralama anahtarı) — sıfırla doldurulmuş skor artı bir eşitlik bozucu.

Naif bir ilk deneme, ham skoru string olarak saklar:

leaderboardIdrankKeyplayerHandle
SEASON#2026-SPRING"9"quickdraw
SEASON#2026-SPRING"10"ace_pilot
SEASON#2026-SPRING"1500"nightowl
SEASON#2026-SPRING"240"bytecrash

SEASON#2026-SPRING üzerindeki bir Query onları şu bayt sırasında döndürür: "10", "1500", "240", "9". 9 puanlık tur en sonda kalır ve 1500 puanlık tur ortalarda kaybolur. Bir lider tablosu için kullanışsız.

Sabit bir genişliğe doldurun

Kaydedeceğiniz en büyük skoru alacak kadar geniş bir genişlik seçin, sonra soldan sıfırla doldurun. Diyelim ki skorlar on milyonda tavan yapıyor — bu sekiz hanedir, o hâlde pay bırakmak için on hane kullanın:

leaderboardIdrankKeyplayerHandle
SEASON#2026-SPRING"0000000009"quickdraw
SEASON#2026-SPRING"0000000010"ace_pilot
SEASON#2026-SPRING"0000000240"bytecrash
SEASON#2026-SPRING"0000001500"nightowl

Artık her anahtar aynı uzunlukta, dolayısıyla bayt bayt karşılaştırma ile sayısal karşılaştırma birebir aynı sırayı üretir. Artan Query size 9, 10, 240, 1500 verir. Matematik nihayet baytlarla uyuşuyor.

Genişlik tek yönlü bir kapıdır. On haneye doldurup sonra bir skor bunu aşarsa, 11 haneli bir değer 10 haneli olanın önünde sıralanır — her şey yeniden bozulur — ve düzeltmek mevcut her rankKey'i yeniden yazmak demektir. Genişliği bol tutun; maliyeti bir avuç bayttır.

Azalan sıralama: farkı saklayın

Bir lider tablosu en yüksek skorun önce gelmesini ister. DynamoDB bir sıralama anahtarını ScanIndexForward: false ile ileri ya da geri okuyabilir, dolayısıyla azalan sıra genellikle okuma zamanında bir bayraktır — önce ona başvurun.

Ama tek bir öğe koleksiyonunun karışık sıralama yönlerine hizmet etmesi gerektiğinde ya da okuma bayraklarından bağımsız olarak en yüksek skorun fiziksel olarak başta olmasını istediğinizde, sayının kendisini ters çevirin. maxValue - score değerini aynı genişliğe sıfırla doldurarak saklayın:

scoreinverted (9999999999 - score)rankKey
15009999998499"9999998499"
2409999999759"9999999759"
109999999989"9999999989"
99999999990"9999999990"

Ters çevrilmiş değer üzerindeki artan bayt sırası artık orijinal skorları yüksekten alçağa veriyor: 1500, 240, 10, 9. Numara, 2007 Amazon Dynamo makalesinin ruhundadır — anahtarlar opak baytlardır, siz de niyeti baytların içine kodlarsınız.

Bir eşitlik bozucu ekleyin

İki oyuncu berabere kalabilir. Çıplak doldurulmuş bir skor sıralama anahtarında çakışır ve ikinci yazma birincinin üzerine yazar (aynı PK + SK). Her tur ayrı bir öğe olsun ve beraberlikler deterministik çözülsün diye benzersiz bir sonek ekleyin:

rankKey = "<paddedScore>#<paddedTimestamp>#<playerId>"

Örneğin "0000001500#0000001719100800#p_8842". Aynı skorda daha erken zaman damgası üst sırayı kazanır — zaman damgasını da doldurun, yoksa az önce düzelttiğiniz hatayı geri getirirsiniz.

DynoTable'da sezon lider tablosuna sıfırla doldurulmuş rankKey'e göre sıralanmış olarak göz atabilir ve doldurulmuş değerlerin satırları doğru hizaladığını görebilirsiniz — genişliklerin doğru olduğunun, canlıya çıkmadan önceki kanıtı.

O bileşik anahtarı elle kurarken bir genişliği yanlış yazmak kolaydır. "Sezonun zirvesi" Query'si için KeyConditionExpression üretimini expression builder içinde yapmak, siz genişliklerle oynarken begins_with / between sözdizimini dürüst tutar.

DynoTable'da sezon lider tablosuna, sıfırla doldurulmuş rankKey'e göre sıralanmış hâlde göz atmak.
DynoTable'da sezon lider tablosuna, sıfırla doldurulmuş rankKey'e göre sıralanmış hâlde göz atmak.

Kaçınılması gereken tuzaklar

  • Çok dar doldurma. Bir değer genişliği taşırdığı ilk anda bütün düzen çöker. En kötü duruma göre boyutlandırın, sonra hane ekleyin.
  • Okuma bayrağını unutmak. Yalnızca azalan sırada okuyorsanız ScanIndexForward: false tek ihtiyacınız olabilir — bir bayrağın yaptığı iş için ters çevrilmiş anahtarlara uzanmayın.
  • Tek koleksiyonda karışık genişlikler. Aynı sıralama aralığını paylaşan her anahtar aynı genişliği kullanmalıdır. Yeni satırları dolduran ama eskileri doldurmayan bir göç, onları yanlış iç içe geçirir.
  • Yanlış parçayı doldurmak. Bileşik bir anahtarda, sıralamaya katılan her sayısal parçayı doldurun — yalnızca skoru değil, skorla birlikte zaman damgasını da.

Sonraki adımlar

Sıfırla doldurma, daha geniş sıralama anahtarı tasarımı araç kutusundaki araçlardan biridir; bir anahtarı birden fazla desene hizmet edecek şekilde aşırı yüklediğinizde bunu öğe koleksiyonlarıyla eşleştirin ve sıralama doğru olduğunda bir Scan yerine kesin bir Query'ye yaslanın.

Gerçek bir tabloya göz atmak ve şemayı göndermeden önce sıfırla doldurulmuş sıralama anahtarlarınızın sayısal sıraya oturuşunu izlemek için DynoTable'ı deneyin.

Güncellendi