Menengah5 menit baca

Zero-Padding Sort Key di DynamoDB

Sebuah sort key string DynamoDB terurut secara leksikografis — satu karakter pada satu waktu, kiri ke kanan — bukan secara numerik. Jadi "10" mendarat sebelum "2", karena "1" datang sebelum "2". Zero-padding ke lebar tetap adalah cara Anda membuat urutan string cocok dengan urutan numerik.

Mengapa "10" terurut sebelum "2" pada sort key DynamoDB?

Karena sort key string DynamoDB dibandingkan secara leksikografis berdasarkan urutan byte UTF-8, bukan secara numerik. Byte untuk "1" mendahului "2", sehingga "10" mendarat sebelum "2". Pad setiap number ke lebar tetap dengan nol di depan — "2" menjadi "0000000002" — dan urutan string kemudian cocok dengan urutan numerik secara tepat.

  • Jebakannya: number yang disimpan sebagai string terurut seperti kata. "100", "11", "2" adalah urutan yang DynamoDB berikan — bukan yang Anda maksud.
  • Solusinya: pad setiap number ke lebar tetap dengan nol di depan, sehingga "2" menjadi "0000000002". Kini urutan leksikografis dan numerik sepakat.
  • Pilih lebar sekali: ukur ia untuk nilai terbesar yang akan pernah Anda simpan, lalu tambah beberapa digit. Mengubah lebarnya nanti berarti menulis ulang setiap key.
  • Menurun secara gratis: untuk mengurutkan tinggi-ke-rendah (kasus leaderboard), simpan maxValue - value, juga ter-zero-pad — DynamoDB tidak punya arah sort per-atribut.

Mengapa sort key string mengkhianati Anda

Datang dari SQL, sebuah ORDER BY score DESC atas kolom integer "tinggal jalan" — engine tahu kolomnya numerik. DynamoDB tak punya kemewahan semacam itu untuk sort key yang bukan tipe Number.

DynamoDB membandingkan sort key string (S) berdasarkan urutan byte UTF-8, per dokumentasi sort-key AWS. Byte, bukan besaran. "9" (0x39) mengungguli "10" karena byte pertamanya mengalahkan "1" (0x31). Panjang tidak relevan — hanya byte berbeda pertama yang menentukan.

Itulah jebakannya: begitu sebuah number tinggal di dalam sort key string, setiap Query yang menelusuri rentang mengembalikan baris dalam urutan yang tampak teracak.

Bangun sort key leaderboard

Ambil sebuah leaderboard arcade musiman. Satu item collection per musim menampung setiap run pemain, dan Anda ingin skor teratas lebih dulu.

Modelkan ia dengan composite key dalam satu item collection:

  • leaderboardId (partition key) — mis. SEASON#2026-SPRING.
  • rankKey (sort key) — skor yang ter-zero-pad plus sebuah tiebreaker.

Upaya naif pertama 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

Sebuah Query pada SEASON#2026-SPRING mengembalikan mereka dalam urutan byte ini: "10", "1500", "240", "9". Run 9-poin duduk di paling buntut dan run 1500-poin terkubur di tengah. Tidak berguna untuk leaderboard.

Pad ke lebar tetap

Pilih lebar yang cukup untuk skor terbesar yang akan pernah Anda catat, lalu left-pad dengan nol. Misalkan skor maksimal sepuluh juta — itu delapan digit, jadi gunakan sepuluh digit untuk ruang lebih:

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

Kini setiap key sama panjang, jadi perbandingan byte-demi-byte dan perbandingan numerik menghasilkan urutan yang identik. Query menaik memberi 9, 10, 240, 1500. Matematikanya akhirnya cocok dengan byte-nya.

Lebar adalah pintu satu arah. Jika Anda mem-pad ke sepuluh digit dan sebuah skor kemudian melampauinya, sebuah nilai 11-digit terurut sebelum yang 10-digit — merusak ulang segalanya — dan memperbaikinya berarti menulis ulang setiap rankKey yang ada. Over-provision lebarnya; biayanya hanya segenggam byte.

Urutkan menurun: simpan selisihnya

Sebuah leaderboard ingin skor tertinggi lebih dulu. DynamoDB bisa membaca sebuah sort key maju atau mundur dengan ScanIndexForward: false, jadi menurun biasanya sebuah flag saat-baca — jangkau itu lebih dulu.

Tapi saat satu item collection harus melayani arah sort campuran, atau Anda ingin skor teratas secara fisik lebih dulu terlepas dari flag baca, balik number-nya sendiri. Simpan maxValue - score, ter-zero-pad ke lebar yang sama:

score   inverted (9999999999 - score)   rankKey
1500    9999998499                       "9999998499"
240     9999999759                       "9999999759"
10      9999999989                       "9999999989"
9       9999999990                       "9999999990"

Urutan byte menaik atas nilai terbalik kini menghasilkan skor asli tinggi-ke-rendah: 1500, 240, 10, 9. Triknya ada dalam semangat paper Amazon Dynamo 2007 — key adalah byte opak, jadi Anda meng-encode maksud ke dalam byte-nya.

Tambahkan tiebreaker

Dua pemain bisa seri. Sebuah skor ter-pad telanjang bertabrakan pada sort key, dan penulisan kedua akan menimpa yang pertama (PK + SK sama). Tambahkan sebuah suffix unik agar setiap run adalah item yang berbeda dan seri terselesaikan secara deterministik:

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

Misalnya "0000001500#0000001719100800#p_8842". Skor sama, timestamp lebih awal memenangkan slot lebih tinggi — pad timestamp-nya juga, atau ia memperkenalkan ulang bug persis yang baru Anda perbaiki.

Di DynoTable, Anda dapat menelusuri leaderboard musim yang diurutkan berdasarkan rankKey yang ter-zero-pad dan menyaksikan nilai-nilai yang ter-pad berbaris dengan benar — bukti bahwa lebar sudah tepat sebelum Anda men-deploy-nya.

Saat Anda merakit composite key itu dengan tangan, mudah salah-pencet lebar. Menghasilkan KeyConditionExpression untuk Query "puncak musim" di expression builder menjaga sintaks begins_with / between tetap jujur saat Anda bereksperimen dengan lebar.

Menjelajahi leaderboard musim di DynoTable, terurut berdasarkan rankKey yang ter-zero-pad.
Menjelajahi leaderboard musim di DynoTable, terurut berdasarkan rankKey yang ter-zero-pad.

Jebakan yang harus dihindari

  • Padding terlalu sempit. Seluruh skema runtuh pertama kali sebuah nilai melampaui lebarnya. Ukur untuk kasus terburuk, lalu tambah digit.
  • Lupa flag baca. Jika Anda hanya pernah membaca menurun, ScanIndexForward: false mungkin sudah cukup — jangan jangkau key terbalik saat sebuah flag melakukannya.
  • Lebar campuran dalam satu collection. Setiap key yang berbagi sebuah rentang sort harus memakai lebar yang sama. Sebuah migrasi yang mem-pad baris baru tapi bukan yang lama menyisipkan mereka secara salah.
  • Mem-pad segmen yang salah. Dalam sebuah composite key, pad setiap segmen numerik yang berpartisipasi dalam pengurutan — skor dan timestamp keduanya, bukan hanya skornya.

Langkah berikutnya

Zero-padding adalah satu alat dalam perangkat desain sort-key yang lebih luas; pasangkan ia dengan item collection saat Anda meng-overload sebuah key untuk melayani beberapa pola, dan andalkan sebuah Query yang presisi alih-alih sebuah Scan begitu pengurutannya benar.

Coba DynoTable untuk menjelajahi tabel nyata dan amati sort key ter-zero-pad Anda jatuh ke urutan numerik sebelum Anda merilis skemanya.

Diperbarui