Amazon DynamoDB
La base de données NoSQL d'Amazon Web Services qui offre des performances fiables à n'importe quelle échelle, avec une haute disponibilité et une sécurité intégrée.
Qu'est-ce que DynamoDB ?
Imaginez une immense bibliothèque capable de stocker des milliards de livres, où vous pourriez instantanément retrouver n'importe quel livre, même si des millions de personnes font la même recherche en même temps. Cette bibliothèque fonctionnerait 24h/24, 7j/7, sans jamais tomber en panne ni ralentir, et s'agrandirait automatiquement lorsque de nouveaux livres arrivent.
DynamoDB est l'équivalent numérique de cette bibliothèque pour les données. C'est un service de base de données proposé par Amazon Web Services (AWS) qui permet de stocker et d'accéder à d'énormes quantités d'informations de manière fiable, rapide et économique.
Pourquoi est-ce important ?
Performances garanties
DynamoDB assure des temps de réponse rapides et constants, même pendant les pics d'activité intenses, comme un Black Friday pour les sites d'e-commerce.
Scalabilité automatique
Contrairement aux bases de données traditionnelles qui nécessitent une planification complexe pour gérer la croissance, DynamoDB s'adapte automatiquement aux besoins de votre application.
En résumé, DynamoDB est la solution idéale pour les entreprises qui ont besoin de stocker et d'accéder à de grandes quantités de données rapidement et de manière fiable, sans se soucier de la maintenance ou de la mise à l'échelle de l'infrastructure sous-jacente.
Fonctionnement technique
DynamoDB est une base de données NoSQL totalement gérée qui prend en charge à la fois les modèles de données clé-valeur et document. Elle offre une latence constante en millisecondes, quelle que soit la taille de la base de données, et intègre des fonctionnalités avancées comme la réplication multi-région, le chiffrement au repos et la gestion des flux de données en temps réel.
Concepts fondamentaux
Structure des tables et clés
DynamoDB organise les données en tables, qui sont des collections d'items (similaires aux rangées). Chaque item a une clé primaire obligatoire qui peut être simple (partition key) ou composite (partition key + sort key). Cette conception est essentielle pour des performances optimales.
- Clé de partition (Partition Key) : Détermine où les données sont stockées physiquement
- Clé de tri (Sort Key) : Permet de trier les données partageant la même clé de partition
- Attributs : Valeurs associées à chaque item, sans schéma rigide (flexibilité NoSQL)
Création de table
Voici comment créer une table DynamoDB avec l'AWS SDK pour JavaScript :
// Création d'une table DynamoDB avec AWS SDK pour JavaScript
const AWS = require('aws-sdk');
AWS.config.update({ region: 'eu-west-3' });
const dynamoDB = new AWS.DynamoDB();
const params = {
TableName: 'Users',
KeySchema: [
{ AttributeName: 'userId', KeyType: 'HASH' }, // Partition key
{ AttributeName: 'email', KeyType: 'RANGE' } // Sort key
],
AttributeDefinitions: [
{ AttributeName: 'userId', AttributeType: 'S' },
{ AttributeName: 'email', AttributeType: 'S' },
{ AttributeName: 'userStatus', AttributeType: 'S' }
],
GlobalSecondaryIndexes: [
{
IndexName: 'UserStatusIndex',
KeySchema: [
{ AttributeName: 'userStatus', KeyType: 'HASH' },
{ AttributeName: 'email', KeyType: 'RANGE' }
],
Projection: {
ProjectionType: 'ALL'
},
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5
}
}
],
BillingMode: 'PROVISIONED',
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10
},
StreamSpecification: {
StreamEnabled: true,
StreamViewType: 'NEW_AND_OLD_IMAGES'
},
Tags: [
{ Key: 'Environment', Value: 'Production' },
{ Key: 'Team', Value: 'UserManagement' }
]
};
dynamoDB.createTable(params, (err, data) => {
if (err) {
console.error('Erreur lors de la création de la table', err);
} else {
console.log('Table créée avec succès', data);
}
});
Opérations CRUD
Le DocumentClient est une abstraction de plus haut niveau qui simplifie l'interaction avec DynamoDB :
// Opérations CRUD avec le DocumentClient
const AWS = require('aws-sdk');
AWS.config.update({ region: 'eu-west-3' });
// Création d'une instance DocumentClient
const docClient = new AWS.DynamoDB.DocumentClient();
// 1. Création d'un élément
async function createUser(user) {
const params = {
TableName: 'Users',
Item: {
userId: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
userStatus: 'ACTIVE',
createdAt: new Date().toISOString(),
preferences: {
theme: 'dark',
notifications: ['email', 'push']
},
permissions: ['read', 'write']
},
// Vérification de non-existence d'un utilisateur avec cette clé
ConditionExpression: 'attribute_not_exists(userId)'
};
try {
await docClient.put(params).promise();
console.log('Utilisateur créé avec succès');
return true;
} catch (error) {
console.error('Erreur lors de la création :', error);
return false;
}
}
// 2. Lecture d'un élément
async function getUser(userId, email) {
const params = {
TableName: 'Users',
Key: {
userId: userId,
email: email
}
};
try {
const data = await docClient.get(params).promise();
return data.Item; // Renvoie l'utilisateur ou undefined s'il n'existe pas
} catch (error) {
console.error('Erreur lors de la lecture :', error);
return null;
}
}
// 3. Mise à jour d'un élément
async function updateUserStatus(userId, email, newStatus) {
const params = {
TableName: 'Users',
Key: {
userId: userId,
email: email
},
UpdateExpression: 'set userStatus = :status, updatedAt = :time',
ExpressionAttributeValues: {
':status': newStatus,
':time': new Date().toISOString(),
':expectedStatus': 'ACTIVE'
},
ConditionExpression: 'userStatus = :expectedStatus',
ReturnValues: 'UPDATED_NEW'
};
try {
const data = await docClient.update(params).promise();
console.log('Utilisateur mis à jour avec succès', data);
return data.Attributes;
} catch (error) {
console.error('Erreur lors de la mise à jour :', error);
return null;
}
}
// 4. Suppression d'un élément
async function deleteUser(userId, email) {
const params = {
TableName: 'Users',
Key: {
userId: userId,
email: email
},
ReturnValues: 'ALL_OLD'
};
try {
const data = await docClient.delete(params).promise();
console.log('Utilisateur supprimé avec succès');
return data.Attributes; // Renvoie l'utilisateur supprimé
} catch (error) {
console.error('Erreur lors de la suppression :', error);
return null;
}
}
Requêtes et filtres
Exemples de requêtes avancées dans DynamoDB :
// Exemples de requêtes avancées avec DynamoDB
const AWS = require('aws-sdk');
AWS.config.update({ region: 'eu-west-3' });
const docClient = new AWS.DynamoDB.DocumentClient();
// 1. Requête de base sur la clé de partition
async function getUsersByStatus(status) {
const params = {
TableName: 'Users',
IndexName: 'UserStatusIndex',
KeyConditionExpression: 'userStatus = :status',
ExpressionAttributeValues: {
':status': status
}
};
try {
const data = await docClient.query(params).promise();
return data.Items;
} catch (error) {
console.error('Erreur lors de la requête :', error);
return [];
}
}
// 2. Requête avec filtre
async function getActiveUsersByDomain(status, emailDomain) {
const params = {
TableName: 'Users',
IndexName: 'UserStatusIndex',
KeyConditionExpression: 'userStatus = :status',
FilterExpression: 'contains(email, :domain)',
ExpressionAttributeValues: {
':status': status,
':domain': emailDomain
}
};
try {
const data = await docClient.query(params).promise();
return data.Items;
} catch (error) {
console.error('Erreur lors de la requête filtrée :', error);
return [];
}
}
// 3. Scan avec filtre (attention aux performances sur les grandes tables)
async function searchUsersByName(nameFragment) {
const params = {
TableName: 'Users',
FilterExpression: 'contains(firstName, :name) OR contains(lastName, :name)',
ExpressionAttributeValues: {
':name': nameFragment
}
};
try {
const data = await docClient.scan(params).promise();
return data.Items;
} catch (error) {
console.error('Erreur lors du scan :', error);
return [];
}
}
// 4. Batch Get - Récupération de plusieurs éléments à la fois
async function getUsersInBatch(userKeys) {
const params = {
RequestItems: {
'Users': {
Keys: userKeys // tableau de clés [{userId: 'id1', email: 'email1'}, ...]
}
}
};
try {
const data = await docClient.batchGet(params).promise();
return data.Responses.Users;
} catch (error) {
console.error('Erreur lors du batch get :', error);
return [];
}
}
Single-Table Design
Une approche avancée pour organiser différents types d'entités dans une seule table DynamoDB :
// Exemple de Single-Table Design dans DynamoDB
// 1. Structure des clés pour différents types d'entités dans une même table
const exampleItems = [
// Utilisateur
{
PK: 'USER#123',
SK: 'PROFILE#123',
Type: 'USER',
Email: 'user@example.com',
Name: 'John Doe',
CreatedAt: '2023-01-15T12:00:00Z'
},
// Adresse de l'utilisateur
{
PK: 'USER#123',
SK: 'ADDRESS#HOME',
Type: 'ADDRESS',
Street: '123 Main St',
City: 'Paris',
Country: 'France',
IsDefault: true
},
// Commande de l'utilisateur
{
PK: 'USER#123',
SK: 'ORDER#456',
Type: 'ORDER',
OrderDate: '2023-02-20T14:30:00Z',
Status: 'DELIVERED',
Total: 129.99,
GSI1PK: 'ORDER#456', // Pour accès direct à la commande
GSI1SK: '2023-02-20' // Pour filtrer par date
},
// Produit de la commande
{
PK: 'ORDER#456',
SK: 'PRODUCT#789',
Type: 'ORDER_ITEM',
ProductName: 'Smartphone XYZ',
Quantity: 1,
Price: 129.99
},
// Information du produit
{
PK: 'PRODUCT#789',
SK: 'METADATA',
Type: 'PRODUCT',
Name: 'Smartphone XYZ',
Category: 'Electronics',
GSI1PK: 'CATEGORY#Electronics', // Pour requêtes par catégorie
GSI1SK: 'PRODUCT#789'
}
];
// 2. Exemples de requêtes efficaces dans ce modèle
// Récupérer toutes les informations d'un utilisateur (profil, adresses, commandes)
const userParams = {
TableName: 'AppData',
KeyConditionExpression: 'PK = :pk',
ExpressionAttributeValues: {
':pk': 'USER#123'
}
};
// Récupérer une commande spécifique avec tous ses produits
const orderParams = {
TableName: 'AppData',
KeyConditionExpression: 'PK = :pk',
ExpressionAttributeValues: {
':pk': 'ORDER#456'
}
};
// Récupérer tous les produits d'une catégorie (utilisant GSI)
const categoryParams = {
TableName: 'AppData',
IndexName: 'GSI1',
KeyConditionExpression: 'GSI1PK = :category',
ExpressionAttributeValues: {
':category': 'CATEGORY#Electronics'
}
};
Fonctionnalités avancées
- Indexes secondaires : Index secondaires globaux (GSI) et locaux (LSI) pour des accès efficaces via des attributs non-clés
- Auto-scaling : Ajustement automatique de la capacité de lecture/écriture en fonction de la charge
- Transactions : Opérations ACID pour garantir l'intégrité des données à travers plusieurs tables
- Time To Live (TTL) : Suppression automatique des items après une date d'expiration
- DynamoDB Streams : Capture des modifications pour le traitement en temps réel avec
Lambda
- DynamoDB Accelerator (DAX) : Cache en mémoire pour réduire la latence à la microseconde
- Tables globales : Réplication multi-régions pour la haute disponibilité et la résilience
- Backup et restauration : Sauvegardes complètes à la demande et continues via AWS Backup
Modèles de tarification
- Mode provisionné : Définir à l'avance les capacités de lecture/écriture, idéal pour les charges prévisibles
- Mode à la demande : Paiement par requête, adapté aux charges variables ou imprévisibles
- Crédits Capacity : Économies réalisées via l'achat de capacités réservées
Bonnes pratiques
- Choisir des clés de partition optimales : Assurer une distribution uniforme des données et éviter les "hot spots"
- Concevoir pour les accès : Structurer les données en fonction des patterns d'accès de l'application
- Utiliser le modèle Single-Table : Regrouper les entités liées dans une même table pour minimiser les requêtes
- Gestion des données volumineuses : Stocker les grands objets dans
S3 avec des références dans DynamoDB
- Comprendre les limites : 400KB par item, quotas de partition, limitations des LSI
- Mettre en cache les résultats : Utiliser DAX ou d'autres systèmes de cache pour réduire les coûts
- Surveiller les métriques : Analyser les patterns d'utilisation via CloudWatch
Cas d'usage
E-commerce
Gérer efficacement les paniers d'achat, les profils utilisateurs et les historiques de commandes, tout en absorbant les pics de trafic lors des périodes de forte activité comme le Black Friday sans dégradation de performance.
Applications mobiles
Stocker les données utilisateurs, les préférences et l'historique des activités des applications mobiles avec une synchronisation en temps réel et une disponibilité hors ligne via AWS AppSync et Amazon Cognito.
Analyse de données d'IoT
Traiter et stocker les flux massifs de données générées par des capteurs et appareils IoT, permettant l'analyse en temps réel et le déclenchement d'alertes ou d'actions automatisées via DynamoDB Streams.
Microservices et applications serverless
S'intègre parfaitement avec AWS Lambda pour créer des architectures serverless qui s'adaptent instantanément à la demande, réduisant à zéro les coûts d'infrastructure en période d'inactivité.
Entreprises qui utilisent DynamoDB
De nombreuses entreprises de premier plan exploitent DynamoDB pour leurs applications critiques :