Intermedio5 min di lettura

Chiavi di ordinamento con riempimento zero in DynamoDB

Una stringa DynamoDB ordina lessicograficamente: un carattere alla volta tempo, da sinistra a destra, non numericamente. Quindi "10" arriva prima di "2", perché "1" viene prima di "2". L'imbottitura zero su una larghezza fissa è il modo in cui crei una stringa l'ordine corrisponde all'ordine numerico.

Perché "10" viene ordinato prima di "2" in una chiave di ordinamento DynamoDB?

Perché una stringa DynamoDB viene confrontata lessicograficamente in base all'ordine dei byte UTF-8, non numericamente. Il byte per "1" precede "2", quindi "10" arriva prima di "2". Riempi ogni numero con una larghezza fissa con zeri iniziali ("2" diventa "0000000002") e l'ordine delle stringhe corrisponde quindi esattamente all'ordine numerico.

  • Ordine lessicografico: i numeri memorizzati come stringhe vengono ordinati come parole. "100", "11", "2" è l'ordine che ti dà DynamoDB, non quello che intendevi.
  • La correzione: riempi ogni numero con una larghezza fissa con zeri iniziali, quindi "2" diventa "0000000002". Ora l'ordine lessicografico e quello numerico coincidono.
  • Scegli una larghezza una volta: dimensionala per il valore più grande che potrai mai memorizzare, quindi aggiungi alcune cifre. Cambiare la larghezza in seguito significa riscrivere ogni chiave.
  • Discendente gratuito: per ordinare dal più alto al più basso (il caso della classifica), archivia maxValue - value, anch'esso con riempimento zero: DynamoDB non ha un ordinamento per attributo direzione.

Perché le chiavi di ordinamento delle stringhe ti tradiscono

Proveniente da SQL, un ORDER BY score DESC su una colonna intera "funziona e basta" — il motore sa che la colonna è numerica. DynamoDB non ha un lusso del genere per un tipo chiave che non sia di tipo Number.

DynamoDB confronta le chiavi di ordinamento delle stringhe (S) in base all'ordine dei byte UTF-8, secondo Documentazione sulla chiave di ordinamento AWS. Byte, non grandezza. "9" (0x39) supera "10" perché il suo primo byte batte "1" (0x31). La lunghezza è irrilevante: decide solo il primo byte diverso.

Questa è la pistola: nel momento in cui un numero vive all'interno di una chiave di ordinamento di stringhe, ogni Query che percorre l'intervallo restituisce le righe in un ordine che sembra criptato.

Crea una chiave di ordinamento della classifica

Prendi una classifica arcade stagionale. Un per stagione vale ogni corsa del giocatore e vuoi prima i punteggi più alti.

Modellalo con un in un'unica collezione di articoli:

  • leaderboardId (chiave di partizione) — ad es. SEASON#2026-SPRING.
  • rankKey (chiave di ordinamento): il punteggio riempito con zero più un tie-break.

Un primo tentativo ingenuo memorizza il punteggio grezzo come una stringa:

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

Un Query su SEASON#2026-SPRING li restituisce in questo ordine di byte: "10", "1500", "240", "9". La corsa da 9 punti è ultima e il La corsa da 1500 punti è sepolta nel mezzo. Inutile per una classifica.

Pad ad una larghezza fissa

Scegli una larghezza sufficientemente ampia per il punteggio più grande che tu abbia mai registrato, quindi premi il tastierino sinistro con zeri. Supponiamo che il limite massimo dei punteggi sia dieci milioni: sono otto cifre, quindi usa dieci cifre per l'headroom:

leaderboardIdrankKeyplayerHandle
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 confronto byte per byte e numerico il confronto produce l'ordine identico. L'ascendente Query9, 10, 240, 1500. La matematica finalmente corrisponde ai byte.

La larghezza è una porta a senso unico. Se inserisci fino a dieci cifre e un punteggio successivo supera cioè, un valore di 11 cifre viene ordinato prima di uno di 10 cifre - ricomponendo tutto - e risolverlo significa riscrivere ogni rankKey esistente. Fornire un eccesso di larghezza; il costo è una manciata di byte.

Ordinamento discendente: memorizza la differenza

Una classifica vuole prima il punteggio più alto. DynamoDB può leggere una chiave di ordinamento avanti o indietro con ScanIndexForward: false, quindi la discesa è solitamente a flag in tempo di lettura: prendilo per primo.

Ma quando una raccolta di elementi deve fornire indicazioni di ordinamento miste, oppure si desidera il file il punteggio più alto fisicamente per primo, indipendentemente dai flag di lettura, capovolgi il numero stesso. Memorizza maxValue - score, con riempimento zero alla stessa larghezza:

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

L'ordine crescente dei byte rispetto al valore invertito ora produce i punteggi originali da alto a basso: 1500, 240, 10, 9. Il trucco sta nel Documento Amazon Dynamo del 2007. spirito: le chiavi sono byte opachi, quindi codifichi l'intento nei byte.

Aggiungi un tie-break

Due giocatori possono pareggiare. Una partitura imbottita nuda si scontra con la chiave di ordinamento e una seconda write sovrascriverebbe il primo (stesso PK + SK). Aggiungi un suffisso univoco in modo che ciascuno run è un elemento distinto e i legami si risolvono in modo deterministico:

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

Ad esempio "0000001500#0000001719100800#p_8842". Stesso punteggio, prima timestamp vince lo slot più alto: riempi anche il timestamp o reintroduce il file esatto bug che hai appena corretto.

In DynoTable, puoi sfogliare la classifica della stagione ordinata in base all'rankKey con riempimento zero e osserva i valori riempiti allineare correttamente le righe: dimostra che le larghezze sono corrette prima di spedirli.

Assemblando quella chiave composita a mano, è facile darci un'occhiata. Generando il KeyConditionExpression per un "top della stagione" Query nella costruttore di espressioni mantiene begins_with / Sintassi between onesta mentre sperimenti le larghezze.

Sfogliando la classifica stagionale in DynoTable, ordinata per rankingKey con zero.
Sfogliando la classifica stagionale in DynoTable, ordinata per rankingKey con zero.

Insidie da evitare

  • Padding troppo stretto. L'intero schema crolla la prima volta che viene inserito un valore supera la larghezza. Dimensioni per il caso peggiore, quindi aggiungi cifre.
  • Dimenticando il flag di lettura. Se leggi solo discendente, ScanIndexForward: false potrebbe essere tutto ciò di cui hai bisogno: non cercare chiavi invertite quando lo fa un flag.
  • Larghezze miste in un'unica raccolta. Ogni chiave che condivide un intervallo di ordinamento deve utilizzare il file stessa larghezza. Una migrazione che riempie le nuove righe ma non quelle vecchie le intercala erroneamente.
  • Riempimento del segmento sbagliato. In una chiave composta, riempi ogni segmento numerico che partecipa all'ordinamento: punteggio e timestamp entrambi, non solo il punteggio.

Passaggi successivi

Il riempimento zero è uno strumento più ampio toolkit progettazione chiave di ordinamento; accoppiarlo con raccolte di elementi quando si sovraccarica una chiave per servirne diversi modelli e appoggiarsi a un Query preciso anziché a Scan una volta che l'ordine è corretto.

Prova DynoTable per sfogliare una tabella reale e osservare il tuo ordinamento con riempimento zero le chiavi rientrano nell'ordine numerico prima di spedire lo schema.

Aggiornato