Interroger DynamoDB par date


102

Je viens d'une base de données relationnelle et j'essaie de travailler avec DynamoDB d'Amazon

J'ai une table avec une clé de hachage "DataID" et une plage "CreatedAt" et un tas d'éléments dedans.

J'essaye d'obtenir tous les éléments qui ont été créés après une date spécifique et triés par date. Ce qui est assez simple dans une base de données relationnelle.

Dans DynamoDB, la chose la plus proche que je puisse trouver est une requête et l'utilisation de la clé de plage supérieure à filtre. Le seul problème est que pour effectuer une requête, j'ai besoin d'une clé de hachage qui va à l'encontre de l'objectif.

Alors qu'est-ce que je fais de mal? Mon schéma de table est-il incorrect, la clé de hachage ne devrait-elle pas être unique? ou y a-t-il une autre façon d'interroger?

Réponses:


34

Réponse mise à jour:

DynamoDB permet la spécification d'index secondaires pour faciliter ce type de requête. Les index secondaires peuvent être soit globaux, ce qui signifie que l'index couvre toute la table à travers les clés de hachage, soit local, ce qui signifie que l'index existerait dans chaque partition de clé de hachage, ce qui nécessite que la clé de hachage soit également spécifiée lors de la requête.

Pour le cas d'utilisation de cette question, vous souhaiterez utiliser un index secondaire global sur le champ "CreatedAt".

Pour plus d'informations sur les index secondaires DynamoDB, consultez la documentation relative aux index secondaires

Réponse originale:

DynamoDB n'autorise pas les recherches indexées sur la clé de plage uniquement. La clé de hachage est requise pour que le service sache dans quelle partition rechercher les données.

Vous pouvez bien sûr effectuer une opération d'analyse pour filtrer par la valeur de la date, mais cela nécessiterait une analyse complète de la table, ce n'est donc pas idéal.

Si vous devez effectuer une recherche indexée des enregistrements par heure sur plusieurs clés primaires, DynamoDB peut ne pas être le service idéal pour vous, ou vous devrez peut-être utiliser une table distincte (dans DynamoDB ou dans un magasin relationnel) pour stocker l'élément métadonnées sur lesquelles vous pouvez effectuer une recherche indexée.


14
Voir les commentaires sur la réponse ci-dessous; il n'y a pas moyen de gérer cela maintenant, du moins pas pour ce que le PO a demandé. Les GSI nécessitent toujours que vous spécifiiez une clé de hachage, vous ne pouvez donc pas interroger tous les enregistrements avec CreatedAtplus d'un certain point.
pkaeding

4
@pkaeding a raison. Vous pouvez obtenir des enregistrements plus anciens qu'une date spécifique à l' aide de l' analyse , mais vous ne pouvez pas les obtenir dans un ordre trié. GSI ne vous aidera pas dans ce cas. Il n'est pas possible de trier la clé de partition , ni d'interroger uniquement la clé de plage .
gkiko

15
Pour ceux d'entre vous confus. CETTE RÉPONSE EST FAUX. Sa réponse originale est juste, mais sa réponse mise à jour ne l'est pas. Lisez la réponse de Warren Parad ci-dessous. C'est correct.
Ryan Shillington

1
@MikeBrant Je veux interroger (pas scanner, qui regarde chaque élément de la table, ce qui le rend très inefficace et coûteux) une table sur la clé de hachage GSI d'une table (CreatedAt) en utilisant le symbole supérieur à. Autant que je sache, cela ne peut pas être fait.
Aziz Javed

4
Le problème que vous rencontrez probablement en utilisant une date comme partition principale est que vous pouvez créer un hotspot sur certains ou l'un des pairs, en raison du fait que dans la plupart des stockages de données, les nouvelles données sont interrogées plus souvent que les anciennes données.
Connaissance

53

Compte tenu de la structure de votre table actuelle, cela n'est actuellement pas possible dans DynamoDB. Le défi majeur est de comprendre que la clé de hachage de la table (partition) doit être traitée comme la création de tables séparées. À certains égards, c'est vraiment puissant (pensez aux clés de partition comme à la création d'une nouvelle table pour chaque utilisateur ou client, etc.).

Les requêtes ne peuvent être effectuées que dans une seule partition. C'est vraiment la fin de l'histoire. Cela signifie que si vous souhaitez interroger par date (vous voudrez utiliser msec depuis l'époque), tous les éléments que vous souhaitez récupérer dans une seule requête doivent avoir le même Hash (clé de partition).

Je devrais nuancer cela. Vous pouvez absolumentscan en fonction du critère que vous recherchez, ce n'est pas un problème, mais cela signifie que vous examinerez chaque ligne de votre tableau, puis vérifierez si cette ligne a une date qui correspond à vos paramètres. C'est vraiment cher, surtout si vous êtes dans le domaine du stockage d'événements par date en premier lieu (c'est-à-dire que vous avez beaucoup de lignes).

Vous pouvez être tenté de mettre toutes les données dans une seule partition pour résoudre le problème, et vous le pouvez absolument, mais votre débit sera douloureusement bas, étant donné que chaque partition ne reçoit qu'une fraction du montant total défini.

La meilleure chose à faire est de déterminer les partitions les plus utiles à créer pour enregistrer les données:

  • Avez-vous vraiment besoin de regarder toutes les lignes ou s'agit-il uniquement des lignes d'un utilisateur spécifique?

  • Serait-il correct de commencer par affiner la liste par mois et de faire plusieurs requêtes (une pour chaque mois)? Ou par année?

  • Si vous effectuez une analyse de séries chronologiques, il y a quelques options, changez la clé de partition en quelque chose de calculé PUTpour rendre le queryplus facile, ou utilisez un autre produit aws comme kinesis qui se prête à la journalisation par ajout uniquement.


4
Je veux mettre l'accent sur l'option que vous avez proposée dans votre dernier paragraphe concernant l'examen «par année». Créez un attribut comme yyyyet hash à ce sujet, mais créez également une createddate que vous pouvez utiliser comme clé de plage. Ensuite, vous obtenez 10 Go de données par an (27 Mo par jour), ce qui convient probablement à plus de circonstances. Cela signifie que vous devez créer une requête par an lorsque les requêtes de date dépassent la limite de l'année, mais au moins cela fonctionnera et c'est plus sûr que de créer une clé de hachage factice.
Ryan Shillington


1
comme l'explique le lien ci-dessus, les clés de partition strictement basées sur le temps peuvent conduire à des points chauds. si vous devez utiliser des clés de partition basées sur le temps, il est préférable d'ajouter un autre élément à la clé de partition pour étaler une période de temps sur plusieurs partitions. J'ai vu des suggestions d'utiliser simplement un préfixe entre 0-n où n est le nombre de partitions à chaque fois que le seau doit être réparti.
dres

@RyanShillington Il n'y a pas de limite de 10 Go sur les index secondaires globaux . Cette limite s'applique uniquement aux index secondaires locaux .
Simon Forsberg le

18

L'approche que j'ai suivie pour résoudre ce problème consiste à créer un index secondaire global comme ci-dessous. Je ne sais pas si c'est la meilleure approche mais, espérons-le, si elle est utile à quelqu'un.

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

Limitation imposée à l'utilisateur de l'API HTTP pour spécifier le nombre de jours pour récupérer les données, par défaut sur 24 heures.

De cette façon, je peux toujours spécifier le HashKey comme jour de la date actuelle et RangeKey peut utiliser les opérateurs> et <lors de la récupération. De cette façon, les données sont également réparties sur plusieurs fragments.


8

Votre clé de hachage (principale de sorte) doit être unique (sauf si vous avez une plage comme indiqué par d'autres).

Dans votre cas, pour interroger votre table, vous devez avoir un index secondaire.

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

Votre clé de hachage est ID Votre index secondaire est défini comme: DataID-Created-index (c'est le nom que DynamoDB utilisera)

Ensuite, vous pouvez faire une requête comme celle-ci:

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
    }
});

Essentiellement, votre requête ressemble à:

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

L'index secondaire augmentera les unités de capacité de lecture / écriture requises, vous devez donc en tenir compte. C'est toujours bien mieux que de faire un scan, qui sera coûteux en lecture et en temps (et est limité à 100 éléments je crois).

Ce n'est peut-être pas la meilleure façon de le faire, mais pour quelqu'un qui a l'habitude de RD (je suis également habitué à SQL), c'est le moyen le plus rapide d'être productif. Comme il n'y a aucune contrainte en ce qui concerne le schéma, vous pouvez créer quelque chose qui fonctionne et une fois que vous avez la bande passante pour travailler de la manière la plus efficace, vous pouvez changer les choses.


1
Vous dites qu'il n'y a pas de contraintes, mais sachez que cette approche signifie que vous pouvez enregistrer au plus 10 Go de données (le maximum d'une seule partition).
Ryan Shillington

Cela aurait été l'approche si DataID était connu. Mais ici, nous devons obtenir chaque ligne pour laquelle la création est plus qu'une date.
Yasith Prabuddhaka

3

Vous pouvez faire de la clé Hash quelque chose comme un identifiant de `` catégorie de produit '', puis la clé de plage comme une combinaison d'un horodatage avec un identifiant unique ajouté à la fin. De cette façon, vous connaissez la clé de hachage et pouvez toujours interroger la date avec une valeur supérieure à.


1

Vous pouvez avoir plusieurs clés de hachage identiques; mais seulement si vous avez une clé de plage qui varie. Pensez-y comme des formats de fichiers; vous pouvez avoir 2 fichiers avec le même nom dans le même dossier tant que leur format est différent. Si leur format est le même, leur nom doit être différent. Le même concept s'applique aux clés de hachage / plage de DynamoDB; pensez simplement au hachage comme nom et à la plage comme format.

De plus, je ne me souviens pas s'ils en avaient au moment de l'OP (je ne crois pas qu'ils l'avaient fait), mais ils offrent maintenant des index secondaires locaux.

Je crois comprendre que cela devrait maintenant vous permettre d'effectuer les requêtes souhaitées sans avoir à effectuer une analyse complète. L'inconvénient est que ces index doivent être spécifiés lors de la création de la table et que (je crois) ne peuvent pas être vides lors de la création d'un élément. De plus, ils nécessitent un débit supplémentaire (bien que généralement pas autant qu'une analyse) et du stockage, ce n'est donc pas une solution parfaite, mais une alternative viable, pour certains.

Je recommande quand même la réponse de Mike Brant comme méthode préférée d'utilisation de DynamoDB; et utiliser cette méthode moi-même. Dans mon cas, j'ai juste une table centrale avec seulement une clé de hachage comme identifiant, puis des tables secondaires qui ont un hachage et une plage qui peuvent être interrogées, puis l'élément pointe le code vers "l'élément d'intérêt" de la table centrale, directement .

Des données supplémentaires concernant les index secondaires peuvent être trouvées dans la documentation DynamoDB d'Amazon ici pour les personnes intéressées.

Quoi qu'il en soit, j'espère que cela aidera quiconque se produira sur ce fil.


J'ai essayé de créer une table DynamoDB où il y avait AWSDynamoDBKeySchemaElement 'createdAt' de type hachage et à nouveau l'AWSDynamoDBKeySchemaElement 'createdAt' de type range et j'ai eu une erreur qui disait Error Domain = com.amazonaws.AWSDynamoDBErrorDomain Code = 0 "(null) UserInfo" = {__ type = com.amazon.coral.validate # ValidationException, message = L'élément Hash Key et Range Key dans le KeySchema ont le même nom}. Je ne pense donc pas que ce que vous dites est correct.
user1709076

Je crois que vous avez mal compris (même si je suppose que je n'ai pas non plus été très clair dans ma description). Vous ne pouvez pas avoir 2 attributs (colonnes) différents avec le même nom, dans une table, mais lorsque vous créez une clé de hachage avec une clé de plage, vous pouvez avoir plusieurs éléments qui utilisent tous le même hachage tant que leur plage est différente, et vice versa. Par exemple: votre hachage est «ID» et votre plage est «Date». Vous pouvez avoir 2 instances de l'ID «1234» tant que leur date est différente.
DGolberg

Ah DGoldberg! Je t'ai maintenant. C'est génial. Donc, pour mon cas, puisque je veux seulement et toujours vouloir simplement interroger les messages texte 'after date = x', il semble que je pourrais définir tous les messages texte pour avoir le même 'fake_hash = 1'. Ensuite, faites ma requête.keyConditionExpression = @ "fake_hash = 1 et #Date>: val". Merci beaucoup. Si vous avez une autre entrée, je serais heureux de l'entendre car il semble étrange d'avoir un hachage qui a toujours la même valeur?
user1709076

Je devrais vérifier à nouveau, mais je suis à peu près sûr que vous pouvez faire une requête sur les tables de hachage uniquement ... bien que si vous utilisez un horodatage comme hachage, je vous recommande d'enregistrer jusqu'au l'unité la plus courte possible, comme les millisecondes ou les nano / microsecondes (quelle que soit la plus petite unité de temps que le code peut enregistrer), afin de réduire le risque de chevauchement de la date / heure. De plus, vous pouvez ajouter un verrouillage optimiste pour réduire davantage la possibilité de chevauchements: docs.aws.amazon.com/amazondynamodb/latest/developerguide/ ... Réessayez simplement une autre fois en cas de conflit.
DGolberg

-11

Réponse mise à jour Il n'existe aucun moyen pratique de le faire à l'aide de requêtes Dynamo DB avec un débit prévisible. Une option (sous-optimale) consiste à utiliser un GSI avec un HashKey artificiel et CreatedAt. Puis interrogez par HashKey seul et mentionnez ScanIndexForward pour classer les résultats. Si vous pouvez trouver une HashKey naturelle (dites la catégorie de l'article, etc.), cette méthode est gagnante. D'autre part, si vous conservez la même HashKey pour tous les éléments, cela affectera le débit principalement lorsque votre ensemble de données dépasse 10 Go (une partition)

Réponse originale: Vous pouvez le faire maintenant dans DynamoDB en utilisant GSI. Faites le champ «CreatedAt» comme un GSI et émettez des requêtes comme (GT some_date). Stockez la date sous forme de nombre (msec depuis l'époque) pour ce type de requêtes.

Les détails sont disponibles ici: Global Secondary Indexes - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Using

C'est une fonctionnalité très puissante. Sachez que la requête est limitée à (EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN) Condition - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html


31
J'ai voté contre parce que pour autant que je sache, votre réponse est incorrecte. Tout comme la clé primaire d'une table, vous ne pouvez interroger la clé de hachage d'un GSI qu'avec l'opérateur EQ. Si vous vouliez dire que cela CreatedAtdevrait être la clé de plage du GSI, vous devrez choisir une clé de hachage - et vous revenez là où vous avez commencé, car vous ne pourrez interroger GT CreatedAtque sur une valeur spécifique du touche dièse.
PaF

D'accord avec PaF. L'utilisation d'un GSI avec la clé de hachage comme heure de création n'aide pas avec la question posée dans l'OP.
4-8-15-16-23-42
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.