Consultar DynamoDB por fecha

102

Vengo de una base de datos relacional y trato de trabajar con DynamoDB de Amazon.

Tengo una tabla con una clave hash "DataID" y un rango "CreatedAt" y un montón de elementos en él.

Estoy tratando de obtener todos los elementos que se crearon después de una fecha específica y se ordenaron por fecha. Lo cual es bastante sencillo en una base de datos relacional.

En DynamoDB, lo más cercano que pude encontrar es una consulta y el uso de la clave de rango mayor que el filtro. El único problema es que para realizar una consulta necesito una clave hash que anula el propósito.

Entonces, ¿qué estoy haciendo mal? ¿Está mal el esquema de mi tabla? ¿No debería ser única la clave hash? o hay otra forma de consultar?

pañuelo de manzana
fuente

Respuestas:

34

Respuesta actualizada:

DynamoDB permite la especificación de índices secundarios para ayudar en este tipo de consulta. Los índices secundarios pueden ser globales, lo que significa que el índice abarca toda la tabla a través de claves hash, o locales, lo que significa que el índice existiría dentro de cada partición de clave hash, lo que requiere que la clave hash también se especifique al realizar la consulta.

Para el caso de uso de esta pregunta, le recomendamos que utilice un índice secundario global en el campo "CreatedAt".

Para obtener más información sobre los índices secundarios de DynamoDB, consulte la documentación del índice secundario

Respuesta original:

DynamoDB no permite búsquedas indexadas solo en la clave de rango. La clave hash es necesaria para que el servicio sepa en qué partición buscar para encontrar los datos.

Por supuesto, puede realizar una operación de escaneo para filtrar por el valor de la fecha, sin embargo, esto requeriría un escaneo completo de la tabla, por lo que no es ideal.

Si necesita realizar una búsqueda indexada de registros por tiempo en varias claves primarias, es posible que DynamoDB no sea el servicio ideal para su uso, o puede que necesite utilizar una tabla separada (ya sea en DynamoDB o en una tienda relacional) para almacenar el elemento. metadatos contra los que puede realizar una búsqueda indexada.

Mike Brant
fuente
14
Vea los comentarios sobre la respuesta a continuación; hay no las formas de manejar esto ahora, al menos no por lo que pidió a la OP. Los GSI aún requieren que especifique una clave hash, por lo que no puede consultar todos los registros con CreatedAtmás de un cierto punto.
pkaeding
4
@pkaeding tiene razón. Puede obtener registros anteriores a una fecha específica mediante el escaneo , pero no puede obtenerlos en orden ordenado. GSI no te ayudará en este caso. No es posible ordenar la clave de partición , ni es posible consultar solo la clave de rango .
gkiko
15
Para aquellos de ustedes confundidos. ESTA RESPUESTA ES INCORRECTA. Su respuesta original es correcta pero su respuesta actualizada no lo es. Lea la respuesta de Warren Parad a continuación. Es correcto.
Ryan Shillington
1
@MikeBrant Quiero consultar (no escanear, que mira cada elemento de la tabla, lo que lo hace muy ineficiente y costoso) una tabla en la clave hash GSI de una tabla (CreatedAt) usando el símbolo mayor que. Hasta donde yo sé, esto no se puede hacer.
Aziz Javed
4
El problema que probablemente tenga al usar una fecha como partición primaria es que podría crear un punto de acceso en algunos o en uno de los pares, debido al hecho de que en la mayoría de los almacenamientos de datos se consultan datos nuevos con más frecuencia que datos antiguos.
Conocimiento
53

Dada su estructura de tabla actual, esto no es posible actualmente en DynamoDB. El gran desafío es entender que la clave Hash de la tabla (partición) debe tratarse como si creara tablas separadas. De alguna manera, esto es realmente poderoso (piense en las claves de partición como la creación de una nueva tabla para cada usuario o cliente, etc.).

Las consultas solo se pueden realizar en una única partición. Ese es realmente el final de la historia. Esto significa que si desea consultar por fecha (querrá usar mseg desde época), entonces todos los elementos que desea recuperar en una sola consulta deben tener el mismo Hash (clave de partición).

Debo calificar esto. Puede hacerlo scansegún el criterio que está buscando, eso no es un problema, pero eso significa que estará mirando cada fila de su tabla y luego verificando si esa fila tiene una fecha que coincida con sus parámetros. Esto es realmente caro, especialmente si, en primer lugar, está en el negocio de almacenar eventos por fecha (es decir, tiene muchas filas).

Puede tener la tentación de poner todos los datos en una sola partición para resolver el problema, y ​​absolutamente puede, sin embargo, su rendimiento será dolorosamente bajo, dado que cada partición solo recibe una fracción de la cantidad total establecida.

Lo mejor que puede hacer es determinar particiones más útiles para crear para guardar los datos:

  • ¿Realmente necesita mirar todas las filas, o son solo las filas de un usuario específico?

  • ¿Estaría bien reducir primero la lista por mes y hacer varias consultas (una para cada mes)? ¿O por año?

  • Si está haciendo un análisis de series de tiempo, hay un par de opciones, cambie la clave de partición a algo calculado PUTpara hacerlo querymás fácil, o use otro producto de AWS como kinesis, que se presta al registro de solo anexos.

Warren Parad
fuente
4
Quiero enfatizar la opción que presentó en su último párrafo sobre considerar "por año". Cree un atributo como yyyyy un hash sobre eso, pero también cree una createdfecha que pueda usar como clave de rango. Luego, obtiene 10 GB de datos por año (27 MB por día), lo que probablemente esté bien para más circunstancias. Sin embargo, significa que debe crear una consulta por año cuando las consultas de fecha superan el límite del año, pero al menos funcionará y es más seguro que crear una clave hash ficticia.
Ryan Shillington
1
Otra opción: stackoverflow.com/questions/35963243/…
Ryan Shillington
1
como explica el enlace anterior, las claves de partición estrictamente basadas en el tiempo pueden conducir a puntos calientes. si debe utilizar claves de partición basadas en el tiempo, es mejor agregar algún otro elemento a la clave de partición para extender un período de tiempo en varias particiones. He visto sugerencias de simplemente usar un prefijo entre 0-n donde n es el número de particiones cada vez que se debe distribuir el depósito.
dres
@RyanShillington No hay límite de 10 GB en índices secundarios globales . Ese límite solo se aplica a los índices secundarios locales .
Simon Forsberg
18

El enfoque que seguí para resolver este problema es crear un índice secundario global como se muestra a continuación. No estoy seguro de si este es el mejor enfoque, pero espero que sea útil para alguien.

Hash Key                 | Range Key
------------------------------------
Date value of CreatedAt  | CreatedAt

Limitación impuesta al usuario de la API HTTP para especificar el número de días para recuperar datos, predeterminado a 24 horas.

De esta manera, siempre puedo especificar HashKey como el día de la fecha actual y RangeKey puede usar los operadores> y <mientras recupera. De esta forma, los datos también se distribuyen en varios fragmentos.

Gireesh
fuente
8

Su clave Hash (principal de tipo) tiene que ser única (a menos que tenga un rango como el indicado por otros).

En su caso, para consultar su tabla debe tener un índice secundario.

|  ID  | DataID | Created | Data |
|------+--------+---------+------|
| hash | xxxxx  | 1234567 | blah |

Su clave hash es ID Su índice secundario se define como: DataID-Created-index (ese es el nombre que usará DynamoDB)

Luego, puede realizar una consulta como esta:

var params = {
    TableName: "Table",
    IndexName: "DataID-Created-index",
    KeyConditionExpression: "DataID = :v_ID AND Created > :v_created",
    ExpressionAttributeValues: {":v_ID": {S: "some_id"},
                                ":v_created": {N: "timestamp"}
    },
    ProjectionExpression: "ID, DataID, Created, Data"
};

ddb.query(params, function(err, data) {
    if (err) 
        console.log(err);
    else {
        data.Items.sort(function(a, b) {
            return parseFloat(a.Created.N) - parseFloat(b.Created.N);
        });
        // More code here
    }
});

Básicamente, su consulta se ve así:

SELECT * FROM TABLE WHERE DataID = "some_id" AND Created > timestamp;

El índice secundario aumentará las unidades de capacidad de lectura / escritura necesarias, por lo que debe tenerlo en cuenta. Todavía es mucho mejor que hacer un escaneo, que será costoso en lecturas y en tiempo (y creo que está limitado a 100 elementos).

Puede que esta no sea la mejor manera de hacerlo, pero para alguien acostumbrado a RD (también estoy acostumbrado a SQL) es la forma más rápida de ser productivo. Dado que no hay restricciones con respecto al esquema, puede preparar algo que funcione y una vez que tenga el ancho de banda para trabajar de la manera más eficiente, puede cambiar las cosas.

ET
fuente
1
Dice que no hay restricciones, pero debe saber que este enfoque significa que puede guardar como máximo 10 GB de datos (el máximo de una sola partición).
Ryan Shillington
Este habría sido el enfoque si se conociera DataID. Pero aquí necesitamos obtener todas las filas para las que el creado es más de una fecha.
Yasith Prabuddhaka
3

Puede hacer que la clave Hash sea algo similar a una identificación de 'categoría de producto', luego la clave de rango como una combinación de una marca de tiempo con una identificación única agregada al final. De esa manera, conoce la clave hash y aún puede consultar la fecha con mayor que.

Greg
fuente
1

Puede tener varias claves hash idénticas; pero solo si tiene una clave de rango que varía. Piense en ello como formatos de archivo; puede tener 2 archivos con el mismo nombre en la misma carpeta siempre que su formato sea diferente. Si su formato es el mismo, su nombre debe ser diferente. El mismo concepto se aplica a las claves hash / rango de DynamoDB; piense en el hash como el nombre y el rango como el formato.

Además, no recuerdo si los tenían en el momento del OP (no creo que los tuvieran), pero ahora ofrecen índices secundarios locales.

Según tengo entendido, ahora debería permitirle realizar las consultas deseadas sin tener que realizar un análisis completo. La desventaja es que estos índices deben especificarse en la creación de la tabla y también (creo) no pueden estar en blanco al crear un elemento. Además, requieren un rendimiento adicional (aunque normalmente no tanto como un escaneo) y almacenamiento, por lo que no es una solución perfecta, sino una alternativa viable para algunos.

Sin embargo, todavía recomiendo la respuesta de Mike Brant como el método preferido para usar DynamoDB; y usar ese método yo mismo. En mi caso, solo tengo una tabla central con solo una clave hash como mi ID, luego tablas secundarias que tienen un hash y un rango que se pueden consultar, luego el elemento apunta el código al "elemento de interés" de la tabla central, directamente .

Se pueden encontrar datos adicionales sobre los índices secundarios en la documentación de DynamoDB de Amazon aquí para aquellos interesados.

De todos modos, espero que esto ayude a cualquier otra persona que se encuentre en este hilo.

DGolberg
fuente
Intenté crear una tabla de DynamoDB donde había AWSDynamoDBKeySchemaElement 'createdAt' de tipo hash y nuevamente AWSDynamoDBKeySchemaElement 'createdAt' de rango de tipo y recibí un error que decía Error Domain = com.amazonaws.AWSDynamoDBErrorDomain Code = 0 "(null)" UserInfo " = {__ type = com.amazon.coral.validate # ValidationException, message = Tanto el elemento Hash Key como el elemento Range Key en KeySchema tienen el mismo nombre}. Así que no creo que lo que estás diciendo sea correcto.
user1709076
Creo que lo entendiste mal (aunque supongo que tampoco fui muy claro en mi descripción). No puede tener 2 atributos (columnas) diferentes con el mismo nombre, en una tabla, pero cuando crea una clave hash con una clave de rango, puede tener varios elementos que usan el mismo hash siempre que su rango sea diferente, y viceversa. Por ejemplo: su hash es "ID" y su rango es "Fecha", podría tener 2 instancias de la ID "1234" siempre que su fecha sea diferente.
DGolberg
¡Ah DGoldberg! Te entiendo ahora. Eso es genial. Entonces, para mi caso, dado que solo y siempre solo querré consultar los mensajes de texto 'después de la fecha = x', parece que podría configurar todos los mensajes de texto para que tengan el mismo 'fake_hash = 1'. Luego haga mi query.keyConditionExpression = @ "fake_hash = 1 y #Date>: val". Muchas gracias. Si tiene alguna otra entrada, me encantaría escucharla, ya que parece extraño tener un hash que siempre tenga el mismo valor.
user1709076
Tendría que comprobarlo de nuevo, pero estoy bastante seguro de que puedes hacer una consulta en tablas de solo hash ... aunque si estás usando una marca de fecha / hora como tu hash, te recomiendo que registres hasta el la unidad más corta posible, como milisegundos o nano / microsegundos (cualquiera que sea la unidad de tiempo más pequeña que el código puede registrar), para reducir la posibilidad de superposición de fecha / hora. Además, puede agregar un bloqueo optimista para reducir aún más la posibilidad de superposiciones: docs.aws.amazon.com/amazondynamodb/latest/developerguide/… Simplemente vuelva a intentarlo en otro momento si hay un conflicto.
DGolberg
-11

Respuesta actualizada No hay una forma conveniente de hacer esto usando Dynamo DB Queries con un rendimiento predecible. Una opción (subóptima) es utilizar un GSI con un HashKey & CreatedAt artificiales. Luego, consulte solo con HashKey y mencione ScanIndexForward para ordenar los resultados. Si puede encontrar una HashKey natural (digamos la categoría del artículo, etc.), este método es un ganador. Por otro lado, si mantiene el mismo HashKey para todos los elementos, afectará el rendimiento principalmente cuando su conjunto de datos supere los 10 GB (una partición)

Respuesta original: puede hacer esto ahora en DynamoDB mediante GSI. Cree el campo "CreatedAt" como GSI y emita consultas como (GT some_date). Almacene la fecha como un número (ms desde época) para este tipo de consultas.

Los detalles están disponibles aquí: Índices secundarios globales - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Using

Esta es una característica muy poderosa. Tenga en cuenta que la consulta se limita a (EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN) Condición - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html

Sony Kadavan
fuente
31
He votado en contra porque, por lo que puedo decir, tu respuesta es incorrecta. Al igual que la clave principal de una tabla, puede consultar la clave hash de una GSI solo con el operador EQ. Si estaba insinuando que CreatedAtdebería ser la clave de rango de GSI, entonces deberá elegir una clave hash, y luego volverá a donde comenzó, porque podrá consultar GT CreatedAtsolo para un valor específico de la clave hash.
PaF
De acuerdo con PaF. El uso de un GSI con la clave hash como tiempo de creación no ayuda con la pregunta formulada en el OP.
4-8-15-16-23-42