Intermedio6 min de lectura

Claves de ordenación con relleno de ceros en DynamoDB

Una cadena DynamoDB ordena lexicográficamente: un carácter a la vez. tiempo, de izquierda a derecha, no numéricamente. Entonces "10" aterriza antes de "2", porque "1" viene antes de "2". Rellenar ceros a un ancho fijo es como se hace una cuerda el orden coincide con el orden numérico.

¿Por qué se ordena "10" antes que "2" en una clave de clasificación DynamoDB?

Porque una cadena DynamoDB se compara lexicográficamente mediante un orden de UTF-8 bytes, no numéricamente. El byte para "1" precede a "2", por lo que "10" aterriza antes de "2". Rellene cada número con un ancho fijo con ceros a la izquierda ("2" se convierte en "0000000002") y el orden de las cadenas coincidirá exactamente con el orden numérico.

  • Orden lexicográfico: los números almacenados como cadenas se ordenan como words. "100", "11", "2" es la orden que te da DynamoDB, no lo que querías decir.
  • La solución: rellena cada número con un ancho fijo con ceros a la izquierda, por lo que "2" se convierte en "0000000002". Ahora el orden lexicográfico y numérico coinciden.
  • Elija un ancho una vez: dimensione para obtener el valor más grande que jamás almacenará, luego agregue unos cuantos dígitos. Cambiar el ancho más tarde significa reescribir cada clave.
  • Descfinalizando gratis: para ordenar de mayor a menor (el caso de la tabla de clasificación), almacenar maxValue - value, también relleno con ceros: DynamoDB no tiene clasificación por atributo dirección.

¿Por qué las claves de clasificación de cadenas te traicionan?

Viniendo de SQL, un ORDER BY score DESC sobre una columna de números enteros "simplemente funciona" - el motor sabe que la columna es numérica. DynamoDB no tiene ese lujo para una especie clave que no es del tipo Number.

DynamoDB compara claves de clasificación de cadenas (S) por UTF-8 orden de bytes, según el AWS documentación de clave de clasificación. Bytes, no magnitud. "9" (0x39) supera a "10" porque su primer byte supera "1" (0x31). La longitud es irrelevante: sólo decide el primer byte diferente.

Ésa es la cuestión: en el momento en que un número vive dentro de una clave de clasificación de cadena, cada Query que recorre el rango devuelve filas en un orden que parece revuelto.

Crear una clave de clasificación para la tabla de clasificación

Tome una tabla de clasificación de arcade de temporada. Una por temporada tiene cada carrera del jugador y primero quieres obtener las puntuaciones más altas.

Modelelo con una en una sola colección de elementos:

  • leaderboardId (clave de partición) — p.e. SEASON#2026-SPRING.
  • rankKey (clave de clasificación): la puntuación rellena con ceros más un desempate.

Un primer intento ingenuo almacena la puntuación en bruto como una cadena:

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

Un Query sobre SEASON#2026-SPRING los devuelve en este orden de bytes: "10", "1500", "240", "9". La racha de 9 puntos queda en último lugar y la de 1500 puntos queda enterrada en el medio. Inútil para una tabla de clasificación.

Pad a un ancho fijo

Elija un ancho lo suficientemente ancho para la partitura más grande que jamás haya grabado, luego presione el botón izquierdo con ceros. Digamos que las puntuaciones tienen un límite de diez millones; eso son ocho dígitos, así que use diez dígitos para el espacio libre:

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

Ahora cada clave tiene la misma longitud, por lo que la comparación byte por byte y la La comparación produce el orden idéntico. Ascendente Query da 9, 10, 240, 1500. Las matemáticas finalmente coinciden con los bytes.

El ancho es una puerta unidireccional. Si rellena hasta diez dígitos y luego una puntuación excede eso, un valor de 11 dígitos se ordena antes que uno de 10 dígitos, volviéndose a dividir todo - y solucionarlo significa reescribir todos los rankKey existentes. Sobreaprovisionar el ancho; el costo es un puñado de bytes.

Ordenar descfinal: almacenar la diferencia

Una tabla de clasificación quiere primero la puntuación más alta. DynamoDB puede leer una clave de clasificación hacia adelante o hacia atrás con ScanIndexForward: false, por lo que desc suele ser una bandera de tiempo de lectura: búsquela primero.

Pero cuando una colección de elementos debe servir direcciones de clasificación mixtas, o desea que La puntuación máxima es físicamente la primera, independientemente de las banderas de lectura, invierte el número. Almacene maxValue - score, relleno con ceros al mismo ancho:

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

El orden de bytes ascendente sobre el valor invertido ahora produce las puntuaciones originales de mayor a menor: 1500, 240, 10, 9. El truco está en el Documentos de Amazon Dynamo de 2007 espíritu: las claves son bytes opacos, por lo que codifica la intención en los bytes.

Agregar un desempate

Dos jugadores pueden empatar. Una partitura acolchada desnuda choca en la clave de clasificación, y una segunda escribir sobrescribiría el primero (mismo PK + SK). Agregue un sufijo único para que cada ejecución sea un elemento distinto y los empates se resuelvan de forma determinista:

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

Por ejemplo "0000001500#0000001719100800#p_8842". A igualdad de puntuación, la marca de tiempo más temprana gana la posición más alta; rellene también la marca de tiempo, o reintroducirá el mismo error que acaba de corregir.

En DynoTable puede explorar la tabla de clasificación de la temporada ordenada por el rankKey relleno con ceros y ver cómo los valores rellenados alinean las filas correctamente: la prueba de que los anchos son correctos antes de enviarlos.

Al ensamblar esa clave compuesta a mano es fácil equivocarse en un ancho. Generar el KeyConditionExpression de un Query de "top de temporada" en el generador de expresiones mantiene honesta la sintaxis begins_with / between mientras experimenta con los anchos.

Navegando por la tabla de clasificación de la temporada en DynoTable, ordenados por clave de rango rellenada con ceros.
Navegando por la tabla de clasificación de la temporada en DynoTable, ordenados por clave de rango rellenada con ceros.

Errores a evitar

  • Relleno demasiado estrecho. Todo el esquema colapsa la primera vez que se introduce un valor. desborda el ancho. Tamaño para el peor de los casos, luego agregue dígitos.
  • Olvidar la bandera de lectura. Si solo lees descfinalizando, ScanIndexForward: false puede ser todo lo que necesitas; no busques claves invertidas cuando una bandera lo hace.
  • Anchos mixtos en una colección. Cada clave que comparte un rango de clasificación debe usar el mismo ancho. Una migración que rellena filas nuevas pero no las antiguas las intercala incorrectamente.
  • Rellenar el segmento incorrecto. En una clave compuesta, rellene cada segmento numérico que participa en el pedido: puntuación y marca de tiempo, no solo la puntuación.

Próximos pasos

El relleno cero es una herramienta más amplia diseño de clave de clasificación kit de herramientas; emparejarlo con colecciones de elementos cuando sobrecargas una clave para servir varios patrones y apóyese en un Query preciso en lugar de un Scan una vez que el pedido sea correcto.

Pruebe DynoTable para explorar una tabla real y observe su clasificación con relleno de ceros. Las claves están en orden numérico antes de enviar el schema.

Actualizado