DynamoDB : le décryptage de la nouvelle solution NoSQL de AWS

Logo DynamoDB
DynamoDB, beaucoup de bruit autour de ce nouvel outil AWS… Mais au delà de ce qui est annoncé en termes de performances, de scalabilité sans limite, quels sont les éléments auxquels il faut prendre garde, les non-dits, ceux qu’il faut prendre en compte pour savoir si cet outil correspond à votre besoin ? Je me base sur ce que j’ai lu : articles techniques sur le sujet, documentation du site AWS (FAQ, documentation référence de l’API, …) qui est complète mais qu’il faut prendre le temps de lire, … Je me base aussi sur des essais que j’ai effectués et qui permettent de relever d’autres éléments importants. Je présenterai donc rapidement le concept de l’outil avant de rentrer plus précisément dans les points à prendre en considération, en termes d’utilisation et de facturation. Je présenterai également les différences avec son prédécesseur : SimpleDB. Finalement, j’ai réalisé un mini bench de l’outil dont je vous ferai partager le code et les résultats.

Modèle de données

C’est une base de données non relationnelle structurée ou NoSQL ou clé/valeur. Elle est basée sur le même modèle que SimpleDB (AWS) à savoir une table dans laquelle on insère des items. Chaque item est constitué d’une clé primaire obligatoire et d’un ensemble d’attributs facultatifs. Sous une représentation graphique, les items sont des lignes et les attributs des colonnes. Cependant, aucun attribut (sauf la clé primaire) n’est obligatoire. On peut donc avoir une table « CatalogueProduits » pour un site de e-commerce qui contient des livres, des aquariums, des voitures, … Chaque produit aura un « IdProduit » (clé primaire), pour le reste certains attributs seront communs comme « Prix », « CategorieProduit », « Disponibilite », … D’autres seront spécifiques à chaque type d’items (types définis par la « CategorieProduit » – livre, … – dans notre cas).

A noter que chaque attribut peut avoir comme valeur :

  • String
  • Number
  • Tableau de String ou de Number (appelé aussi attribut multi-valué) => par exemple à l’attribut Couleur peut correspondre la valeur ["bleu", "rouge", "vert"]

A noter que la clé primaire peut être de 2 types :

  • Hash : la clé primaire est constituée d’un attribut et un index non ordonné sur l’attribut est construit.
  • Hash & Range : la clé primaire est constituée de 2 attributs (le hash et le range) et 2 index sont construits : le hash index non ordonné sur l’attribut défini comme hash et le sorted range index sur l’attribut défini comme range.

Ci-dessous un exemple illustratif tiré de la documentation de référence de l’API :

DynamoDB - Keys

Et pour vous donner une idée des requêtes possibles sur le sorted range index, voici les options disponibles (tirées du SDK Ruby proposé par AWS) :

RANGE_KEY_OPTIONS =
{
:range_less_than => "LT",
:range_greater_than => "GT",
:range_lte => "LE",
:range_gte => "GE",
:range_begins_with => "BEGINS_WITH"
}

Accès aux données

L’accès s’effectue via des requêtes HTTP(S) qui peuvent être envoyées depuis le réseau AWS (à partir d’une instance EC2) ou bien de l’extérieur (attention au temps réseau HTTP(S)). Le service est donc accessible de partout sans limitation, pour peu que vous ayez un couple de clés access key / secret key associées aux bonnes credentials pour vous authentifier et que vous puissiez sortir sur le Net en 80 et/ou 443.

Le format des échanges au dessus du protocole HTTP(S) est en JSON, autant au niveau de la requête que de la réponse. Voici un exemple de réponse :

{"Items":
[{"date":{"S":1989},
"fans":{"SS":["Keneau","Alexis","John"]},
"name":{"S":"Bill & Ted's Excellent Adventure"},
"rating":{"S":"****"}}]
}

Cependant, les SDKs (fournis par Amazon ou par d’autres) intègrent déjà une interface plus fonctionnelle de communication avec les APIs AWS afin que vous n’ayez pas à vous occuper de la création des requêtes et du parsing des réponses. Les SDKs s’occupent donc de la couche transport basée sur le format JSON.

Différences avec SimpleDB

Tout est histoire de compromis : entre l’implémentation de SimpleDB et de DynamoDB, il a fallu choisir entre fonctionnalités et performances. SimpleDB privilégie plus les fonctionnalités, d’ailleurs certaines d’entre elles impliquent que toutes les données d’une table (appelée « domaine » dans SimpleDB) soient sur un même serveur… Donc pas de sharding possible et pas de forte scalabilité envisageable. De plus les APIs de SimpleDB partent du principe que tous les attributs des items soient automatiquement indexés (donc moins bonnes performances). DynamoDB fait le choix inverse en limitant les fonctionnalités de ses APIs et en ne conservant que un ou deux index (fonction du type de clé primaire) pour optimiser les performances.

Les différences principales sont les suivantes :

  • Plus de limitation à 10GB par table.
  • Pas d’indexation sur tous les attributs (colonnes) et moins de possibilités (fonctionnalités) en termes de requêtage via les APIs.
  • Une garantie des performances : on choisit le nombre de requêtes/secondes dont on a besoin en écriture et en lecture sur chaque table.
  • Le protocole de transport : JSON over HTTP(S) pour DynamoDB, REST style pour SimpleDB avec une réponse au format XML.
  • Le pricing : les 2 ont une composante lié à la quantité de données stockées, plus éventuellement la quantité de données qui transitent hors du réseau AWS si on fait des appels depuis l’extérieur. La différence se fait entre :
    • La Machine Utilization de SimpleDB qui se base sur la consommation en ressources machine de chaque requête (SELECT, GET, PUT, …).
    • La Throughput Capacity de DynamoDB qui se base sur une capacité de requêtes par seconde que vous allez définir. Contrairement à SimpleDB, on est dans ce cas sur de l’alloué plutôt que du consommé (la même différence que l’on a entre le volume alloué que l’on paie sur les EBS et le volume utilisé que l’on paie sur S3).

Les éléments à prendre en compte

Il faut faire attention à un certain nombre d’éléments plus ou moins mis en valeur afin de faire une bonne utilisation du produit :

  • Il s’agit de la première release et il faut suivre la roadmap du produit qui va permettre au service de s’étendre en termes de couverture géographique (le service est actuellement disponible sur une seule région : US East (N. Virginia)) et en termes de fonctionnalités (par exemple celle de backup/restauration, actuellement absente, est dans les priorités).
  • La disponibilité et la durabilité des données sont assurées sur le même scope que les services de type S3 ou SimpleDB (entre autres) : il y a une réplication multi zones d’accessibilité sur la région du service.
  • La modification de l’allocation des ressources n’est pas instantanée : si l’action de diminution de la capacité en lecture ou en écriture est une question de secondes ou de minutes, l’augmentation de cette capacité se compte en minutes ou en heures en fonction de la taille de la base (la croissance n’est pas forcément proportionnelle car met en place des mécanismes de parallélisation). Il faut donc s’y prendre à l’avance ! On ne peut donc pas, par exemple, lisser l’allocation de la capacité sur la journée pour une application qui a des pics d’utilisation sur une période donnée de la journée (une application de jeu qui voit son trafic exploser le midi et le soir par exemple). La fréquence des pics de charge serait trop importante. En revanche cela est tout à fait envisageable pour une utilisation avec des pics plus espacés (période de soldes sur l’année pour un site de e-commerce).
  • Autre point important à prendre en compte sur l’augmentation de la capacité en lecture ou en écriture : le facteur maximum d’augmentation est de 2. Donc au maximum on double sa capacité en une demande au service DynamoDB. Si on veut multiplier sa capacité en lecture ou/et en écriture par 8, il faudra donc effectuer 3 requêtes successives (2 x 2 x 2), avec entre chaque demande un temps de mise en application de la capacité allant de quelques minutes à quelques heures.

 

AWS - DynamoDB - Modify Provisioned Throughput

  • Il est possible également de diminuer cette capacité de la quantité souhaitée (pas de facteur) avec un minimum de 5 unités de capacité de débit pour la lecture et/ou l’écriture. Attention cependant, une seule diminution de la capacité est autorisée par jour : cela ne me semble pas poser de problème sur une vraie utilisation (c’est-à-dire autre que des tests :o)).
  • Durant cette modification de capacité, il est important de noter qu’il n’y a pas d’interruption du service sur cette table : il est toujours possible de la requêter en lecture et en écriture.
  • La taille maximale d’un item à stocker est de 64KB, ceci inclut le nom et la valeur des attributs. Il n’y a pas de limite sur le nombre d’attributs d’un item (tant que l’on reste dans la limite des 64KB en taille totale de l’item).
  • On peut créer jusqu’à 256 tables pour un compte donné, avec 2 limitations sur les unités de capacité (ces 2 limitations sur la capacité de débit peuvent être dépassées en remplissant ce formulaire) :
    • 10,000 unités en capacité de débit en lecture par table et pareil en écriture.
    • 20,000 unités en capacité de débit en lecture sur le compte, réparties entre toutes les tables et pareil en écriture.
  • Les temps de traitements indiqués pour les requêtes « in the single-digit milliseconds » correspondent aux temps de traitements côté service (DynamoDB). Pensez à ajouter le temps réseau pour le transport (JSON over HTTP(S)) des informations de requêtes/réponses.
  • Concernant la composante du prix liée à la capacité de débit des requêtes (Throughput Capacity), on a actuellement les tarifs suivants :
    • « Write Throughput: $0.01 per hour for every 10 units of Write Capacity »
    • « Read Throughput: $0.01 per hour for every 50 units of Read Capacity »
  • Mais qu’est ce qu’une unité de capacité ?

Il s’agit d’une requête (lecture ou écriture) par seconde pour un item de 1KB ou moins.

Voici un petit exemple de calcul de la capacité que vous allez devoir réserver (attention : on parle toujours de « réservé » ou « alloué » et pas de « consommé ») :

Si vous avez des items de 512 bytes (rentre dans la catégorie des 1KB ou moins) et que vous souhaitez lire (ou écrire) 100 items/secondes, vous allez devoir provisionner 100 unités de capacité de lecture (ou d’écriture). Ce qui donne en termes de prix :

Lecture : 100 unités de lecture / 50 unités de lecture (unité de base de la facturation) = 2, soit 2 x $0.01 = $0.02 par heure pour réserver une capacité de lecture de 100 unités (100 requêtes/secondes pour des items de 1KB ou moins).

Ecriture : 100 unités d’écriture / 10 unités d’écriture (unité de base de la facturation) = 10, soit 10 x $0.01 = $0.10 par heure pour réserver une capacité d’écriture de 100 unités (100 requêtes/secondes pour des items de 1KB ou moins).

Voici un autre exemple avec des items de taille plus importante :

Si vous avez des items de 14.5KB et que vous souhaitez lire (ou écrire) 100 items/secondes, vous allez devoir provisionner 15 (la taille est arrondie à l’entier supérieur) x 100 requêtes/secondes = 1500 unités de capacité de lecture (ou d’écriture). Ce qui donne en termes de prix :

Lecture : 1500 unités de lecture / 50 unités de lecture (unité de base de la facturation) = 30, soit 30 x $0.01 = $0.30 par heure pour réserver une capacité de lecture de 1500 unités (100 requêtes/secondes pour des items de 14KB+ à 15KB).

Ecriture : 1500 unités d’écriture / 10 unités d’écriture (unité de base de la facturation) = 150, soit 150 x $0.01 = $1.50 par heure pour réserver une capacité d’écriture de 1500 unités (100 requêtes/secondes pour des items de 14KB+ à 15KB).

A noter que vous pouvez choisir de requêter votre table en lecture selon 2 modes :

  • cohérence à terme (par défaut au niveau de l’API DynamoDB)
  • cohérence forte (à spécifier dans votre requête à l’API quand vous voudrez lire de la sorte : par exemple dans le SDK Ruby ajoutez :consistent_read => true)

En choisissant la cohérence à terme, vous pourrez ne provisionner que la moitié du nombre d’unités que vous avez calculé pour obtenir le même résultat en terme de requêtes (lecture) par seconde.

Ne pas oublier d’ajouter à ces exemples le stockage de la donnée (« Storage: $1.00 Per GB-month ») et éventuellement la quantité de données échangées si vous dialoguez avec le service depuis une application hors du réseau AWS (c’est-à-dire qui n’est pas sur une instance EC2 ou bien qui est sur une instance EC2 dans une autre région que le service DynamoDB que vous utilisez).

2 notes concernant les données stockées :

  • Contrairement à la capacité de débit des requêtes qui correspond à de l’alloué/provisionné/réservé, la capacité de stockage correspond à du consommé en termes de facturation.
  • DynamoDB est une base de données indexées et la quantité d’espace disque correspond à la taille des données que vous avez chargées plus un overhead par item de 100 bytes pour l’indexation.

Bench

J’ai effectué un mini bench à partir d’une instance EC2 m1.large dans la même région que le service DynamoDB : j’ai lancé un script Ruby qui insère 10000 éléments de moins de 1Ko dans une table vide dont j’ai fait varier la capacité de débit de requêtes en écriture. Entre chaque test, j’ai détruit/recréé la table.

Voici le code que j’ai utilisé basé sur le SDK Ruby (que je vous recommande par ailleurs) proposé par AWS :

require 'rubygems'
require 'aws'

dynamo_db = AWS::DynamoDB.new(
:access_key_id => 'XXXXXXXXXXXXXXXXXX',
:secret_access_key => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')

table = dynamo_db.tables['TestFredTableForDecrypt']
table.hash_key = [:ImageID, :string]

10000.times do |i|
    item = table.items.create(
    :ImageID => "img-#{i}",
    :name => "name-#{i}",
    :other => ["nothing", "NA", "nada", "nil", "other-#{i}"])
end

Le résultat de ce test est le suivant :
AWS – DynamoDB – Débit écritures
AWS – DynamoDB – Débit écritures (DataSource)

On constate que le débit en écriture correspond bien à ce que nous attendions :

  • 10000 w / 5 w/s = 2000 s = 33,33 min
  • 10000 w / 20 w/s = 2000 s = 8,33 min
  • 10000 w / 40 w/s = 250 s = 4,17 min

Une dérive apparaît à partir de 160 unités, mais cela n’est pas représentatif car le test est effectué à partir d’un seul thread Ruby (l’instance EC2 m1.large elle-même n’était pas beaucoup sollicitée – CPU, Load Average, …) qui a du rencontrer sa propre limite. :o)

J’ai bien entendu vérifié après chaque test que tout avait bien été inséré : table.items.count ! :o)

A noter que j’ai aussi essayé le même test en ne recréant pas la table et en doublant la capacité en écriture à chaque fois : le résultat est le même et chaque opération de modification de la capacité du débit a pris au maximum une minute pour ces 10000 éléments.

Comme précisé dans l’introduction, il s’agit d’un mini bench. Pour bien faire avec plus de temps, il faudrait refaire le même test en optimisant un injecteur (l’instance EC2) avec plusieurs threads et en multipliant ce nombre d’injecteurs pour bencher DynamoDB et pas le code Ruby ou l’OS. Il faudrait alors vérifier la tenue des performances en écriture et en lecture (fonction de la cohérence) et surtout voir si la modification de capacité du débit sur un gros volume de données est complètement transparente pour les requêtes en cours d’exécution et quelle est la durée nécessaire à cette augmentation.

Conclusion

DynamoDB est un outil intéressant car il s’inscrit dans un démarche de plus en plus packagée des services, plus proche d’un PaaS que d’un IaaS. Le modèle est similaire à celui de SimpleDB, mais le gros avantage réside dans la non limitation de la taille d’une table et de la garantie de la performance quel que soit le volume de données stockées (sans parler de la disponibilité et de la durabilité des données qui sont assurées de manière inhérente par Amazon). Ce point technique est réalisé par la répartition (sharding) des données sur plusieurs serveurs (plus le débit souhaité sera important, plus le nombre de serveurs le sera aussi) pour permettre l’exécution de requêtes en parallèle. On comprend bien alors que l’augmentation de cette capacité peut prendre du temps sur de gros volumes, cela ne me choque pas, d’autant plus que le service reste disponible pendant cette opération !

Je suis un peu plus embêté par la limitation à un facteur 2 d’augmentation de ce débit par opération. Je n’ai pas trouvé de formulaire pour upgrader cette limite. Même en s’y prenant à l’avance, cela peut être fastidieux de recommencer cette opération (avec le temps d’attente induit) si le site concerné attend un pourcentage d’augmentation significatif.

Dans tous les cas, il faut bien comprendre que cette scalabilité n’est pas instantanée, mais qu’il faut déterminer son capacity planning et ensuite l’appliquer : le très gros avantage c’est qu’il suffit pour le mettre en place d’envoyer une requête (ou quelques unes) aux AWS qui s’occupent de tout le reste.

Au niveau fonctionnalités/modèle on retrouve un classique du NoSQL avec ses choix propres et compromis entre complétude/performance. C’est donc un moteur de base de données non relationnel intéressant qu’il faudra prendre en compte dans les choix architecturaux de nos futures applications, au même titre que d’autres moteurs sur ce créneau (y compris SimpleDB qui conserve un intérêt du fait de fonctionnalités supplémentaires possibles via ses APIs par rapport aux APIs de dynamoDB). Il faudra suivre avec attention les prochaines releases du service pour voir les fonctionnalités qui seront embarquées en plus dans le produit.

Frédéric FAURE @Twitter @Ysance

6 commentaires pour DynamoDB : le décryptage de la nouvelle solution NoSQL de AWS

  • Bonjour,

    Serait-il possible de mettre en perspective DynamoDB et HBase ? J’y vois beaucoup de similitude et je serai ravi d’avoir votre regard sur ces deux bases NoSQL

  • Je ne connais HBase que de manière générale : le concept est similaire au niveau du modèle de la donnée et au niveau de la répartition (sharding) des données sur plusieurs serveurs pour assurer les performances, mais c’est justement les « détails » qu’il faut observer pour voir les différences et savoir si cela va correspondre à ce que l’on cherche : taille et type des objets qu’il est possible de stocker, opérations disponibles, … Et là, je ne connais pas suffisamment.

    Mais la comparaison s’arrête là car l’avantage de DynamoDB et ce qui fait la différence avec d’autres possibilités NoSQL c’est qu’il est vendu comme un service. Pas besoin de se soucier de ce qui va héberger notre système.

    Ce qui induit un autre point : avant de mettre en place HBase, il faut s’assurer d’avoir suffisamment de données à y stocker, sinon ce ne sera ni efficace, ni intéressant. DynamoDB peut être utilisé pour n’importe quelle quantité de données et la capacité de débit est assurée par ailleurs.

    La mise à disposition est donc complètement différente.

    Frédéric FAURE

  • Yann

    Bonjour,

    Est-il judicieux de garder une même structure de données lorsque l’on passe d’une basse de données relationnelle à une base DynamoDB ?
    (en partant bien entendu du principe que les colonnes sont facultatives)

    Ou faut-il complètement la remodeler ?

    le mode de tarification a-t-il une incidence sur cette modélisation dans le sens où la taille des items est prise en compte ?

  • Le passage d’une base relationnelle à une base NoSQL de manière générale nécessite de revoir complètement le modèle de données, et encore si cela est possible puisqu’en NoSQL vous n’aurez pas toutes les opérations qui sont disponibles en modèle relationnel classique. Donc peut-être que votre objectif ne pourra pas être atteint en NoSQL.

    Effectivement, la tarification a une incidence sur cette modélisation, mais comme n’importe quelle base de données où on devrait optimiser la structure de sa base pour diminuer l’espace des données sur le disque, ou bien jouer sur de la dé-normalisation pour optimiser la performance des requêtes, … Modéliser sa base en fonction de ses priorités en fait. Il peut être intéressant, en plus de jouer sur la structure des items, d’optimiser la taille des attributs (nom et valeur) que l’on stocke.

  • Data005dy

    Salut à tous,

    DynamoDB n’est apparemment pas fait pour les requêtes telles qu’on a l’habitude d’utiliser dans les BDD opérationnelles.
    J’ai pu avoir une démonstration par le CTO d’une startup, j’ai été assez convaincu, cependant je suis encore débutant dans le domaine et j’aimerais avoir l’avis d’autres professionnels si possible.
    Je suis entrain de mettre en place une BDD analytique qui aura pour objectif de stocker les évènements utilisateurs sur une application web. Le modèle économique de la société est orienté Saas. Je suis fortement intéressé pour intégrer DynamoDB, quelle est la meilleure façon de le faire ?
    Et quelles alternatives à DynamoDB avec les mêmes performance ? en terme d’architecture technique et d’outils.

    Merci !

  • Bonjour !

    Désolé du délai pour la réponse, mais j’étais en congés ! Et en congés… Pas de PC ! :o)

    DynamoDB est effectivement un bon produit, mais comme toute base de type NoSQL, il faut voir si le fonctionnel que vous voulez mettre en place correspond à l’utilisation cible du produit comme il est décrit dans son API. La question à se poser est : pourrai-je effectuer toutes les opérations que je souhaite avec les limitations de l’API proposée par rapport à une base « relationnelle » classique. Par exemple dans le cas du stockage des évènements utilisateurs, cela peut être indiqué, mais quel type de requêtes allez-vous effectuer dessus ensuite ?

    Egalement certaines contraintes comme celle liée à la gestion de l’augmentation/diminution de la capacité en lecture ou en écriture est à prendre en compte : aurez-vous de fortes variations ponctuelles ?

    Sinon l’outil est bien sûr adapté au modèle économique SaaS puisque vous n’augmentez la capacité (et donc payez plus) que lorsque vous avez les clients.

    Concernant les alternatives, il y en a beaucoup (trop ?) en termes de fonctionnalités ou bien d’architectures (les bases NoSQL diverses et variées se sont multipliées). Mais ce que vend DynamoDB c’est avant tout de ne pas vous soucier de toute la partie technique (infrastructure et architecture) derrière et vous permet ainsi de vous concentrer sur le métier de votre application.

Répondre

 

 

 

Vous pouvez utiliser ces balises HTML

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>