DynamoDB : Guide complet sur la base de données NoSQL ultra-performante d'AWS

samedi 29 mars 202520 min de lecturePar Damien Gilbrin
DynamoDB : Guide complet sur la base de données NoSQL ultra-performante d'AWS

Table des matières

🗄️

Introduction

Dans le monde moderne des applications cloud, la gestion des données à grande échelle représente un défi considérable. Les bases de données traditionnelles relationnelles, bien qu'éprouvées, atteignent souvent leurs limites face aux exigences de performances, d'évolutivité et de disponibilité des applications modernes.

C'est dans ce contexte qu'Amazon a développé DynamoDB, un service de base de données NoSQL entièrement géré qui offre des performances exceptionnelles à n'importe quelle échelle. Conçu à l'origine pour répondre aux besoins internes d'Amazon pendant la période cruciale du Black Friday, DynamoDB est aujourd'hui l'une des solutions de base de données les plus robustes et évolutives disponibles dans le cloud.

DynamoDB fait partie des services fondamentaux d'AWS, lancé en 2012. Il est né de l'évolution des technologies internes d'Amazon, notamment Dynamo (le prédécesseur publié dans un article de recherche en 2007) et des besoins croissants de l'entreprise en matière de stockage de données à grande échelle.

Dans cet article, nous allons explorer en profondeur ce qu'est DynamoDB, comment il fonctionne, et pourquoi il pourrait être la solution idéale pour vos applications. Nous examinerons ses principales caractéristiques, les modèles de conception associés, et nous vous guiderons à travers des exemples concrets pour vous aider à tirer le meilleur parti de ce service puissant.

🔍

Qu'est-ce que DynamoDB ?

Amazon DynamoDB est un service de base de données NoSQL entièrement géré qui fournit des performances rapides et prévisibles avec une évolutivité sans limites. DynamoDB élimine les contraintes opérationnelles et administratives des bases de données traditionnelles, vous permettant de vous concentrer sur la création d'applications plutôt que sur la gestion d'infrastructure.

Comment fonctionne DynamoDB ?

DynamoDB fonctionne sur un modèle fondamentalement différent des bases de données relationnelles traditionnelles :

  • Stockage clé-valeur et document : DynamoDB stocke les données sous forme de collections d'éléments (items) avec des attributs. Chaque élément est accessible par une clé primaire unique.

  • Architecture distribuée : Les données sont automatiquement répliquées sur plusieurs serveurs et zones de disponibilité pour assurer durabilité et haute disponibilité.

  • Partitionnement : DynamoDB distribue automatiquement les données et la charge de travail entre un nombre suffisant de serveurs pour gérer les exigences de capacité.

  • Sans serveur : Aucun serveur à gérer ou à provisionner, AWS s'occupe de tout le travail d'administration de la base de données.

Cette architecture offre des performances constantes à pratiquement n'importe quelle échelle, ce qui en fait un choix idéal pour les applications qui nécessitent un accès aux données rapide et prévisible, quel que soit le volume de données ou le nombre d'utilisateurs.

Concepts fondamentaux

  • Tables : Conteneurs de niveau supérieur pour les données dans DynamoDB, similaires aux tables dans les bases de données relationnelles mais sans schéma fixe.

  • Éléments (Items) : Équivalents aux lignes ou enregistrements dans une base de données relationnelle. Chaque élément est une collection d'attributs uniquement identifiée par une clé primaire.

  • Attributs : Équivalents aux colonnes dans une base de données relationnelle, mais chaque élément peut avoir un ensemble différent d'attributs.

  • Clé primaire : Identification unique pour chaque élément dans une table, peut être simple (clé de partition) ou composite (clé de partition + clé de tri).

  • Index secondaires : Structures de données supplémentaires qui permettent d'interroger efficacement les données sur des attributs autres que la clé primaire.

Caractéristiques clés

Performances exceptionnelles

DynamoDB offre des performances exceptionnelles avec une latence constamment faible :

  • Latence en millisecondes : Temps de réponse de l'ordre de quelques millisecondes pour les opérations de lecture et d'écriture, quelle que soit la taille de la base de données.

  • Mise en cache intégrée : DynamoDB Accelerator (DAX) fournit un cache en mémoire qui réduit les temps de réponse de microsecondes.

  • Capacité réservée : Possibilité de provisionner la capacité exacte nécessaire pour garantir les performances.

Les performances de DynamoDB sont véritablement impressionnantes : des tests indépendants ont démontré que DynamoDB peut traiter plus de 20 millions de requêtes par seconde avec une latence constante inférieure à 10 millisecondes. Même à des échelles extrêmes, les performances restent prévisibles.

Évolutivité sans limites

L'une des forces principales de DynamoDB est son évolutivité quasi infinie :

  • Pas de limites pratiques : Capable de stocker et de traiter des pétaoctets de données et des milliards d'éléments.

  • Mise à l'échelle transparente : Augmentation ou diminution automatique de la capacité en fonction de la demande.

  • Partitionnement automatique : Distribution automatique des données entre les partitions pour équilibrer la charge.

Haute disponibilité et durabilité

  • SLA de 99,999% : Un des meilleurs accords de niveau de service dans l'industrie.

  • Réplication multi-AZ : Données automatiquement répliquées sur plusieurs zones de disponibilité.

  • Réplication globale : Tables globales pour la réplication multi-région avec résolution de conflits intégrée.

Sécurité robuste

  • Intégration avec IAM : Contrôle d'accès fin basé sur les identités et les politiques AWS.

  • Chiffrement : Chiffrement au repos et en transit par défaut.

  • VPC Endpoints : Accès privé via des points de terminaison VPC.

Flexibilité du modèle de données

  • Sans schéma : Aucune nécessité de définir un schéma fixe avant d'ajouter des données.

  • Support JSON : Stockage natif de documents JSON complexes.

  • Types de données riches : Support pour les types de données scalaires, document et ensemble.

🚀

Bien démarrer avec DynamoDB

Avant de plonger dans les détails techniques, explorons les bases de la création et de l'utilisation de DynamoDB.

Création d'une table

Pour créer une table DynamoDB, vous devez définir :

  • Nom de la table : Identifiant unique pour votre table.

  • Clé primaire : Peut être une clé de partition simple ou une clé composite (partition + tri).

  • Mode de capacité : Provisionné (spécifier les capacités de lecture/écriture) ou à la demande (payer par requête).

1// Exemple de création d'une table DynamoDB avec AWS SDK pour Node.js
2const AWS = require('aws-sdk');
3AWS.config.update({ region: 'us-east-1' });
4
5const dynamodb = new AWS.DynamoDB();
6
7const params = {
8  TableName: 'Users',
9  KeySchema: [
10    { AttributeName: 'userId', KeyType: 'HASH' },  // Clé de partition
11    { AttributeName: 'email', KeyType: 'RANGE' }    // Clé de tri
12  ],
13  AttributeDefinitions: [
14    { AttributeName: 'userId', AttributeType: 'S' },
15    { AttributeName: 'email', AttributeType: 'S' }
16  ],
17  BillingMode: 'PAY_PER_REQUEST'  // Mode à la demande
18};
19
20dynamodb.createTable(params, (err, data) => {
21  if (err) {
22    console.error('Erreur lors de la création de la table:', err);
23  } else {
24    console.log('Table créée avec succès:', data);
25  }
26});

Opérations de base

DynamoDB prend en charge les opérations CRUD (Create, Read, Update, Delete) de base :

Créer un élément

1// Exemple d'ajout d'un élément
2const docClient = new AWS.DynamoDB.DocumentClient();
3
4const params = {
5  TableName: 'Users',
6  Item: {
7    userId: '12345',
8    email: 'john.doe@example.com',
9    name: 'John Doe',
10    age: 30,
11    address: {
12      street: '123 Main St',
13      city: 'Seattle',
14      state: 'WA',
15      zipCode: '98101'
16    },
17    interests: ['photography', 'hiking', 'reading'],
18    createdAt: new Date().toISOString()
19  }
20};
21
22docClient.put(params, (err, data) => {
23  if (err) {
24    console.error('Erreur lors de l\'ajout de l\'élément:', err);
25  } else {
26    console.log('Élément ajouté avec succès');
27  }
28});

Lire un élément

1// Exemple de lecture d'un élément par sa clé primaire
2const params = {
3  TableName: 'Users',
4  Key: {
5    userId: '12345',
6    email: 'john.doe@example.com'
7  }
8};
9
10docClient.get(params, (err, data) => {
11  if (err) {
12    console.error('Erreur lors de la lecture de l\'élément:', err);
13  } else if (!data.Item) {
14    console.log('Élément non trouvé');
15  } else {
16    console.log('Élément récupéré avec succès:', data.Item);
17  }
18});

Mettre à jour un élément

1// Exemple de mise à jour d'un élément
2const params = {
3  TableName: 'Users',
4  Key: {
5    userId: '12345',
6    email: 'john.doe@example.com'
7  },
8  UpdateExpression: 'set age = :a, address.city = :c, updatedAt = :u',
9  ExpressionAttributeValues: {
10    ':a': 31,  // Nouvel âge
11    ':c': 'Portland',  // Nouvelle ville
12    ':u': new Date().toISOString()  // Horodatage de mise à jour
13  },
14  ReturnValues: 'UPDATED_NEW'  // Retourne les attributs mis à jour
15};
16
17docClient.update(params, (err, data) => {
18  if (err) {
19    console.error('Erreur lors de la mise à jour de l\'élément:', err);
20  } else {
21    console.log('Élément mis à jour avec succès. Nouvelles valeurs:', data.Attributes);
22  }
23});

Supprimer un élément

1// Exemple de suppression d'un élément
2const params = {
3  TableName: 'Users',
4  Key: {
5    userId: '12345',
6    email: 'john.doe@example.com'
7  }
8};
9
10docClient.delete(params, (err, data) => {
11  if (err) {
12    console.error('Erreur lors de la suppression de l\'élément:', err);
13  } else {
14    console.log('Élément supprimé avec succès');
15  }
16});

Requêtes et analyses

DynamoDB offre deux principales méthodes pour récupérer plusieurs éléments : Query et Scan.

Opération Query

L'opération Query trouve des éléments en fonction des valeurs de clé primaire :

  • Nécessite de spécifier la clé de partition exacte.

  • Peut filtrer davantage sur la clé de tri si vous utilisez une clé primaire composite.

  • Efficace et peu coûteuse, car elle accède directement à la partition spécifique.

1// Exemple de requête pour trouver tous les utilisateurs avec un userId spécifique
2// et un email commençant par 'john'
3const params = {
4  TableName: 'Users',
5  KeyConditionExpression: 'userId = :uid AND begins_with(email, :e)',
6  ExpressionAttributeValues: {
7    ':uid': '12345',
8    ':e': 'john'
9  }
10};
11
12docClient.query(params, (err, data) => {
13  if (err) {
14    console.error('Erreur lors de l\'exécution de la requête:', err);
15  } else {
16    console.log(`Éléments trouvés: ${data.Items.length}`);
17    console.log('Résultats:', data.Items);
18  }
19});

Opération Scan

L'opération Scan examine chaque élément d'une table :

  • Peut appliquer des filtres, mais examine quand même tous les éléments.

  • Moins efficace et plus coûteuse que Query, surtout pour les grandes tables.

  • Utile pour les opérations de données en masse ou lorsque vous ne connaissez pas la clé primaire.

1// Exemple de scan pour trouver tous les utilisateurs âgés de plus de 30 ans
2const params = {
3  TableName: 'Users',
4  FilterExpression: 'age > :a',
5  ExpressionAttributeValues: {
6    ':a': 30
7  }
8};
9
10docClient.scan(params, (err, data) => {
11  if (err) {
12    console.error('Erreur lors de l\'exécution du scan:', err);
13  } else {
14    console.log(`Éléments trouvés: ${data.Items.length}`);
15    console.log('Résultats:', data.Items);
16    
17    // Vérifier s'il y a plus de résultats (pagination)
18    if (data.LastEvaluatedKey) {
19      console.log('Plus de résultats disponibles, LastEvaluatedKey:', data.LastEvaluatedKey);
20    }
21  }
22});

Bonne pratique : Évitez d'utiliser Scan sur de grandes tables en production. Privilégiez toujours Query en concevant correctement vos clés primaires et index secondaires pour prendre en charge vos modèles d'accès aux données.

📊

Modélisation des données dans DynamoDB

La modélisation des données dans DynamoDB est fondamentalement différente de celle des bases de données relationnelles. Au lieu de normaliser les données et de créer plusieurs tables liées, DynamoDB encourage la dénormalisation et l'optimisation pour les modèles d'accès spécifiques.

Comprendre les modèles d'accès

Le principe le plus important de la modélisation avec DynamoDB est de commencer par les modèles d'accès - c'est-à-dire, comprendre exactement comment votre application accédera aux données.

  • Identifiez toutes les requêtes que votre application devra effectuer.

  • Déterminez quelles données seront lues et écrites, et à quelle fréquence.

  • Comprenez les relations entre différents types de données et comment votre application naviguera entre eux.

Principe clé de conception DynamoDB

Contrairement aux bases de données relationnelles où vous modélisez d'abord les données puis créez des requêtes, avec DynamoDB vous commencez par les requêtes (modèles d'accès) puis concevez votre modèle de données pour les prendre en charge efficacement.

Conception à table unique

Une approche populaire dans DynamoDB est la conception à table unique, où vous stockez plusieurs types d'entités dans une seule table DynamoDB.

  • Avantages : Réduit la latence en minimisant le nombre d'opérations, simplifie les transactions, réduit les coûts.

  • Complexité : Structure de table plus complexe, courbe d'apprentissage plus raide.

  • Mise en œuvre : Utilise des préfixes et des conventions de nommage pour distinguer différents types d'entités.

1// Exemple d'implémentation d'une conception à table unique
2// Table qui stocke des utilisateurs, des commandes et des produits
3
4// Ajouter un utilisateur
5const userItem = {
6  PK: `USER#${userId}`,  // Clé de partition avec préfixe
7  SK: `PROFILE#${userId}`,  // Clé de tri avec préfixe
8  type: 'USER',
9  name: 'John Doe',
10  email: 'john@example.com',
11  // autres attributs d'utilisateur
12};
13
14// Ajouter une commande pour cet utilisateur
15const orderItem = {
16  PK: `USER#${userId}`,  // Même utilisateur (clé de partition)
17  SK: `ORDER#${orderId}`,  // Différente clé de tri pour la commande
18  type: 'ORDER',
19  orderDate: '2023-05-15',
20  status: 'PENDING',
21  amount: 129.99,
22  // autres attributs de commande
23};
24
25// Ajouter des détails de produit dans la commande
26const orderProductItem = {
27  PK: `ORDER#${orderId}`,  // Clé de partition de la commande
28  SK: `PRODUCT#${productId}`,  // Clé de tri du produit
29  type: 'ORDER_PRODUCT',
30  productName: 'Wireless Headphones',
31  quantity: 1,
32  price: 79.99,
33  // autres attributs
34};
35
36// Plus tard, vous pouvez facilement interroger tous les produits d'une commande
37const params = {
38  TableName: 'MyStore',
39  KeyConditionExpression: 'PK = :pk AND begins_with(SK, :skPrefix)',
40  ExpressionAttributeValues: {
41    ':pk': `ORDER#${orderId}`,
42    ':skPrefix': 'PRODUCT#'
43  }
44};
45
46// Ou toutes les commandes d'un utilisateur
47const userOrdersParams = {
48  TableName: 'MyStore',
49  KeyConditionExpression: 'PK = :pk AND begins_with(SK, :skPrefix)',
50  ExpressionAttributeValues: {
51    ':pk': `USER#${userId}`,
52    ':skPrefix': 'ORDER#'
53  }
54};

Choisir les bonnes clés

Le choix des clés primaires est l'aspect le plus crucial de la conception DynamoDB :

Clé de partition

  • Détermine comment les données sont distribuées entre les partitions.

  • Doit avoir une cardinalité élevée (beaucoup de valeurs uniques) pour éviter les points chauds.

  • Exemples : ID utilisateur, ID produit, GUID, valeurs hachées.

Clé de tri

  • Permet de trier les éléments au sein d'une partition.

  • Permet des requêtes efficaces avec des opérateurs comme begins_with, between, >, <, etc.

  • Peut être utilisée pour modéliser des relations hiérarchiques ou chronologiques.

  • Exemples : horodatages, versions, préfixes hiérarchiques.

Voici quelques stratégies courantes pour les clés composites :

  • Hiérarchie : organisation#123/département#marketing/utilisateur#john

  • Chronologique : Clé de partition pour l'identité + horodatage comme clé de tri pour les séries chronologiques

  • Inversée : Stocker la même donnée avec des combinaisons différentes de PK/SK pour prendre en charge différents modèles d'accès

Index secondaires

Les index secondaires permettent d'interroger les données sur des attributs autres que la clé primaire :

Index secondaire global (GSI)

  • Possède une clé primaire différente de la table de base.

  • Peut couvrir l'ensemble de la table.

  • Fonctionne comme une vue dérivée de votre table principale.

  • Limite de 20 GSI par table.

1// Définition d'un GSI lors de la création d'une table
2const params = {
3  TableName: 'Users',
4  // ... autres paramètres de table ...
5  
6  GlobalSecondaryIndexes: [
7    {
8      IndexName: 'EmailIndex',
9      KeySchema: [
10        { AttributeName: 'email', KeyType: 'HASH' }  // Clé de partition de l'index
11      ],
12      Projection: {
13        ProjectionType: 'ALL'  // Projeter tous les attributs dans l'index
14      },
15      ProvisionedThroughput: {  // Si vous utilisez le mode provisionné
16        ReadCapacityUnits: 5,
17        WriteCapacityUnits: 5
18      }
19    }
20  ]
21};
22
23// Interrogation à l'aide du GSI
24const queryParams = {
25  TableName: 'Users',
26  IndexName: 'EmailIndex',
27  KeyConditionExpression: 'email = :email',
28  ExpressionAttributeValues: {
29    ':email': 'john@example.com'
30  }
31};

Index secondaire local (LSI)

  • Partage la même clé de partition que la table de base, mais utilise une clé de tri différente.

  • Doit être créé lors de la création de la table (impossible d'ajouter ultérieurement).

  • Limite de 5 LSI par table.

  • Utile pour les requêtes qui filtrent sur la même identité mais avec différents critères de tri.

1// Définition d'un LSI lors de la création d'une table
2const params = {
3  TableName: 'Orders',
4  KeySchema: [
5    { AttributeName: 'customerId', KeyType: 'HASH' },  // Clé de partition
6    { AttributeName: 'orderId', KeyType: 'RANGE' }     // Clé de tri
7  ],
8  AttributeDefinitions: [
9    { AttributeName: 'customerId', AttributeType: 'S' },
10    { AttributeName: 'orderId', AttributeType: 'S' },
11    { AttributeName: 'orderDate', AttributeType: 'S' }  // Attribut pour LSI
12  ],
13  
14  LocalSecondaryIndexes: [
15    {
16      IndexName: 'OrderDateIndex',
17      KeySchema: [
18        { AttributeName: 'customerId', KeyType: 'HASH' },  // Même clé de partition
19        { AttributeName: 'orderDate', KeyType: 'RANGE' }   // Différente clé de tri
20      ],
21      Projection: {
22        ProjectionType: 'INCLUDE',
23        NonKeyAttributes: ['status', 'amount']
24      }
25    }
26  ]
27};
28
29// Interrogation à l'aide du LSI
30const queryParams = {
31  TableName: 'Orders',
32  IndexName: 'OrderDateIndex',
33  KeyConditionExpression: 'customerId = :cid AND orderDate BETWEEN :start AND :end',
34  ExpressionAttributeValues: {
35    ':cid': 'CUST#12345',
36    ':start': '2023-01-01',
37    ':end': '2023-12-31'
38  }
39};

GSI vs LSI : Quand utiliser l'un ou l'autre ?

Utilisez des GSI pour la plupart des cas d'utilisation, en particulier lorsque vous avez besoin d'un nouveau modèle d'accès qui utilise une clé primaire complètement différente. Les LSI sont utiles uniquement lorsque vous avez besoin de garanties de cohérence forte pour des requêtes avec la même clé de partition mais triées différemment.

Gestion de la capacité et performances

DynamoDB offre deux modes de capacité principaux pour gérer les performances et les coûts.

Mode provisionné

  • Vous spécifiez à l'avance le nombre d'unités de capacité de lecture (RCU) et d'écriture (WCU) dont vous avez besoin.

  • Moins cher pour les charges de travail prévisibles et stables.

  • Peut utiliser le dimensionnement automatique pour ajuster la capacité en fonction de l'utilisation.

Comment les capacités fonctionnent :

  • 1 RCU = 1 lecture fortement cohérente jusqu'à 4 Ko par seconde (ou 2 lectures éventuellement cohérentes).

  • 1 WCU = 1 écriture jusqu'à 1 Ko par seconde.

1// Configuration d'une table en mode provisionné avec auto scaling
2const params = {
3  TableName: 'MyTable',
4  KeySchema: [
5    // ... définition de clé ...
6  ],
7  AttributeDefinitions: [
8    // ... définitions d'attributs ...
9  ],
10  BillingMode: 'PROVISIONED',
11  ProvisionedThroughput: {
12    ReadCapacityUnits: 5,    // Capacité de lecture de base
13    WriteCapacityUnits: 5    // Capacité d'écriture de base
14  }
15};
16
17// Ajout de l'auto scaling avec AWS Application Auto Scaling
18// Exemple avec AWS CloudFormation
19/*
20MyTableReadCapacityScaling:
21  Type: AWS::ApplicationAutoScaling::ScalableTarget
22  Properties:
23    MaxCapacity: 100
24    MinCapacity: 5
25    ResourceId: !Sub table/${MyTable}
26    ScalableDimension: dynamodb:table:ReadCapacityUnits
27    ServiceNamespace: dynamodb
28
29MyTableReadScalingPolicy:
30  Type: AWS::ApplicationAutoScaling::ScalingPolicy
31  Properties:
32    PolicyName: ReadScalingPolicy
33    PolicyType: TargetTrackingScaling
34    ScalingTargetId: !Ref MyTableReadCapacityScaling
35    TargetTrackingScalingPolicyConfiguration:
36      TargetValue: 70.0
37      PredefinedMetricSpecification:
38        PredefinedMetricType: DynamoDBReadCapacityUtilization
39*/

Mode à la demande

  • Paiement par demande, sans nécessité de spécifier à l'avance la capacité.

  • S'adapte instantanément aux pics de trafic sans aucune configuration.

  • Idéal pour les charges de travail imprévisibles, les applications en développement ou les cas d'utilisation sporadiques.

  • Plus cher que le mode provisionné pour les charges de travail stables et prévisibles.

1// Configuration d'une table en mode à la demande
2const params = {
3  TableName: 'MyEventDrivenTable',
4  KeySchema: [
5    // ... définition de clé ...
6  ],
7  AttributeDefinitions: [
8    // ... définitions d'attributs ...
9  ],
10  BillingMode: 'PAY_PER_REQUEST'
11};

DynamoDB Accelerator (DAX)

Pour les applications qui nécessitent des performances en microsecondes, AWS propose DAX, un service de cache en mémoire entièrement géré pour DynamoDB.

  • Réduit les temps de réponse de millisecondes à microsecondes, même à des millions de requêtes par seconde.

  • Fonctionne comme un cache en écriture différée, réduisant les coûts d'utilisation des RCU.

  • Compatible avec les SDK DynamoDB existants, nécessitant des changements minimes dans l'application.

  • Idéal pour les applications à lecture intensive avec des exigences de latence ultra-faible.

Comprendre les performances réelles

Les performances de DynamoDB sont véritablement impressionnantes dans des scénarios réels :

  • Latence : Latence constante de 1 à 10 millisecondes pour les opérations individuelles, quelle que soit la taille de la table.

  • Débit : Capacité à gérer plus de 20 millions de requêtes par seconde pour une seule table.

  • Mise à l'échelle : Traitement de plus de 10 milliards d'opérations par jour pour des clients comme Amazon.com.

  • Avec DAX : Réduction de la latence moyenne à 200-300 microsecondes pour les lectures en cache.

Lors d'un test de charge réel, une table DynamoDB a maintenu un débit constant de 1,5 million d'opérations par seconde pendant 24 heures sans dégradation des performances, démontrant sa capacité à gérer des charges extrêmes. Avec DAX, les applications peuvent atteindre des millions de lectures par seconde avec une latence inférieure à une milliseconde, ce qui est comparable aux bases de données en mémoire les plus performantes.

🔧

Fonctionnalités avancées

DynamoDB offre de nombreuses fonctionnalités avancées qui étendent ses capacités au-delà d'une simple base de données NoSQL.

Transactions

DynamoDB prend en charge les transactions ACID sur plusieurs éléments et tables :

  • TransactWriteItems : Effectue plusieurs opérations d'écriture comme une seule unité atomique.

  • TransactGetItems : Effectue plusieurs opérations de lecture comme une seule unité atomique.

  • Garantit l'atomicité, la cohérence, l'isolation et la durabilité (ACID).

1// Exemple de transaction pour transférer des fonds entre comptes
2const params = {
3  TransactItems: [
4    {
5      Update: {
6        TableName: 'Accounts',
7        Key: { accountId: 'account1' },
8        UpdateExpression: 'set balance = balance - :amount',
9        ConditionExpression: 'balance >= :amount',  // Vérifier les fonds suffisants
10        ExpressionAttributeValues: { ':amount': 100 }
11      }
12    },
13    {
14      Update: {
15        TableName: 'Accounts',
16        Key: { accountId: 'account2' },
17        UpdateExpression: 'set balance = balance + :amount',
18        ExpressionAttributeValues: { ':amount': 100 }
19      }
20    },
21    {
22      Put: {
23        TableName: 'Transactions',
24        Item: {
25          transactionId: 'tx123',
26          fromAccount: 'account1',
27          toAccount: 'account2',
28          amount: 100,
29          status: 'COMPLETED',
30          timestamp: new Date().toISOString()
31        }
32      }
33    }
34  ]
35};
36
37docClient.transactWrite(params, (err, data) => {
38  if (err) {
39    console.error('Transaction failed:', err);
40  } else {
41    console.log('Transaction completed successfully');
42  }
43});

Time to Live (TTL)

DynamoDB permet de définir une durée de vie automatique pour les éléments :

  • Définissez un attribut qui contient un horodatage d'expiration (en secondes depuis epoch).

  • DynamoDB supprime automatiquement les éléments expirés, généralement dans les 48 heures suivant l'expiration.

  • Idéal pour les données temporaires, les sessions, les journaux, etc.

1// Activer TTL sur une table
2const params = {
3  TableName: 'SessionData',
4  TimeToLiveSpecification: {
5    AttributeName: 'expiresAt',  // Nom de l'attribut qui contient l'horodatage d'expiration
6    Enabled: true
7  }
8};
9
10dynamodb.updateTimeToLive(params, (err, data) => {
11  if (err) console.error('Erreur lors de l\'activation de TTL:', err);
12  else console.log('TTL activé avec succès:', data);
13});
14
15// Ajouter un élément avec TTL
16const sessionItem = {
17  sessionId: 'sess_123456',
18  userId: 'user_789',
19  data: { /* données de session */ },
20  createdAt: new Date().toISOString(),
21  // Expirer dans 24 heures (86400 secondes)
22  expiresAt: Math.floor(Date.now() / 1000) + 86400
23};
24
25docClient.put({
26  TableName: 'SessionData',
27  Item: sessionItem
28}, (err) => {
29  if (err) console.error('Erreur lors de l\'ajout de la session:', err);
30  else console.log('Session ajoutée avec succès');
31});

DynamoDB Streams

DynamoDB Streams capture les modifications des éléments d'une table dans l'ordre chronologique :

  • Capture les insertions, mises à jour et suppressions.

  • Conserve les modifications pendant 24 heures.

  • Permet l'intégration avec Lambda pour des réactions en temps réel aux changements de données.

  • Utile pour les agrégations, les notifications, la réplication, etc.

1# Exemple AWS SAM pour configurer une fonction Lambda déclenchée par DynamoDB Streams
2Resources:
3  MyTable:
4    Type: AWS::DynamoDB::Table
5    Properties:
6      TableName: Users
7      AttributeDefinitions:
8        - AttributeName: userId
9          AttributeType: S
10      KeySchema:
11        - AttributeName: userId
12          KeyType: HASH
13      BillingMode: PAY_PER_REQUEST
14      StreamSpecification:
15        StreamViewType: NEW_AND_OLD_IMAGES  # Capture les images avant et après
16  
17  StreamProcessor:
18    Type: AWS::Serverless::Function
19    Properties:
20      Handler: index.handler
21      Runtime: nodejs14.x
22      CodeUri: ./stream-processor/
23      Policies:
24        - DynamoDBStreamReadPolicy:
25            TableName: !Ref MyTable
26      Events:
27        Stream:
28          Type: DynamoDB
29          Properties:
30            Stream: !GetAtt MyTable.StreamArn
31            StartingPosition: TRIM_HORIZON
32            BatchSize: 100
1// Exemple de fonction Lambda traitant les événements DynamoDB Streams
2exports.handler = async (event) => {
3  for (const record of event.Records) {
4    console.log('Stream record:', JSON.stringify(record, null, 2));
5    
6    if (record.eventName === 'INSERT') {
7      // Traiter un nouvel élément
8      const newItem = record.dynamodb.NewImage;
9      await processNewUser(newItem);
10    } else if (record.eventName === 'MODIFY') {
11      // Traiter une mise à jour d'élément
12      const oldItem = record.dynamodb.OldImage;
13      const newItem = record.dynamodb.NewImage;
14      await processUserUpdate(oldItem, newItem);
15    } else if (record.eventName === 'REMOVE') {
16      // Traiter une suppression d'élément
17      const oldItem = record.dynamodb.OldImage;
18      await processUserDeletion(oldItem);
19    }
20  }
21  
22  return { statusCode: 200, body: 'Processed successfully' };
23};
24
25async function processNewUser(user) {
26  // Exemple : Envoyer un email de bienvenue
27  console.log(`Nouvel utilisateur créé: ${user.email.S}`);
28  // Implémentation de l'envoi d'email...
29}
30
31async function processUserUpdate(oldUser, newUser) {
32  // Exemple : Détecter les modifications d'adresse pour mettre à jour un système externe
33  if (oldUser.address && newUser.address && 
34      oldUser.address.S !== newUser.address.S) {
35    console.log(`Adresse mise à jour pour l'utilisateur: ${newUser.userId.S}`);
36    // Mettre à jour un système de livraison externe...
37  }
38}
39
40async function processUserDeletion(user) {
41  // Exemple : Nettoyer les ressources associées
42  console.log(`Utilisateur supprimé: ${user.userId.S}`);
43  // Supprimer les fichiers S3 associés, etc.
44}

Tables globales

Les tables globales permettent la réplication multi-région avec une résolution de conflits automatique :

  • Réplication bidirectionnelle entre plusieurs régions AWS.

  • Latence de lecture et d'écriture locales pour les utilisateurs du monde entier.

  • Résilience contre les pannes régionales.

  • Résolution de conflits basée sur "dernier écrivain gagne".

  • Facile à configurer et à gérer.

1// Configuration d'une table globale avec AWS SDK v3
2const { DynamoDBClient, CreateGlobalTableCommand } = require('@aws-sdk/client-dynamodb');
3
4// Fonction pour configurer une table globale
5async function setupGlobalTable() {
6  // La table doit déjà exister dans au moins une région
7  const client = new DynamoDBClient({ region: 'us-east-1' });
8  
9  const command = new CreateGlobalTableCommand({
10    GlobalTableName: 'MyGlobalTable',
11    ReplicationGroup: [
12      { RegionName: 'us-east-1' },
13      { RegionName: 'eu-west-1' },
14      { RegionName: 'ap-southeast-2' }
15    ]
16  });
17  
18  try {
19    const response = await client.send(command);
20    console.log('Table globale créée avec succès:', response);
21    return response;
22  } catch (error) {
23    console.error('Erreur lors de la création de la table globale:', error);
24    throw error;
25  }
26}

Meilleures pratiques

Pour tirer le meilleur parti de DynamoDB, suivez ces meilleures pratiques éprouvées :

Conception et modélisation

  • Commencez par les modèles d'accès : Identifiez toutes vos requêtes avant de concevoir vos tables.

  • Une seule table lorsque possible : Utilisez la conception à table unique pour réduire la latence et simplifier les requêtes.

  • Évitez les points chauds : Utilisez des clés de partition à cardinalité élevée et distribuées uniformément.

  • Limitez les collections d'éléments : Évitez de stocker trop d'éléments avec la même clé de partition.

  • Concevez pour les GSI dès le départ : Planifiez vos index secondaires lors de la conception initiale.

  • Normalisez uniquement si nécessaire : N'hésitez pas à dénormaliser et dupliquer les données pour optimiser les requêtes.

Performance et coûts

  • Utilisez des lectures éventuellement cohérentes : Elles coûtent moitié moins cher lorsque la cohérence forte n'est pas nécessaire.

  • Considérez DAX pour les lectures répétées : Utilisez DynamoDB Accelerator pour les applications à lecture intensive.

  • Choisissez le bon mode de capacité : Mode provisionné pour les charges de travail prévisibles, mode à la demande pour les charges variables.

  • Utilisez l'auto scaling : Configurez l'auto scaling pour les tables en mode provisionné.

  • Minimisez la taille des éléments : Stockez les grands objets dans S3 et conservez uniquement les métadonnées dans DynamoDB.

  • Surveillez les métriques : Utilisez CloudWatch pour surveiller l'utilisation de la capacité et la limitation.

Opérations et maintenance

  • Utilisez des transactions pour les opérations complexes : Garantissez l'intégrité des données avec des transactions ACID.

  • Gérez la durée de vie avec TTL : Utilisez TTL pour supprimer automatiquement les données obsolètes.

  • Mettez en œuvre une stratégie de sauvegarde : Utilisez les sauvegardes à la demande ou planifiées.

  • Testez les récupérations : Vérifiez régulièrement que vous pouvez restaurer à partir des sauvegardes.

  • Implémentez une limitation côté client : Utilisez des backoffs exponentiels pour gérer les erreurs de limitation.

  • Utilisez DynamoDB Streams pour les événements : Réagissez aux modifications de données en temps réel.

💡

Cas d'utilisation réels

DynamoDB est utilisé par des milliers d'entreprises pour une large gamme de cas d'utilisation. Voici quelques exemples notables :

Amazon Prime Day

Pour son événement Prime Day, Amazon utilise DynamoDB pour gérer des pics de trafic massifs :

  • Échelle : Traitement de plus de 89,9 millions de demandes par seconde.

  • Résilience : Zéro temps d'arrêt malgré une charge extrême.

  • Flexibilité : Adaptation automatique à des variations de charge de plus de 10x en quelques heures.

Lyft

Lyft utilise DynamoDB pour ses systèmes critiques de mise en relation conducteur-passager :

  • Latence faible : Traitement des mises à jour de localisation en quelques millisecondes.

  • Haute disponibilité : Fonctionnement sans interruption pour un service 24/7.

  • Évolutivité : Support de la croissance de l'entreprise de zéro à des millions d'utilisateurs.

Airbnb

Airbnb utilise DynamoDB pour diverses fonctionnalités, notamment :

  • Gestion des messages : Stockage et récupération des conversations entre hôtes et invités.

  • Traitement des paiements : Suivi des transactions financières.

  • Analytique en temps réel : Analyse des tendances de réservation et d'utilisation.

🎯

Conclusion

Amazon DynamoDB représente une évolution majeure dans le monde des bases de données, offrant une combinaison unique de performances, d'évolutivité et de fiabilité qui la rend idéale pour les applications modernes basées sur le cloud.

Sa capacité à fournir une latence constante en millisecondes à pratiquement n'importe quelle échelle, associée à une évolutivité sans limites et à une disponibilité de 99,999%, en fait un choix incontournable pour les applications critiques où les performances et la fiabilité sont essentielles.

  • Puissance inégalée : Performances constantes en millisecondes, quelle que soit la taille de la base de données.

  • Évolutivité sans limites : Capacité à s'adapter instantanément sans aucune modification de l'infrastructure.

  • Flexibilité de conception : Support pour les modèles de données clé-valeur et document.

  • Fonctionnalités riches : Transactions, TTL, Streams, tables globales, et plus encore.

  • Intégration profonde : Intégration native avec d'autres services AWS comme Lambda, S3 et CloudWatch.

Bien que l'apprentissage de DynamoDB nécessite un changement de paradigme par rapport aux bases de données relationnelles traditionnelles, les avantages en termes de performances, d'évolutivité et de réduction de la charge opérationnelle en font un investissement qui en vaut largement la peine pour les équipes de développement modernes.

Point clé à retenir

DynamoDB n'est pas seulement une base de données ; c'est un moteur de données de classe mondiale conçu pour les applications qui ne peuvent pas se permettre de ralentir, de tomber en panne ou de coûter une fortune à mesure qu'elles se développent. En commençant par comprendre vos modèles d'accès et en suivant les meilleures pratiques, vous pouvez libérer tout le potentiel de cette technologie remarquable et construire des applications véritablement évolutives et résilientes.

Pour commencer avec DynamoDB, identifiez d'abord vos modèles d'accès critiques, concevez votre modèle de données pour prendre en charge ces modèles efficacement, et commencez avec une seule table avant d'étendre votre architecture. Une conception minutieuse dès le départ vous permettra d'éviter des problèmes coûteux et complexes plus tard.

Damien Gilbrin

Damien Gilbrin

Développeur fullstack passionné, je crée des applications web performantes et modernes grâce à mon expertise en React, Next.js, PHP Symfony et les solutions AWS.