Zero-Padding delle Sort Key in DynamoDB
Una sort key string di DynamoDB si ordina lessicograficamente — un carattere alla
volta, da sinistra a destra — non numericamente. Quindi "10" finisce prima di "2", perché
"1" viene prima di "2". Lo zero-padding a una larghezza fissa è il modo per far corrispondere l'ordine
delle stringhe all'ordine numerico.
Perché "10" si ordina prima di "2" in una sort key di DynamoDB?
Perché una sort key string di DynamoDB viene confrontata lessicograficamente per ordine di byte UTF-8, non numericamente. Il byte di "1" precede "2", quindi "10" finisce prima di "2". Padda ogni numero a una larghezza fissa con zeri iniziali — "2" diventa "0000000002" — e l'ordine delle stringhe corrisponderà esattamente all'ordine numerico.
- La trappola: numeri memorizzati come stringhe si ordinano come parole.
"100","11","2"è l'ordine che DynamoDB ti dà — non quello che intendevi. - La soluzione: padda ogni numero a una larghezza fissa con zeri iniziali, così
"2"diventa"0000000002". Ora ordine lessicografico e numerico concordano. - Scegli la larghezza una volta: dimensionala per il valore più grande che mai memorizzerai, poi aggiungi qualche cifra. Cambiare la larghezza più tardi significa riscrivere ogni chiave.
- Discendente gratis: per ordinare dal più alto al più basso (il caso leaderboard), memorizza
maxValue - value, anch'esso zero-paddato — DynamoDB non ha una direzione di sort per attributo.
Perché le sort key string ti tradiscono
Venendo da SQL, un ORDER BY score DESC su una colonna intera "funziona e basta" —
il motore sa che la colonna è numerica. DynamoDB non ha questo lusso per una sort
key che non è di tipo Number.
DynamoDB confronta le sort key string (S) per ordine di byte UTF-8, secondo la
documentazione AWS sulle sort key.
Byte, non grandezza. "9" (0x39) supera "10" perché il suo primo byte batte
"1" (0x31). La lunghezza è irrilevante — decide solo il primo byte differente.
Questo è il footgun: nel momento in cui un numero vive dentro una sort key string, ogni
Query che percorre il range restituisce righe in un ordine che sembra scomposto.
Costruisci una sort key per leaderboard
Prendi una leaderboard arcade stagionale. Una item collection per stagione contiene ogni run di ogni giocatore, e vuoi i punteggi più alti per primi.
Modellala con una composite key in un'unica item collection:
leaderboardId(chiave di partizione) — es.SEASON#2026-SPRING.rankKey(sort key) — il punteggio zero-paddato più un tiebreaker.
Un primo tentativo ingenuo memorizza il punteggio grezzo come stringa:
| leaderboardId | rankKey | playerHandle |
|---|---|---|
| SEASON#2026-SPRING | "9" | quickdraw |
| SEASON#2026-SPRING | "10" | ace_pilot |
| SEASON#2026-SPRING | "1500" | nightowl |
| SEASON#2026-SPRING | "240" | bytecrash |
Una Query su SEASON#2026-SPRING li restituisce in questo ordine di byte:
"10", "1500", "240", "9". La run da 9 punti sta dead last e la
run da 1500 punti è sepolta nel mezzo. Inutile per una leaderboard.
Padda a una larghezza fissa
Scegli una larghezza abbastanza ampia per il punteggio più grande che mai registrerai, poi padda a sinistra con zeri. Diciamo che i punteggi arrivano al massimo a dieci milioni — sono otto cifre, quindi usa dieci cifre per margine:
| leaderboardId | rankKey | playerHandle |
|---|---|---|
| SEASON#2026-SPRING | "0000000009" | quickdraw |
| SEASON#2026-SPRING | "0000000010" | ace_pilot |
| SEASON#2026-SPRING | "0000000240" | bytecrash |
| SEASON#2026-SPRING | "0000001500" | nightowl |
Ora ogni chiave ha la stessa lunghezza, quindi il confronto byte-per-byte e il confronto
numerico producono l'ordine identico. Una Query ascendente dà 9, 10, 240, 1500. La matematica finalmente corrisponde ai byte.
La larghezza è una porta a senso unico. Se paddi a dieci cifre e un punteggio successivamente la
supera, un valore a 11 cifre si ordina prima di uno a 10 cifre — ri-rompendo tutto —
e correggerlo significa riscrivere ogni rankKey esistente. Sovradimensiona la larghezza;
il costo è una manciata di byte.
Ordina discendente: memorizza la differenza
Una leaderboard vuole il punteggio più alto per primo. DynamoDB può leggere una sort key
in avanti o all'indietro con ScanIndexForward: false, quindi discendente è di solito un
flag in fase di lettura — reggiungilo per primo.
Ma quando una item collection deve servire direzioni di sort miste, o vuoi il
punteggio più alto fisicamente per primo indipendentemente dai flag di lettura, inverti il numero stesso.
Memorizza maxValue - score, zero-paddato alla stessa larghezza:
score inverted (9999999999 - score) rankKey
1500 9999998499 "9999998499"
240 9999999759 "9999999759"
10 9999999989 "9999999989"
9 9999999990 "9999999990"L'ordine di byte ascendente sul valore invertito ora produce i punteggi originali
dall'alto al basso: 1500, 240, 10, 9. Il trucco è nello spirito del
paper Amazon Dynamo del 2007 —
le chiavi sono byte opachi, quindi codifichi l'intento dentro i byte.
Aggiungi un tiebreaker
Due giocatori possono pareggiare. Un punteggio paddato nudo collide sulla sort key, e una seconda scrittura sovrascriverebbe la prima (stessa PK + SK). Aggiungi un suffisso univoco così ogni run è un Item distinto e i pareggi si risolvono in modo deterministico:
rankKey = "<paddedScore>#<paddedTimestamp>#<playerId>"Per esempio "0000001500#0000001719100800#p_8842". Stesso punteggio, timestamp
precedente vince lo slot più alto — padda anche il timestamp, altrimenti reintroduce
l'esatto bug che hai appena corretto.
In DynoTable puoi sfogliare la leaderboard di stagione ordinata per rankKey
zero-paddata e osservare i valori riempiti allineare correttamente le righe — prova che
le larghezze sono giuste prima di rilasciarle.
Quando assembli quella composite key a mano è facile sbagliare una larghezza.
Generare la KeyConditionExpression per una Query "top della stagione" nell'
expression builder mantiene onesta la sintassi
begins_with / between mentre sperimenti con le larghezze.

Errori da evitare
- Padding troppo stretto. L'intero schema collassa la prima volta che un valore sfora la larghezza. Dimensiona per il caso peggiore, poi aggiungi cifre.
- Dimenticare il flag di lettura. Se leggi sempre solo discendente,
ScanIndexForward: falsepotrebbe essere tutto ciò che ti serve — non reggiungere a chiavi invertite quando basta un flag. - Larghezze miste in una collection. Ogni chiave che condivide un range di sort deve usare la stessa larghezza. Una migrazione che padda le nuove righe ma non le vecchie le intervalla in modo sbagliato.
- Paddare il segmento sbagliato. In una composite key, padda ogni segmento numerico che partecipa all'ordinamento — punteggio e timestamp entrambi, non solo il punteggio.
Prossimi passi
Lo zero-padding è uno strumento nel più ampio toolkit di
design delle sort key; abbinalo alle
item collection quando sovraccarichi una chiave per servire diversi
pattern, e appoggiati a una Query precisa invece di uno
Scan una volta che l'ordinamento è giusto.
Prova DynoTable per navigare una tabella reale e osservare le tue sort key zero-paddate cadere in ordine numerico prima di rilasciare lo schema.


