Un index TTL (Time-To-Live) est un index mono-champ spécial qu'utilise MongoDB pour supprimer automatiquement les documents une fois leur délai d'expiration atteint. Cette rubrique explique le fonctionnement des index TTL, leur création et leur gestion, leurs limites, les problèmes courants avec leurs solutions, ainsi que les bonnes pratiques pour utiliser les index TTL dans ApsaraDB for MongoDB.
Présentation
Un index TTL (Time-To-Live) est un index mono-champ spécial qui permet à MongoDB de supprimer automatiquement les documents après une durée de conservation définie. Les index TTL conviennent parfaitement à la gestion des données dont le cycle de vie est bien défini, comme les journaux d'événements, les informations de session, les caches temporaires et les codes de vérification. En utilisant correctement les index TTL, vous pouvez maîtriser efficacement le volume de données d'une collection et éviter une croissance incontrôlée de l'espace de stockage.
Toutefois, une utilisation inappropriée des index TTL en production peut entraîner divers problèmes, tels que des pics périodiques d'utilisation du processeur, un retard dans la suppression des données expirées ou une augmentation continue de l'espace disque occupé. Cette rubrique présente le fonctionnement des index TTL, leur mise en œuvre, les problèmes fréquents et les bonnes pratiques afin de vous aider à les utiliser correctement dans ApsaraDB for MongoDB.
Fonctionnement des index TTL
Mécanisme de base
Au démarrage d'un processus mongod, un thread d'arrière-plan nommé TTLMonitor se crée. Par défaut, ce thread exécute un cycle de nettoyage TTL toutes les 60 secondes. Chaque cycle effectue les opérations suivantes :
Collecte tous les index TTL présents dans la base de données actuelle.
Génère un plan d'exécution pour chaque index TTL, l'un après l'autre, puis nettoie les données.
Supprime les documents dont la valeur du champ indexé, ajoutée au nombre de secondes défini par
expireAfterSeconds, est antérieure à l'heure actuelle.
Le seuil d'expiration se calcule comme suit : on additionne la valeur de date du champ indexé et le nombre de secondes spécifié par expireAfterSeconds. Si le résultat est antérieur à l'heure actuelle, le document est considéré comme expiré.
Comportement dans les ensembles de réplicas
Dans un ensemble de réplicas, le thread d'arrière-plan TTL n'effectue les opérations de suppression que sur le nœud Primary. Les threads TTL des nœuds Secondary restent inactifs et synchronisent les suppressions en répliquant l'oplog depuis le nœud Primary. Cela signifie que les suppressions TTL génèrent des entrées supplémentaires dans l'oplog et peuvent affecter le délai de réplication de l'ensemble de réplicas.
Différences entre versions : optimisation des suppressions par lots (MongoDB 7.0+)
À partir de MongoDB 7.0 (une fonctionnalité initialement introduite dans la version de développement 6,1), la suppression TTL utilise un mécanisme de « suppression équitable » qui répartit le temps de suppression entre chaque index TTL par tranches horaires. Cela empêche les données expirées de certaines collections de ne jamais être nettoyées. Les principales améliorations sont les suivantes :
ttlMonitorBatchDeletes : active le mode de suppression par lots. Activé par défaut. Lorsqu'il est activé, les suppressions TTL sont réparties plus équitablement entre les collections.
ttlIndexDeleteTargetTimeMS : limite de temps maximale par cycle de suppression pour chaque index TTL. Valeur par défaut : 1 000 ms.
ttlIndexDeleteTargetDocs : nombre maximal de documents supprimés par cycle pour chaque index TTL. Valeur par défaut : 50 000.
ttlMonitorSubPassTargetSecs : limite de temps maximale pour un sous-cycle parcourant les index TTL. Valeur par défaut : 60 secondes.
Ces paramètres apparaissent sous forme d'étape BATCHED_DELETE dans le plan d'exécution et peuvent être consultés à l'aide de la commande explain.
Créer et gérer des index TTL
Créer un index TTL
Utilisez la méthode createIndex pour créer un index TTL sur un champ de type Date.
Exemple 1 : créez un index TTL afin que les documents expirent 3 600 secondes après la valeur du champ lastModifiedDate.
db.eventlog.createIndex(
{ "lastModifiedDate": 1 },
{ expireAfterSeconds: 3600 }
)
Exemple 2 : expirez à un instant précis. Définissez expireAfterSeconds sur 0 afin que seule la valeur du champ détermine l'heure d'expiration.
db.sessions.createIndex(
{ "expireAt": 1 },
{ expireAfterSeconds: 0 }
)
Modifier le délai d'expiration d'un index TTL
Utilisez la commande collMod pour modifier la valeur expireAfterSeconds d'un index TTL existant sans reconstruire l'index.
db.runCommand({
"collMod": "log_events",
"index": {
"keyPattern": { "createdAt": 1 },
"expireAfterSeconds": 600
}
})
Convertir un index standard en index TTL (MongoDB 6.0+)
À partir de MongoDB 6.0, vous pouvez utiliser la commande collMod pour convertir un index standard mono-champ existant en index TTL, sans avoir à le supprimer ni à le reconstruire au préalable.
db.runCommand({
"collMod": "tickets",
"index": {
"keyPattern": { "lastModifiedDate": 1 },
"expireAfterSeconds": 100
}
})
Surveiller l'état TTL
Surveillez les opérations TTL à l'aide des commandes suivantes.
// View the total number of documents deleted by TTL
db.serverStatus().metrics.ttl.deletedDocuments
// View the number of TTL thread passes
db.serverStatus().metrics.ttl.passes
// View the TTL scan interval (default: 60 seconds)
db.runCommand({ getParameter: 1, ttlMonitorSleepSecs: 1 })
// View the TTL delete operations that are currently running
db.currentOp()
Limites des index TTL
Avant d'utiliser un index TTL, prenez connaissance des limites importantes suivantes :
Les index TTL ne prennent en charge que les index mono-champ. Les index composés ne peuvent pas utiliser la fonctionnalité TTL. Même si vous définissez
expireAfterSeconds, cette valeur est ignorée.Le champ _id ne prend pas en charge les index TTL.
Le champ indexé doit être de type BSON Date. Si la valeur du champ n'est pas de type Date (par exemple, un entier représentant un horodatage UNIX ou une chaîne de caractères), les documents ne sont pas supprimés automatiquement.
Les documents ne contenant pas le champ indexé n'expirent pas.
Si le champ est un tableau, la date la plus ancienne contenue dans le tableau sert à calculer l'heure d'expiration.
**Vous ne pouvez pas modifier un index TTL existant avec
createIndex**; vous devez utiliser la commandecollMod.**La valeur
expireAfterSecondsdoit être comprise entre 0 et 2 147 483 647** et ne doit pas être définie surNaN. Dans le cas contraire, un comportement inattendu et une perte de données peuvent survenir.
Problèmes courants et solutions
Problème 1 : Pics périodiques d'utilisation du processeur causés par les suppressions TTL
Symptôme
L'utilisation du processeur de l'instance affiche des pics nets à intervalles réguliers. En exécutant db.currentOp(), vous constatez que le thread TTL effectue un grand nombre d'opérations de suppression.
Cause
Lorsque l'application insère une grande quantité de données simultanément et que les valeurs du champ TTL sont identiques ou très proches, ces documents expirent dans la même fenêtre temporelle. Le thread TTL doit alors supprimer un grand nombre de documents en une seule analyse, ce qui exerce une pression importante sur le processeur et les E/S.
Solution
Étaler les heures d'expiration : lors de l'insertion des données, ajoutez un décalage aléatoire au champ TTL afin de répartir les expirations sur une plage horaire et d'éviter les suppressions massives. Par exemple, pour des données expirant dans 24 heures, ajoutez ou soustrayez un décalage aléatoire de quelques minutes à l'heure d'expiration.
Migrer vers MongoDB 7.0 ou version ultérieure : utilisez les mécanismes de suppression par lots et de suppression équitable pour lisser la charge liée aux suppressions.
Choisir une spécification d'instance adaptée : assurez-vous que l'instance dispose de suffisamment de ressources processeur et mémoire pour gérer la charge supplémentaire générée par les suppressions TTL.
Exemple : ajouter un décalage d'expiration aléatoire aux données insérées.
// Add a random offset of 0 to 600 seconds on top of 24 hours
var randomOffset = Math.floor(Math.random() * 600);
var expireTime = new Date(Date.now() + (86400 + randomOffset) * 1000);
db.logs.insertOne({
data: "log content",
createdAt: expireTime
})
Problème 2 : Les données ne sont pas supprimées après expiration
Symptôme
Les documents ont dépassé leur heure d'expiration prévue mais existent toujours dans la collection.
Étapes de dépannage
-
Vérifier le type de champ : contrôlez si la valeur du champ indexé TTL est bien de type BSON Date. Une erreur fréquente consiste à utiliser un entier d'horodatage UNIX au lieu d'un objet Date.
// Check the field type db.collection.findOne({}, { createdAt: 1 }) // Correct: ISODate("2024-01-01T00:00:00Z") // Wrong: 1704067200 (integer timestamp) Vérifier l'existence du champ : assurez-vous que les documents contiennent bien le champ indexé TTL. Les documents ne contenant pas ce champ ne sont pas supprimés automatiquement.
-
Contrôler la définition de l'index : vérifiez que l'index inclut la propriété
expireAfterSeconds.db.collection.getIndexes() Vérifier le sens de l'index : assurez-vous que l'index TTL est en ordre croissant (la valeur est
1). Un index décroissant peut empêcher le bon fonctionnement de TTL. Si le sens de l'index est incorrect, supprimez-le et recréez-le.-
Vérifier si TTL Monitor est activé : assurez-vous que le paramètre
ttlMonitorEnabledest défini surtrue.db.adminCommand({ getParameter: 1, ttlMonitorEnabled: 1 })
Problème 3 : La suppression TTL ne suit pas le rythme d'insertion des données
Symptôme
Le volume de données de la collection augmente continuellement et l'utilisation du disque progresse, malgré la configuration d'un index TTL.
Cause
Le thread TTL est mono-thread et s'exécute toutes les 60 secondes. Lorsque le taux d'insertion des données dépasse largement le taux de nettoyage TTL, les données expirées s'accumulent. De plus, si plusieurs index TTL existent sur une instance, ils sont traités de manière séquentielle, ce qui réduit encore l'efficacité du nettoyage.
Solution
-
Ajuster la fréquence d'analyse : réduisez l'intervalle d'analyse en définissant le paramètre
ttlMonitorSleepSecs. Notez qu'une fréquence plus élevée accroît la charge système.// Set the scan interval to 10 seconds db.adminCommand({ setParameter: 1, ttlMonitorSleepSecs: 10 }) Nettoyer côté application : pour les scénarios avec un volume d'écriture particulièrement élevé, implémentez une suppression par lots planifiée dans votre application plutôt que de compter exclusivement sur les index TTL.
Envisager des collections partitionnées par temps : pour les scénarios d'écriture extrêmement intenses, partitionnez les données en collections selon le temps et supprimez les collections entières une fois expirées. Cette approche est bien plus efficace que la suppression document par document via un index TTL.
Problème 4 : L'espace disque n'est pas libéré après suppression
Symptôme
TTL a supprimé un grand nombre de documents, mais l'utilisation du disque n'a pas diminué de manière significative.
Solution
MongoDB utilise le moteur de stockage WiredTiger. Lorsque des documents sont supprimés, l'espace est marqué comme réutilisable, mais n'est pas immédiatement restitué au système d'exploitation. Pour récupérer immédiatement l'espace disque, exécutez la commande compact ou reconstruisez les fichiers de données via une synchronisation initiale. Nous vous recommandons d'exécuter compact pendant les heures creuses.
Bonnes pratiques
1. Garantir des types de données corrects
Les index TTL ne fonctionnent que sur les champs de type BSON Date. Nous vous recommandons d'imposer le type de date correct pour les champs TTL côté application ou en utilisant la fonctionnalité de validation de schéma de MongoDB.
db.createCollection("sessions", {
validator: {
$jsonSchema: {
properties: {
expireAt: { bsonType: "date", description: "The TTL field must be of the Date type" }
},
required: ["expireAt"]
}
}
})
2. Étaler les heures d'expiration pour éviter les suppressions massives
Il s'agit de la méthode la plus efficace pour prévenir les pics d'utilisation du processeur causés par les suppressions TTL. Lors de l'insertion des données, ajoutez un décalage aléatoire à l'heure d'expiration afin qu'un lot de données expire sur une plage de temps. Ainsi, le thread TTL ne supprime qu'un petit nombre de documents par analyse, ce qui réduit considérablement l'impact sur les performances du système.
Pour les données avec une longue période de rétention (24 heures ou plus), nous vous recommandons d'ajouter un décalage aléatoire de 0 à 10 minutes à l'heure d'expiration.
3. Ajuster expireAfterSeconds avec prudence
Réduire la valeur expireAfterSeconds d'un index TTL existant entraîne l'expiration immédiate de nombreux documents, ce qui peut déclencher des opérations de suppression à grande échelle et affecter sérieusement les performances de l'instance. Nous vous recommandons les actions suivantes :
Estimez le nombre de documents qui expireront immédiatement après l'ajustement.
Effectuez la modification pendant les heures creuses.
Si le nombre de documents expirés est important, supprimez d'abord une partie des données par lots à l'aide d'un script, puis modifiez
expireAfterSeconds.
4. Considérations lors de la création d'un nouvel index TTL
La création d'un index TTL sur une collection contenant déjà un grand nombre de documents répondant aux conditions d'expiration peut déclencher des suppressions massives immédiatement après la construction de l'index. Nous vous recommandons les actions suivantes :
Créez l'index TTL pendant les heures creuses.
Nettoyez d'abord les données historiques expirées, puis créez l'index TTL.
Construisez l'index en arrière-plan pour éviter d'affecter les activités métier normales.
5. Ne pas désactiver TTL Monitor pendant une longue période
Bien qu'il soit possible de désactiver temporairement la fonctionnalité TTL en définissant le paramètre ttlMonitorEnabled, nous ne recommandons pas de le laisser désactivé longtemps en production. Le grand nombre de documents expirés accumulés pendant la période de désactivation sera supprimé en bloc lors de la réactivation, ce qui peut provoquer de graves problèmes de performance.
Après avoir temporairement désactivé TTL Monitor, réactivez-le rapidement pour éviter l'accumulation de données expirées.
6. Évaluer l'impact de TTL sur l'oplog
Chaque document supprimé par TTL génère une entrée dans l'oplog. Lorsque le volume de suppressions est important, le volume d'écriture de l'oplog augmente considérablement, ce qui peut affecter le délai de réplication de l'ensemble de réplicas. Nous vous recommandons d'évaluer l'impact de la manière suivante :
Surveillez
metrics.ttl.deletedDocumentspour suivre le volume des suppressions TTL.Surveillez le délai de réplication de l'ensemble de réplicas afin de vous assurer que les nœuds Secondary peuvent gérer l'oplog supplémentaire généré par TTL.
Augmentez la taille de l'oplog si nécessaire pour éviter son débordement causé par les suppressions TTL.
7. Alternatives pour les scénarios à fort volume d'écriture
Les index TTL conviennent aux scénarios avec un volume d'écriture modéré. Pour les scénarios avec un volume d'écriture extrêmement élevé, envisagez les alternatives suivantes :
Collections partitionnées par temps : partitionnez la collection selon une dimension temporelle (par jour ou par semaine) et supprimez la collection entière une fois expirée. C'est la méthode la plus efficace pour expirer les données.
Collections de séries temporelles : MongoDB 5.0+ prend en charge les collections de séries temporelles, qui incluent une expiration des données intégrée et suppriment les données en masse par compartiment. Cette approche est bien plus efficace que la suppression document par document dans une collection standard.
Nettoyage planifié côté application : utilisez des tâches planifiées pour effectuer des suppressions par lots pendant les heures creuses, ce qui vous offre un contrôle plus précis sur le rythme et le moment des suppressions.
Fonctionnalités TTL par version
|
Version |
Fonctionnalités |
|
6.0+ |
Prend en charge la conversion d'un index standard mono-champ en index TTL à l'aide de |
|
7.0+ |
Introduit le mécanisme de suppression par lots (BATCHED_DELETE), qui améliore l'efficacité des suppressions ; met en œuvre la suppression équitable pour empêcher certaines collections d'être « affamées » ; prend en charge les index TTL partiels pour les collections de séries temporelles et une expression |
Référence des paramètres TTL
|
Paramètre |
Description |
Valeur par défaut |
|
|
L'intervalle d'analyse du thread TTL. |
60 secondes |
|
|
L'interrupteur de la fonctionnalité TTL. |
true |
|
|
Le mode de suppression par lots (7.0+). |
true |
|
|
La limite de temps maximale par cycle de suppression (7.0+). |
1 000 ms |
|
|
Le nombre maximal de documents supprimés par cycle (7.0+). |
50 000 |
|
|
La limite de temps maximale pour un sous-cycle (7.0+). |
60 secondes |
Résumé
Les index TTL constituent un mécanisme puissant et pratique d'expiration automatique des données dans MongoDB, permettant de gérer efficacement les données dont le cycle de vie est défini. En production, configurez-les en fonction de votre scénario métier afin d'éviter les problèmes de performance. Voici les points clés à retenir :
Assurez-vous que le champ TTL est de type BSON Date. C'est la condition préalable au bon fonctionnement des index TTL.
L'étalement des heures d'expiration est la méthode la plus efficace pour éviter les pics d'utilisation du processeur.
Surveillez en continu l'état TTL (volume de suppressions, cycles d'analyse, délai de réplication de l'ensemble de réplicas).
Pour les scénarios à fort volume d'écriture, envisagez des alternatives telles que les collections partitionnées par temps ou les collections de séries temporelles.
La migration vers une version plus récente (comme la 7,0+) offre de meilleures performances et une meilleure équité pour les suppressions TTL.