Menengah5 menit baca

Kunci Sortir Tanpa Bantalan di DynamoDB

String DynamoDB diurutkan secara leksikografis — satu karakter pada a waktu, kiri ke kanan — bukan secara numerik. Jadi "10" mendarat sebelum "2", karena "1" hadir sebelum "2". Zero-padding dengan lebar tetap adalah cara Anda membuat string pesanan cocok dengan urutan numerik.

Mengapa "10" mengurutkan sebelum "2" di kunci pengurutan DynamoDB?

Karena string DynamoDB dibandingkan secara leksikografis berdasarkan urutan byte UTF-8, bukan secara numerik. Byte untuk "1" mendahului "2", jadi "10" mendarat sebelum "2". Padukan setiap angka ke lebar tetap dengan angka nol di depannya — "2" menjadi "0000000002" — dan urutan string kemudian cocok dengan urutan numerik dengan tepat.

  • Urutan leksikografis: angka yang disimpan sebagai string diurutkan seperti kata. "100", "11", "2" adalah perintah yang diberikan DynamoDB kepada Anda — bukan yang Anda maksudkan.
  • Perbaikannya: masukkan setiap angka ke lebar tetap dengan nol di depannya, jadi "2" menjadi "0000000002". Sekarang urutan leksikografis dan numerik setuju.
  • Pilih lebar satu kali: sesuaikan dengan nilai terbesar yang pernah Anda simpan, lalu tambahkan beberapa digit. Mengubah lebarnya nanti berarti menulis ulang setiap kunci.
  • Descending gratis: untuk mengurutkan tinggi ke rendah (kotak peringkat), simpan maxValue - value, juga tanpa bantalan — DynamoDB tidak memiliki pengurutan per atribut arah.

Mengapa kunci pengurutan string mengkhianati Anda

Berasal dari SQL, ORDER BY score DESC pada kolom integer "berfungsi" - mesin mengetahui kolomnya numerik. DynamoDB tidak memiliki kemewahan seperti itu kunci yang bukan tipe Number.

DynamoDB membandingkan kunci pengurutan string (S) berdasarkan urutan byte UTF-8, sesuai dengan Dokumentasi kunci pengurutan AWS. Byte, bukan besaran. "9" (0x39) mengungguli "10" karena byte pertamanya mengalahkan "1" (0x31). Panjangnya tidak relevan — hanya byte pertama yang berbeda yang menentukan.

Itulah langkahnya: saat sebuah angka berada di dalam kunci pengurutan string, semuanya Query yang berjalan dalam rentang tersebut mengembalikan baris dalam urutan yang terlihat acak-acakan.

Buat kunci pengurutan papan peringkat

Ambil papan peringkat arcade musiman. Satu per musim berlaku setiap lari pemain, dan Anda ingin skor tertinggi terlebih dahulu.

Modelkan dengan dalam satu koleksi item:

  • leaderboardId (kunci partisi) — mis. SEASON#2026-SPRING.
  • rankKey (kunci pengurutan) — skor dengan bantalan nol ditambah pemecah tiebreak.

Upaya pertama yang naif menyimpan skor mentah sebagai string:

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

Query di SEASON#2026-SPRING mengembalikannya dalam urutan byte ini: "10", "1500", "240", "9". Lari 9 poin berada di posisi terakhir dan Lari 1500 poin terkubur di tengah. Tidak berguna untuk papan peringkat.

Bantalan dengan lebar tetap

Pilih lebar yang cukup lebar untuk skor terbesar yang pernah Anda rekam, lalu pad kiri dengan angka nol. Katakanlah skor dibatasi hingga sepuluh juta — itu berarti delapan digit, jadi gunakan sepuluh digit untuk ruang kepala:

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

Sekarang setiap kunci memiliki panjang yang sama, jadi perbandingan byte demi byte dan numerik perbandingan menghasilkan urutan yang sama. Query menaik menghasilkan 9, 10, 240, 1500. Perhitungannya akhirnya cocok dengan byte.

Lebarnya adalah pintu satu arah. Jika Anda menambahkan hingga sepuluh digit dan skor kemudian melebihi itu, nilai 11 digit mengurutkan sebelum menjadi 10 digit — memecah ulang semuanya — dan memperbaikinya berarti menulis ulang setiap rankKey yang ada. Penyediaan lebar yang berlebihan; biayanya hanya beberapa byte.

Sortir descending: simpan selisihnya

Papan peringkat menginginkan skor tertinggi terlebih dahulu. DynamoDB dapat membaca kunci pengurutan maju atau mundur dengan ScanIndexForward: false, jadi descending biasanya a tanda waktu baca — raihlah itu terlebih dahulu.

Namun ketika satu koleksi item harus melayani arah pengurutan campuran, atau Anda menginginkannya skor teratas secara fisik terlebih dahulu terlepas dari bendera yang dibaca, balikkan nomor itu sendiri. Simpan maxValue - score, dengan bantalan nol dengan lebar yang sama:

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

Menaikkan urutan byte di atas nilai yang dibalik sekarang menghasilkan skor asli tinggi ke rendah: 1500, 240, 10, 9. Caranya ada di Makalah Amazon Dynamo 2007's spirit - kunci adalah byte buram, jadi Anda mengkodekan maksud ke dalam byte.

Tambahkan pemecah dasi

Dua pemain bisa seri. Skor yang empuk bertabrakan dengan tombol pengurutan, dan yang kedua write akan menimpa yang pertama (PK + SK yang sama). Tambahkan akhiran unik untuk masing-masingnya run adalah item yang berbeda dan ikatannya diselesaikan secara deterministik:

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

Misalnya "0000001500#0000001719100800#p_8842". Skor yang sama, sebelumnya stempel waktu memenangkan slot yang lebih tinggi — masukkan stempel waktu juga, atau stempel waktu tersebut akan diperkenalkan kembali bug sebenarnya yang baru saja Anda perbaiki.

Di DynoTable, Anda dapat menelusuri papan peringkat musim yang diurutkan berdasarkan rankKey dengan bantalan nol dan lihat nilai yang diisi menyejajarkan baris dengan benar — buktikan bahwa lebarnya tepat sebelum Anda mengirimkannya.

Merakit kunci komposit itu dengan tangan, mudah untuk mengetahui lebarnya. Menghasilkan KeyConditionExpression untuk Query "musim terbaik" di pembuat ekspresi mempertahankan begins_with / Sintaks between jujur saat Anda bereksperimen dengan lebar.

<gambar kelas="doc-media-placeholder" jenis data="tangkapan layar" data-src="docs/guide-dynamodb-zero-padding-sort-leaderboard.png"

Menjelajahi papan peringkat musim di DynoTable, diurutkan berdasarkan rankKey tanpa bantalan.

Jebakan yang harus dihindari

  • Padding terlalu sempit. Seluruh skema akan diciutkan saat nilai pertama kali dimasukkan melebihi lebarnya. Ukuran untuk kasus terburuk, lalu tambahkan angka.
  • Lupa tanda baca. Jika Anda hanya membaca descending, ScanIndexForward: false mungkin yang Anda butuhkan — jangan menggunakan kunci terbalik ketika sebuah bendera melakukannya.
  • Lebar campuran dalam satu koleksi. Setiap kunci yang berbagi rentang pengurutan harus menggunakan lebar yang sama. Migrasi yang memasukkan baris-baris baru tetapi bukan baris-baris lama akan menyisipkannya salah.
  • Mengisi segmen yang salah. Di kunci komposit, mengisi setiap segmen numerik yang berpartisipasi dalam pengurutan — skor dan stempel waktu keduanya, bukan hanya skor.

Langkah selanjutnya

Zero-padding adalah salah satu alat yang lebih luas desain tombol sortir toolkit; pasangkan dengan koleksi item ketika Anda membebani kunci secara berlebihan untuk menyajikan beberapa pola, dan bersandar pada Query yang tepat, bukan a Scan setelah urutannya benar.

Coba DynoTable untuk menelusuri tabel sebenarnya dan melihat pengurutan tanpa bantalan Anda kunci disusun dalam urutan numerik sebelum Anda mengirimkan skema.

Diperbarui