Performance et scalabilitĂ© de vos requĂȘtes
Module NoSQL - Ăcole d'IngĂ©nieurs
Collection scan complet
1 million de documents
AccĂšs direct
~1000x plus rapide
// Sans index : MongoDB examine TOUS les documents
db.products.find({ sku: "CAF-2024" })
// executionStats.totalDocsExamined: 1,000,000
// executionStats.executionTimeMillis: 3200
// Avec index sur 'sku'
db.products.createIndex({ sku: 1 })
// executionStats.totalDocsExamined: 1
// executionStats.executionTimeMillis: 3
| Type d'Index | Usage | Exemple |
|---|---|---|
| Single Field | RequĂȘtes sur un champ unique | { sku: 1 } |
| Compound | RequĂȘtes sur plusieurs champs | { category: 1, price: -1 } |
| Text | Recherche textuelle | { name: "text" } |
| 2dsphere | RequĂȘtes gĂ©ospatiales | { location: "2dsphere" } |
// Garantit l'unicité des valeurs
db.customers.createIndex(
{ email: 1 },
{ unique: true }
)
// Index seulement les documents qui matchent le filtre
db.orders.createIndex(
{ customerId: 1 },
{ partialFilterExpression: { status: "active" } }
)
3 / 12
// Création d'un index composé
db.orders.createIndex({
status: 1,
customerId: 1,
createdAt: -1
})
find({ status: "pending", customerId: "CLT-10234" })
find({ status: "pending" }).sort({ customerId: 1 })
find({ customerId: "CLT-10234" })
// Analyser l'exĂ©cution d'une requĂȘte
db.products.find({
category: "VĂȘtements",
price: { $gte: 1000 }
}).explain("executionStats")
// Résultat explain() - SANS INDEX
{
"winningPlan": {
"stage": "COLLSCAN" // â Collection Scan
},
"executionStats": {
"totalDocsExamined": 500000,
"totalKeysExamined": 0,
"executionTimeMillis": 2341
}
}
// Activer le profiling pour identifier les requĂȘtes lentes
db.setProfilingLevel(1, { slowms: 100 })
// Analyser les requĂȘtes lentes
db.system.profile.find({
millis: { $gt: 100 }
}).sort({ ts: -1 }).limit(5)
// Pour une boutique en ligne marocaine
// RequĂȘte frĂ©quente : produits par ville et catĂ©gorie
db.products.createIndex({
"warehouse.city": 1,
category: 1,
price: 1
})
// Support pour recherche textuelle en français/arabe
db.products.createIndex(
{ name: "text", description: "text" },
{ default_language: "french" }
)
// L'index contient TOUS les champs nécessaires
db.orders.createIndex({
customerId: 1,
status: 1,
total: 1
})
// Cette requĂȘte est entiĂšrement couverte par l'index
db.orders.find(
{ customerId: "CLT-10234", status: "pending" },
{ total: 1, _id: 0 }
)
6 / 12
// Structure pour stocker les coordonnées
{
"name": "Restaurant La Mamounia",
"location": {
"type": "Point",
"coordinates": [-7.6192, 33.5731] // [longitude, latitude]
},
"city": "Marrakech"
}
// Créer un index 2dsphere
db.restaurants.createIndex({ location: "2dsphere" })
// Trouver les restaurants dans un rayon de 2km
db.restaurants.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [-7.6192, 33.5731]
},
$maxDistance: 2000 // en mĂštres
}
}
})
// Trouver les livreurs disponibles prĂšs d'une commande
db.delivery_drivers.find({
status: "available",
location: {
$near: {
$geometry: orderLocation,
$maxDistance: 5000
}
}
}).limit(3)
7 / 12
// Lister les index d'une collection
db.products.getIndexes()
// Taille des index
db.products.stats().indexSizes
// Statistiques d'utilisation des index (MongoDB 3.2+)
db.products.aggregate([
{ $indexStats: {} }
])
// Résultat
{
"name": "category_1_price_1",
"accesses": {
"ops": 42341, // Nombre d'utilisations
"since": ISODate("2024-01-01")
}
}
// Reconstruire un index (maintenance)
db.products.reIndex()
// Supprimer un index inutilisé
db.products.dropIndex("old_index_name")
Optimisons une plateforme de vente en ligne marocaine avec :
// 1. Recherche produits (le plus fréquent)
db.products.createIndex({
category: 1,
"stock.city": 1,
price: 1
})
// 2. Recherche textuelle multilingue
db.products.createIndex(
{ name: "text", description: "text", tags: "text" },
{
weights: { name: 10, tags: 5, description: 1 },
default_language: "french"
}
)
// 3. Commandes par client
db.orders.createIndex({
customerId: 1,
createdAt: -1
})
// 4. Produits populaires (avec TTL)
db.trending_products.createIndex(
{ lastViewed: 1 },
{ expireAfterSeconds: 604800 } // 7 jours
)
find({ category: "Artisanat", "stock.city": "FĂšs", price: { $lte: 500 } })
â Utilise l'index #1, temps: ~5ms
// MAUVAIS : Un index par champ
db.products.createIndex({ name: 1 })
db.products.createIndex({ category: 1 })
db.products.createIndex({ price: 1 })
db.products.createIndex({ stock: 1 })
// MIEUX : Index composé stratégique
db.products.createIndex({ category: 1, price: 1, stock: 1 })
// ĂVITER : Index sur des champs avec peu de valeurs distinctes
db.users.createIndex({ isActive: 1 }) // Seulement true/false
// MIEUX : Utiliser un index partiel
db.users.createIndex(
{ lastLogin: -1 },
{ partialFilterExpression: { isActive: true } }
)
// RequĂȘte frĂ©quente
db.orders.find().sort({ createdAt: -1, total: 1 })
// Index optimal (respecte les directions)
db.orders.createIndex({ createdAt: -1, total: 1 })
| Action | Commande | Fréquence |
|---|---|---|
| Profiler les requĂȘtes lentes | db.setProfilingLevel(1, { slowms: 100 }) |
Continue |
| Analyser avec explain() | .explain("executionStats") |
Avant production |
| Vérifier l'usage des index | $indexStats |
Mensuel |
| Nettoyer les index inutilisés | dropIndex() |
Trimestriel |
// Script d'audit des performances
db.getMongo().getDBNames().forEach(dbName => {
const db = db.getSiblingDB(dbName)
db.getCollectionNames().forEach(coll => {
const stats = db[coll].stats()
if (stats.size > 1000000) { // Collections > 1MB
print(`${dbName}.${coll}: ${stats.indexSizes}`)
}
})
})
11 / 12
Aggregation Pipeline : Transformez et analysez vos données avec la puissance du framework d'agrégation MongoDB
12 / 12