Backup quotidiano per un milione di database.

Le offerte di OVH prevedono uno o più database SQL condivisi e database privati opzionali aggiuntivi, gli "SQL Privati". Queste due tipologie, per le quali viene eseguito un backup quotidiano, includono più di un milione di database. Come ripartire quindi i backup nel tempo perevitare picchi di carico? Quale metodologia utilizzare per eseguirli? Scopriamolo insieme al team OVH dedicato a questo servizio.

Quando effettuiamo i backup?

L'esecuzione del backup di un database, o di qualsiasi altro dato, comporta inevitabilmente un aumento del consumo di risorse, in particolare di Input/Output (I/O) sul disco e di rete in quanto i dati vengono trasferiti da una macchina all'altra.

Per questo motivo i sysadministrator decidono di effettuare i backup durante la notte, in modo da evitare l'impatto sul servizio coinvolto.

In OVH abbiamo adottato questo approccio intuitivo sin dagli esordi. Per la maggior parte dei nostri utenti di hosting Web, infatti, il traffico minore viene registrato tra l'01:00 e le 07:00 del mattino. Di conseguenza i backup dei database venivano pianificati durante questa fascia oraria.

Questo approccio fin da subito ha rivelato qualche limite. Nella fascia oraria 01:00-07:00 i nostri indicatori di sistema rilevavano dei picchi che davano origine a colli di bottiglia e le esportazioni dei database SQL (dump) erano rallentate, superando l'intervallo definito e intaccando la qualità del servizio. In questo modo la piattaforma doveva riuscire ad assorbire il carico generato dalla realizzazione dei backup e allo stesso tempo, a partire dalle prime ore del mattino, essere in grado di gestire l’affluenza crescente dei visitatori sui siti ospitati… Questa situazione era insostenibile.

Abbiamo allora deciso di adottare un altro metodo: ripartire le operazioni di backup nell'arco della giornata, in modo da mantenere prestazioni ottimali sugli hosting Web a qualunque ora del giorno e della notte.

Algoritmo di ripartizione

Per una distribuzione uniforme dei backup nel corso della giornata, abbiamo associato un database a un orario preciso.

Ci siamo resi conto che non era possibile effettuare una ripartizione (sharding) solo sulla base del nome del database: non era possibile calcolare il numero del database "wordpress", "prestashop", "test" e "demo". Questo avrebbe comportato un riavvio del backup di tutti i database "wordpress" alla stessa ora, con risultati non molto efficaci.

Anche in caso di nome unico, non era possibile effettuare lo sharding basato esclusivamente sul nome dell'istanza che ospitava il database. Applicando la legge dei grandi numeri è risultato infatti che, con molte meno istanze di database, la ripartizione dei backup in base all'identità delle istanze sarebbe stata meno uniforme di quella effettuata considerando l'identità degli stessi database. Abbiamo deciso di combinare queste due informazioni e utilizzare per il nostro sharding la concatenazione del nome del database e dell'istanza.

A questo punto non ci restava che trovare una formula in grado di associare ai caratteri un orario preciso, ovvero un numero compreso tra 1 e 1.440 (i minuti di una giornata). Visto che 1.440 è divisibile per 16 (cogliamo l'occasione per ringraziare i Babilonesi per aver scelto la base 60 - facilmente divisibile - per il calcolo dei minuti e dei secondi), abbiamo generato il famoso numero utilizzando una semplice funzione crittografica di hash (digest) esadecimale (sha512):

int(hashlib.sha512(instance_name + "." + db_name).hexdigest()[:90], 16) % 1440

I backup sono distribuiti nel tempo in maniera uniforme, come dimostra il grafico.

Numero di backup pianificati a intervalli di 5 minuti.

In che modo effettuiamo i backup?

I backup dei database possono essere eseguiti in due modi: esportando i dati in formato SQL o salvandoli in un formato leggibile dal sistema di gestione dei database (ad esempio SGBD o MySQL). Ogni metodo presenta vantaggi, inconvenienti e casi utilizzo. Abbiamo optato per una combinazione delle due metodologie, in modo da usufruire dei vantaggi di entrambe.

I dump

L’idea alla base del metodo del dump è generare un file di testo contenente i comandi SQL che, una volta eseguiti, ricreano il database ex novo, riportandolo allo stato in cui si trovava al momento dell'esportazione. Quali sono i principali vantaggi? Permette la migrazione dei dati verso altre versioni del SGBD o altri motori e consente la modifica manuale del file prima di una nuova importazione. Questa operazione potrebbe rivelarsi utile per clonare un ambiente di produzione per realizzare i test.

I nostri clienti possono usufruire di questi vantaggi e avere libero accesso ai dump, disponibili e scaricabili direttamente dallo Spazio Cliente OVH. Come detto precedentemente, i dump vengono effettuati tutti i giorni alla stessa ora e conservati per un mese.

Affinché un dump sia coerente (cioè rifletta esattamente lo stato del database in un determinato istante), si possono utilizzare due metodi:
Bloccare le tabelle

Il database viene mantenuto in un certo stato e le richieste, messe in attesa, si accumulano. Una volta che il dump è terminato, la coda di attesa si sblocca e vengono eseguite le richieste .

Vantaggi: il metodo è compatibile con tutti i motori (tra cui i principali, MyISAM e InnoDB) e il dump è sempre coerente.

Inconvenienti: le tabelle sono bloccate e il database (e quindi il sito Web) non disponibile fino alla fine del dump. Nella maggior parte dei casi questa situazione non costituisce un problema, ma può diventarlo quando il dump è lungo e le connessioni numerose, in quanto potrebbe essere raggiunto il numero massimo di connessioni simultanee autorizzate. Aumentare la soglia non servirebbe a risolvere il problema, ma solo a posticiparlo. Inoltre, limitare il numero di connessioni serve a proteggere i server dei clienti: una connessione richiede RAM e oltre 200 connessioni simultanee (il massimo consentito per le istanze SQL Privato) e la RAM utilizzata dalle connessioni in corso potrebbe provocare problemi di Out Of Memory sulla tua istanza.

Utilizzare una transazione

Vantaggi: non causa blocchi e il dump è totalmente trasparente e coerente.

Inconvenienti: può essere utilizzato solo con un motore che gestisce le transazioni (InnoDB) perché la presenza anche di una sola tabella in MyISAM renderebbe il dump incoerente. Inoltre, anche con InnoDB, il dump sarà incoerente se durante l'operazione viene effettuato un ALTER, CREATE, DROP, RENAME o TRUNCATE TABL.
Alla luce di tutto ciò, abbiamo combinato i due metodi: se il database è InnoDB al 100%, utilizziamo una transazione. In caso contrario ricorriamo al blocco delle tabelle.

Per questo motivo consigliamo di trasformare tutte le tabelle in formato InnoDB, incluso e attivo su tutte le distribuzioni fornite da MySQL AB dalla versione 4, e diventato il motore predefinito a partire da MySQL 5.5.5 (consulta la guida)!

Backup in formato SGBD

Come abbiamo visto, effettuare il dump di un database comporta molti vantaggi rispetto a un backup "classico". Il metodo presenta però un inconveniente: la durata di ripristino dipende dalla dimensione del dump.

Il volume dei dump su base quotidianaci ha permesso di calcolare statistiche precise circa la durata delle esportazioni dei database e delle operazioni di ripristino. Di seguito riportiamo il risultato delle regressioni lineari, ottenuto sulla base dei tempi di risposta di oltre 40.000 export di database 100% InnoDB, scelta che permette di non considerare il tempo di attesa prima dello sblocco del database.

$ ./lr.py list
y=0.142865163057*x1+4.65205077853
R2=0.792955

Tempi di dump in base alla dimensione del database, in MB.

R2 è il coefficiente di correlazione. Il valore di 0.8 indica che la correlazione è forte, determinare i tempi di un dump in base alla dimensione del database ha senso, i due valori sono strettamente collegati.

Ci siamo chiesti se nella determinazione dei tempi di dump entravano in gioco altri parametri. Abbiamo quindi ripetuto l'esercizio con più variabili: numero di row, dimensione media dei record…, il calcolo più pertinente consisteva nel considerare, oltre alla dimensione del database, il numero di record (row):

$ ./lr.py list
y=2.62412392084*x1+0.130888461045*x2+4.60953501036
R2=0.800904

Tempi di dump in base al numero di record (in milioni) e alla dimensione del database (in MB)

Forti di questi insegnamenti, abbiamo adattato la nostra strategia per ridurre al minimo il tempo di ripristino dei database in caso di incidente sui nostri sistemi, perché sappiamo bene che un database non disponibile significa - nella maggioranza dei casi - che il sito Web interessato non è raggiungibile, situazione critica per l'utente. Per questo, quando la dimensione di un'istanza che ospita database è superiore ai 4 GB (corrispondente a una durata media di ripristino di 10 minuti), raddoppiamo sistematicamente i dump dei backup in formato SGBD. Il vantaggio di questa operazione è offrire un ripristino molto più rapido in caso di problemi sul cluster di storage. Questi backup, realizzati per tutte le istanze condivise che ospitano database e, caso per caso, per le istanze SQL Privato superiori ai 4 GB, non vengono messi a disposizione degli utenti ma servono solo in caso di problemi su un cluster di storage. L’utente ha accesso solo ai propri dump.

Per effettuare backup in formato SGBD e semplificarne il ripristino utilizziamo, per MySQL e MariaDB, il tool XtraBackup in modalità full-backup e non in modalità incrementale. XtraBackup è uno strumento open source di Percona e permette di realizzare backup coerenti e senza blocco delle tabelle, a prescindere dal motore utilizzato (MyISAM, InnoDB…). Si noti che i backup con XtraBackup non vengono effettuati su semplici database, ma su istanze complete. Per PostgreSQL, utilizziamo l’utility pg_basebackup.

Verifica delle tabelle e esigenze di RAM

Prima di effettuare un dump, quotidiano o on demand, verifichiamo lo stato delle tabelle e, se necessario, le ripariamo. Per eseguire questa operazione sulle istanze MySQL e MariaDB utilizziamo il comando mysqlcheck. Questo problema si verifica sempre più di rado: le ultime versioni di MySQL e MariaDB gestiscono sempre meglio la scrittura di operazioni interrotte a causa di crash, e il problema è completamente risolto con PostgreSQL.

La correzione di una tabella e, soprattutto il suo ripristino, può richiedere molta più RAM di quella disponibile su un'istanza. Ecco perché, per l'intera durata del mysqlcheck e del dump, aumentiamo temporaneamente la RAM dell'istanza, aggiungendo 4 GB. Se i database sono sufficientemente voluminosi, l'aggiunta di RAM è visibile nello Spazio Cliente OVH:

Nel caso di dump che durano meno di un minuto, l'aggiunta della RAM può passare inosservata tra due misurazioni. In questo caso, non comparirà nei grafici dello Spazio Cliente.

Questo picco di memoria disponibile, infatti, appiattisce il grafico rendendo quasi illeggibile la curva di utilizzo della memoria. Cliccando su "Maximum RAM limit" nella parte bassa del grafico, è possibile visualizzare solo la curva di utilizzo della memoria.

Salvataggio dei backup

Un backup ha senso solo se salvato su una terza piattaforma. Conserviamo inoltre i dump sulla piattaforma Public Cloud Storage, totalmente separata dalle nostre piattaforme di gestione di database del servizio di hosting Web. Per ogni backup vengono effettuate tre repliche sincrone, in tre siti diversi (Gravelines, Roubaix e Strasburgo) e conservate per un mese.

I backup XtraBackup e pg_basebackup dei Private SQL della piattaforma di Parigi (hosting Web creati prima di luglio 2016) sono salvati su un cluster Ceph diverso da quello utilizzato per salvare i dati di produzione. I backup della piattaforma di Gravelines (hosting Web creati dopo luglio 2016) sono invece conservati sui dischi locali (e i dati di produzione su un cluster Ceph).