Migliori Pratiche per Gestire e Ridurre l'Utilizzo dello Spazio Disco di MongoDB

Ottimizza l'utilizzo del disco di MongoDB con questa guida completa alle migliori pratiche. Impara strategie efficaci per compattare collezioni e indici, identificare e rimuovere indici non necessari e sfruttare le funzionalità di compressione di WiredTiger. Scopri come implementare l'archiviazione dei dati, gestire le dimensioni dell'oplog e monitorare proattivamente lo spazio su disco per prevenire interruzioni del sistema e migliorare le prestazioni. Questo articolo fornisce approfondimenti pratici ed esempi concreti per mantenere le tue implementazioni MongoDB snelle ed efficienti.

Migliori Pratiche per Gestire e Ridurre l'Utilizzo dello Spazio Disco di MongoDB

L'utilizzo del disco di MongoDB di solito diventa urgente nel momento peggiore: un job batch impiega più tempo del previsto, le eliminazioni non sembrano liberare spazio, o un membro del replica set inizia ad avvisare che il volume è quasi pieno. La soluzione raramente è un singolo comando magico. Devi sapere se lo spazio è occupato da dati live, indici, spazio riutilizzabile di WiredTiger, oplog, log o backup locali.

L'approccio più sicuro è misurare prima, ridurre ciò che non deve più esistere, e solo allora eseguire manutenzioni più pesanti come la compattazione o la ricostruzione dei membri. Questo ordine ti impedisce di creare un lungo evento di manutenzione che restituisce poco spazio.

Comprendere il Consumo di Spazio Disco di MongoDB

MongoDB utilizza spazio su disco per diversi componenti:

  • File di Dati: Memorizza i documenti BSON effettivi all'interno delle collezioni.
  • File degli Indici: Memorizza gli indici B-tree creati per supportare l'esecuzione efficiente delle query.
  • File di Journal (WiredTiger): Registra le operazioni di scrittura prima che vengano applicate ai file di dati, garantendo la durabilità dei dati. Questi sono pre-allocati.
  • Oplog (Log Operativo): Una collezione capped speciale nei replica set che registra tutte le operazioni di scrittura. Essenziale per la replica.
  • Dati Diagnostici: Log, file di processo mongod e altre informazioni relative al sistema.

Col tempo, a causa di aggiornamenti, eliminazioni e crescita dei documenti (padding), le collezioni e gli indici possono diventare frammentati o contenere spazio allocato non utilizzato, portando a un utilizzo inefficiente del disco. Questo "spazio bianco" non viene immediatamente recuperato dal sistema operativo, anche se il database non ne ha più bisogno per i dati live.

Strategie per Ridurre lo Spazio Disco di MongoDB

1. Compattazione di Collezioni e Indici

Le operazioni di compattazione aiutano a recuperare spazio su disco inutilizzato riscrivendo i file di dati e degli indici in modo più efficiente. Questo può essere particolarmente utile dopo significative eliminazioni o aggiornamenti di dati.

Compattazione delle Collezioni

Con il motore di archiviazione WiredTiger (predefinito da MongoDB 3.2), compact recupera principalmente spazio libero da documenti eliminati e deframmenta le collezioni. Non ricostruisce il file di dati della collezione da zero come faceva l'operazione compact di MMAPv1.

db.runCommand({ compact: "myCollection" })

Considerazioni per compact:

  • Le operazioni compact possono essere intensive in termini di risorse (CPU, I/O) e richiedere molto tempo, specialmente per collezioni di grandi dimensioni. È spesso meglio eseguirle durante le finestre di manutenzione o sui membri secondari di un replica set.
  • I requisiti di disco e il comportamento di blocco variano in base alla versione di MongoDB, al motore di archiviazione e alla forma del deployment. Controlla la documentazione per la tua versione esatta prima di eseguirlo su una collezione di produzione di grandi dimensioni.
  • Per i cluster shardati, esegui compact su ogni shard in modo indipendente.

Ricostruzione degli Indici

Anche gli indici possono diventare frammentati. Ricostruire un indice può recuperare spazio e potenzialmente migliorare le prestazioni delle query.

db.myCollection.reIndex()

Considerazioni su reIndex():

  • Il comportamento di reIndex() è cambiato tra le versioni di MongoDB e può ancora essere dirompente su sistemi occupati. Controlla il manuale per la tua versione, testa in staging e, dove possibile, preferisci il lavoro a rotazione attraverso i membri del replica set.
  • Simile a compact, reIndex() richiede spazio su disco aggiuntivo durante l'operazione.

repairDatabase (Operazione Offline)

Per frammentazione grave o corruzione dei dati, repairDatabase può ricostruire tutti i file di dati. Questa è un'operazione offline e richiede l'arresto dell'istanza mongod.

mongod --repair

Avvertenza: repairDatabase dovrebbe essere usato come ultima risorsa per il recupero dello spazio, poiché è un'operazione distruttiva se non gestita con cura e può richiedere molto tempo. Avere sempre un backup.

2. Ottimizzazione degli Indici

Gli indici sono cruciali per le prestazioni ma possono consumare spazio su disco significativo. Gli indici inutilizzati o ridondanti sono puro overhead.

Identificare e Rimuovere Indici Non Necessari

Rivedi regolarmente i tuoi indici per assicurarti che siano ancora necessari.

  1. Elenca tutti gli indici per una collezione:
    
    

db.myCollection.getIndexes() ``` 2. Monitora l'utilizzo degli indici: Usa $indexStats, i piani di query, la profilazione e la cronologia del carico di lavoro dell'applicazione. Le statistiche delle collezioni mostrano la dimensione dell'indice, ma non provano se un indice è utile. 3. Identifica indici duplicati o ridondanti: Ad esempio, un indice su { a: 1, b: 1 } rende un indice su { a: 1 } ridondante per le query che possono utilizzare l'indice composto. Un indice su { a: 1, b: 1 } è anche coperto da un indice su { a: 1, b: 1, c: 1 } per le query che coinvolgono solo a e b.

Una volta identificato, elimina l'indice inutilizzato:

db.myCollection.dropIndex("indexName")

Consiglio: Testa sempre l'impatto dell'eliminazione di un indice in un ambiente di staging prima di applicarlo in produzione.

Utilizzo di Indici Parziali

Gli indici parziali indicizzano solo i documenti in una collezione che soddisfano una specifica espressione di filtro. Questo riduce il numero di documenti indicizzati, risparmiando spazio su disco e migliorando le prestazioni di scrittura.

db.orders.createIndex(
   { customerId: 1, orderDate: -1 },
   { partialFilterExpression: { status: "active" } }
)

Questo indice includerebbe solo i documenti in cui status è "active", riducendo le sue dimensioni se la maggior parte degli ordini sono storici, cancellati, archiviati o comunque fuori dal percorso caldo. La parte importante non è la parola "active"; è l'abitudine di indicizzare il sottoinsieme che la tua applicazione interroga effettivamente ogni giorno.

Inizia con un Triage dello Spazio Disco, Non con un Comando di Pulizia

Quando lo spazio su disco di MongoDB sta crescendo, il primo errore è passare direttamente a compact, repair o all'eliminazione di vecchi dati. Queste azioni possono aiutare, ma possono anche creare carico, prendere lock in alcune situazioni o nascondere il vero problema per alcune settimane. Inizia rispondendo a tre domande:

  • Quale filesystem si sta riempiendo: il percorso del database, il percorso del journal, il percorso dei log o il volume di backup?
  • I dati live stanno crescendo, o sta crescendo lo spazio allocato ma non utilizzato dopo eliminazioni e aggiornamenti?
  • La crescita proviene da collezioni, indici, oplog, log, dati diagnostici o snapshot?

Un primo passaggio rapido di solito assomiglia a questo:

df -h
du -h --max-depth=1 /var/lib/mongodb | sort -h
du -h --max-depth=1 /var/log/mongodb | sort -h

Poi controlla MongoDB dall'interno della shell:

db.adminCommand({ listDatabases: 1 })
db.getSiblingDB("app").stats()
db.getSiblingDB("app").orders.stats()

storageSize, totalIndexSize e dataSize raccontano storie diverse. Se dataSize sta crescendo, probabilmente hai un problema di ciclo di vita dei dati. Se storageSize è molto più grande di dataSize, potresti avere a che fare con spazio interno riutilizzabile dopo le eliminazioni. Se totalIndexSize è grande rispetto a dataSize, il design degli indici merita attenzione prima di toccare la compattazione.

Capire Cosa MongoDB Può e Non Può Restituire

Con WiredTiger, l'eliminazione di documenti di solito rende lo spazio disponibile per il riutilizzo da parte di MongoDB. Non sempre restituisce immediatamente quello spazio al sistema operativo. Questo comportamento sorprende le persone durante la pulizia di emergenza: eliminano un grande batch, eseguono df -h e non vedono quasi nessun miglioramento.

Ciò non significa che l'eliminazione sia fallita. Significa che MongoDB può spesso riutilizzare quello spazio per future inserzioni e aggiornamenti. Se l'obiettivo è fermare la crescita, eliminare o archiviare vecchi dati potrebbe essere sufficiente. Se l'obiettivo è ridurre il filesystem perché il volume è quasi pieno o l'host viene ridimensionato, potresti aver bisogno di compattazione, risincronizzazione di un membro del replica set o una ricostruzione di tipo dump-and-restore.

Per i sistemi di produzione, di solito separo il lavoro in due binari. Il primo binario è la sicurezza immediata: aggiungere disco, rimuovere l'accumulo ovvio di log, mettere in pausa job batch rischiosi o spostare i backup dal volume del database. Il secondo binario è la riduzione reale: correggere la retention, rimuovere indici inutilizzati e ricostruire l'archiviazione solo dopo aver capito dove sono finiti i byte.

Correggi la Retention dei Dati Prima di Deframmentare Qualsiasi Cosa

Se la tua applicazione conserva per sempre log di richieste, eventi, sessioni, notifiche, record di job o documenti di analisi, l'utilizzo del disco tornerà indipendentemente da quanto attentamente compatti. MongoDB ti offre alcune opzioni pratiche.

Per i dati che scadono su un semplice timestamp, un indice TTL è spesso la risposta più pulita:

db.sessions.createIndex(
  { expiresAt: 1 },
  { expireAfterSeconds: 0 }
)

Questo indice rimuove i documenti dopo la data memorizzata in expiresAt. È utile per sessioni, token temporanei, job di importazione di breve durata o risposte API memorizzate nella cache. Non è un sostituto per le regole di retention aziendali. Il monitor TTL viene eseguito in background, quindi non aspettarti un'eliminazione secondo per secondo e non usare TTL su dati che richiedono un flusso di lavoro di approvazione prima dell'eliminazione.

Per i record aziendali, archivia invece di eliminare ciecamente. Un pattern comune è:

  1. Copiare i documenti più vecchi della finestra di retention in un archivio più economico o in un database di archivio.
  2. Verificare i conteggi e un campione di campi importanti.
  3. Eliminare in piccoli batch dalla collezione primaria.
  4. Osservare il lag di replica e le metriche del disco mentre il job è in esecuzione.

I piccoli batch contano. Una singola enorme eliminazione può creare pressione sulla replica, riempire i log e rendere più difficile il rollback se qualcuno si accorge che il filtro era sbagliato. Un job batch più sicuro potrebbe eliminare poche migliaia di documenti alla volta, dormire brevemente e registrare i progressi tramite _id o timestamp.

while (true) {
  const result = db.events.deleteMany({
    createdAt: { $lt: ISODate("2025-01-01T00:00:00Z") },
    archived: true
  });

  print(`deleted ${result.deletedCount}`);
  if (result.deletedCount === 0) break;
  sleep(500);
}

In uno script di produzione reale, aggiungi un pattern di limite invece di deleteMany sull'intero intervallo, registra ogni batch e fermati automaticamente se il lag di replica o l'I/O del disco superano la tua soglia.

Fai Attenzione ai Consigli sugli Indici Che Sembrano Troppo Semplici

Eliminare gli indici inutilizzati è uno dei modi migliori per ridurre lo spazio su disco di MongoDB, ma "inutilizzato" ha bisogno di contesto. Un indice può sembrare inutilizzato durante una settimana tranquilla ed essere ancora critico per report di fine mese, riconciliazioni in background o un raro flusso di lavoro di supporto clienti.

Usa $indexStats per vedere i pattern di accesso:

db.orders.aggregate([{ $indexStats: {} }])

Poi confronta il risultato con il codice dell'applicazione, i job schedulati, le dashboard e le query di supporto. Se un indice non è stato utilizzato dall'ultimo riavvio, questo è un segnale, non un verdetto. Prima di eliminarlo, controlla se il server è stato riavviato di recente e se il campione di carico di lavoro include i job che contano.

Fai anche attenzione agli indici composti sovrapposti. Se hai questi:

{ customerId: 1 }
{ customerId: 1, createdAt: -1 }
{ customerId: 1, createdAt: -1, status: 1 }

potresti essere in grado di rimuoverne uno, ma solo dopo aver controllato l'ordinamento, i filtri delle query e se l'indice più corto supporta un pattern di accesso diverso. MongoDB può usare il prefisso sinistro di un indice composto, ma ciò non significa che l'indice più grande sia sempre un sostituto gratuito. Gli indici più grandi costano più memoria e I/O di scrittura, quindi tieni quello che si adatta al carico di lavoro, non quello che sembra più completo.

Preferisci la Risincronizzazione per Grandi Operazioni di Riduzione sui Replica Set

Per un grande replica set, il modo più pulito per recuperare spazio su disco del sistema operativo è spesso ricostruire un secondario alla volta. L'idea di base è:

  1. Conferma di avere una replica sana e backup aggiornati.
  2. Rimuovi o ferma un secondario.
  3. Cancella la sua directory dati locale.
  4. Lascia che si risincroni dal primario o da un altro membro sano.
  5. Ripeti per il successivo secondario.
  6. Abbassa il primario durante una finestra di manutenzione e ricostruisci il vecchio primario per ultimo.

Questo approccio è più lento dell'esecuzione di un comando, ma è più facile da ragionare perché ogni membro ricostruito scrive nuovi file di archiviazione basati sui dati correnti. Evita anche di cercare di compattare ogni collezione sotto il traffico di produzione. Non è gratuito: la sincronizzazione iniziale può essere pesante per rete e disco, e hai bisogno di abbastanza membri rimanenti per mantenere il replica set al sicuro mentre un membro viene ricostruito.

Per un server MongoDB standalone, non hai quel lusso. In tal caso, pianifica una finestra di manutenzione, fai un backup testato e considera mongodump/mongorestore o la migrazione a livello di filesystem su un volume nuovo. Non scegliere mongod --repair solo perché vuoi una directory dati più piccola. Tratta la riparazione come uno strumento di recupero, non come una manutenzione di routine.

Controlla Anche Oplog, Log e Backup

Non tutta la pressione sul disco di MongoDB proviene dalle collezioni. Nei replica set, l'oplog è una collezione capped, quindi non dovrebbe crescere per sempre, ma la sua dimensione configurata conta comunque. Se è troppo piccolo, i secondari possono cadere durante la manutenzione. Se è molto più grande del necessario su un disco piccolo, potrebbe sprecare spazio. Rivedilo deliberatamente:

db.getSiblingDB("local").oplog.rs.stats()

Anche i log di MongoDB possono riempire un disco quando il logging di query lente, la verbosità di debug o un ciclo di errore dell'applicazione diventano rumorosi. Usa la rotazione dei log e tieni i log del database lontani dallo stesso volume piccolo che memorizza i dati, quando possibile.

I backup sono un'altra sorpresa comune. I team a volte eseguono mongodump sullo stesso host perché è comodo, poi si chiedono perché gli avvisi di disco scattano durante la finestra di backup. Un backup memorizzato sullo stesso filesystem non è molto un backup e può spingere MongoDB in un'interruzione peggiore durante un'operazione già rischiosa. Trasmetti i backup in streaming a un object storage, un server di backup o un volume montato separato.

Un Runbook Pratico per un Disco MongoDB Pieno

Se il disco è già oltre il 90 percento, rallenta e lavora in questo ordine:

  1. Conferma se MongoDB sta ancora accettando scritture e se il replica set è sano.
  2. Aggiungi capacità disco temporanea se la piattaforma lo consente. Questo è spesso più sicuro dell'eliminazione di emergenza.
  3. Sposta o ruota i log sovradimensionati e i file di backup locali.
  4. Ferma i job batch non essenziali che stanno scrivendo pesantemente.
  5. Identifica le collezioni e gli indici più grandi con db.stats() e stats() delle collezioni.
  6. Archivia o elimina solo i dati con una chiara regola di retention.
  7. Pianifica compattazione, risincronizzazione o ripristino dopo che il sistema è stabile.

La migliore soluzione a lungo termine è noiosa: regole di retention, revisioni degli indici, avvisi sul disco e procedure di ricostruzione testate. MongoDB è a suo agio nel riutilizzare lo spazio libero interno, ma gli operatori devono ancora decidere quali dati meritano di vivere su storage veloce e cosa può essere spostato altrove.