Mysql
 sql >> Database >  >> RDS >> Mysql

Come fallire o arrestare in modo anomalo le istanze MySQL per il test

Puoi rimuovere un database MySQL in diversi modi. Alcuni modi ovvi sono spegnere l'host, estrarre il cavo di alimentazione o interrompere il processo mysqld con SIGKILL per simulare un comportamento di spegnimento non pulito di MySQL. Ma ci sono anche modi meno subdoli per mandare in crash deliberatamente il tuo server MySQL e poi vedere che tipo di reazione a catena innesca. Perché vorresti farlo? Il fallimento e il recupero possono avere molti casi d'angolo e comprenderli può aiutare a ridurre l'elemento sorpresa quando le cose accadono in produzione. Idealmente, dovresti simulare gli errori in un ambiente controllato, quindi progettare e testare le procedure di failover del database.

Ci sono diverse aree in MySQL che possiamo affrontare, a seconda di come vuoi che fallisca o si arresti in modo anomalo. Puoi corrompere il tablespace, sovraccaricare i buffer e le cache MySQL, limitare le risorse per far morire di fame il server e anche pasticciare con le autorizzazioni. In questo post del blog, ti mostreremo alcuni esempi di come mandare in crash un server MySQL in un ambiente Linux. Alcuni di loro sarebbero adatti ad es. Istanze Amazon RDS, in cui non avresti accesso all'host sottostante.

Uccidi, uccidi, uccidi, muori, muori, muori

Il modo più semplice per fallire un server MySQL è semplicemente uccidere il processo o l'host e non dare a MySQL la possibilità di eseguire un arresto regolare. Per simulare un crash di mysqld, invia semplicemente il segnale 4, 6, 7, 8 o 11 al processo:

$ kill -11 $(pidof mysqld)

Quando guardi il log degli errori di MySQL, puoi vedere le seguenti righe:

11:06:09 UTC - mysqld got signal 11 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
Attempting to collect some information that could help diagnose the problem.
As this is a crash and something is definitely wrong, the information
collection process might fail.
..
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong...

Puoi anche usare kill -9 (SIGKILL) per terminare immediatamente il processo. Maggiori dettagli sul segnale Linux possono essere trovati qui. In alternativa, puoi utilizzare un modo più cattivo sul lato hardware come staccare il cavo di alimentazione, premere il pulsante di ripristino hardware o utilizzare un dispositivo di scherma su STONITH.

Attivazione di OOM

Le popolari offerte MySQL nel cloud come Amazon RDS e Google Cloud SQL non hanno un modo semplice per bloccarle. In primo luogo perché non otterrai alcun accesso a livello di sistema operativo all'istanza del database e, in secondo luogo, perché il provider utilizza un server MySQL con patch proprietario. Un modo è sovraccaricare alcuni buffer e lasciare che il gestore di memoria insufficiente (OOM) escluda il processo MySQL.

Puoi aumentare la dimensione del buffer di ordinamento a qualcosa di più grande di quello che la RAM può gestire e sparare una serie di query di ordinamento mysql sul server MySQL. Creiamo una tabella di 10 milioni di righe utilizzando sysbench sulla nostra istanza Amazon RDS, in modo da poter creare un ordinamento enorme:

$ sysbench \
--db-driver=mysql \
--oltp-table-size=10000000 \
--oltp-tables-count=1 \
--threads=1 \
--mysql-host=dbtest.cdw9q2wnb00s.ap-tokyo-1.rds.amazonaws.com \
--mysql-port=3306 \
--mysql-user=rdsroot \
--mysql-password=password \
/usr/share/sysbench/tests/include/oltp_legacy/parallel_prepare.lua \
run

Modifica sort_buffer_size a 5G (la nostra istanza di test è db.t2.micro - 1GB, 1vCPU) andando su Amazon RDS Dashboard -> Gruppi di parametri -> Crea gruppo di parametri -> specifica il nome del gruppo -> Modifica parametri -> scegli "sort_buffer_size" e specifica il valore come 5368709120.

Applica le modifiche al gruppo di parametri andando su Istanze -> Azione istanza -> Modifica -> Opzioni database -> Gruppo parametri database -> e scegli il nostro gruppo di parametri appena creato. Quindi, riavvia l'istanza RDS per applicare le modifiche.

Una volta su, verifica il nuovo valore di sort_buffer_size :

MySQL [(none)]> select @@sort_buffer_size;
+--------------------+
| @@sort_buffer_size |
+--------------------+
|         5368709120 |
+--------------------+

Quindi attiva 48 semplici query che richiedono l'ordinamento da un client:

$ for i in {1..48}; do (mysql -urdsroot -ppassword -h dbtest.cdw9q2wnb00s.ap-tokyo-1.rds.amazonaws.com -e 'SELECT * FROM sbtest.sbtest1 ORDER BY c DESC >/dev/null &); done

Se esegui quanto sopra su un host standard, noterai che il server MySQL verrà terminato e vedrai apparire le seguenti righe nel syslog o dmesg del sistema operativo:

[164199.868060] Out of memory: Kill process 47060 (mysqld) score 847 or sacrifice child
[164199.868109] Killed process 47060 (mysqld) total-vm:265264964kB, anon-rss:3257400kB, file-rss:0kB

Con systemd, MySQL o MariaDB verranno riavviati automaticamente, così come Amazon RDS. Puoi vedere che il tempo di attività per la nostra istanza RDS verrà reimpostato su 0 (sotto lo stato mysqladmin) e il valore "Tempo di ripristino più recente" (sotto il dashboard RDS) verrà aggiornato al momento in cui è sceso.

Corruzione dei dati

InnoDB ha il proprio tablespace di sistema per memorizzare dizionario di dati, buffer e segmenti di rollback all'interno di un file chiamato ibdata1. Memorizza anche lo spazio tabella condiviso se non configuri innodb_file_per_table (abilitato per impostazione predefinita in MySQL 5.6.6+). Possiamo semplicemente azzerare questo file, inviare un'operazione di scrittura e svuotare le tabelle per arrestare in modo anomalo mysqld:

# empty ibdata1
$ cat /dev/null > /var/lib/mysql/ibdata1
# send a write
$ mysql -uroot -p -e 'CREATE TABLE sbtest.test (id INT)'
# flush tables
$ mysql -uroot -p -e 'FLUSH TABLES WITH READ LOCK; UNLOCK TABLES'

Dopo aver inviato una scrittura, nel log degli errori, noterai:

2017-11-15T06:01:59.345316Z 0 [ERROR] InnoDB: Tried to read 16384 bytes at offset 98304, but was only able to read 0
2017-11-15T06:01:59.345332Z 0 [ERROR] InnoDB: File (unknown): 'read' returned OS error 0. Cannot continue operation
2017-11-15T06:01:59.345343Z 0 [ERROR] InnoDB: Cannot continue operation.

A questo punto, mysql si bloccherà perché non può eseguire alcuna operazione e, dopo lo svuotamento, otterrai le righe "mysqld ha ricevuto il segnale 11" e mysqld si spegnerà. Per ripulire, devi rimuovere ibdata1 danneggiato, così come ib_logfile* perché i file di log di ripristino non possono essere utilizzati con un nuovo tablespace di sistema che verrà generato da mysqld al prossimo riavvio. È prevista una perdita di dati.

Per le tabelle MyISAM, possiamo scherzare con .MYD (file di dati MyISAM) e .MYI (indice MyISAM) nella directory dati MySQL. Ad esempio, il comando seguente sostituisce qualsiasi occorrenza della stringa "F" con "9" all'interno di un file:

$ replace F 9 -- /var/lib/mysql/sbtest/sbtest1.MYD

Quindi, invia alcune scritture (ad esempio, utilizzando sysbench) alla tabella di destinazione ed esegui lo svuotamento:

mysql> FLUSH TABLE sbtest.sbtest1;

Nel registro degli errori di MySQL dovrebbe apparire quanto segue:

2017-11-15T06:56:15.021564Z 448 [ERROR] /usr/sbin/mysqld: Incorrect key file for table './sbtest/sbtest1.MYI'; try to repair it
2017-11-15T06:56:15.021572Z 448 [ERROR] Got an error from thread_id=448, /export/home/pb2/build/sb_0-24964902-1505318733.42/rpm/BUILD/mysql-5.7.20/mysql-5.7.20/storage/myisam/mi_update.c:227

La tabella MyISAM verrà contrassegnata come arrestata in modo anomalo ed è necessaria l'esecuzione dell'istruzione REPAIR TABLE per renderla nuovamente accessibile.

Limitare le risorse

Possiamo anche applicare il limite delle risorse del sistema operativo al nostro processo mysqld, ad esempio il numero di descrittori di file aperti. L'uso della variabile open_file_limit (il valore predefinito è 5000) consente a mysqld di riservare descrittori di file utilizzando il comando setrlimit(). Puoi impostare questa variabile relativamente piccola (quanto basta per l'avvio di mysqld) e quindi inviare più query al server MySQL finché non raggiunge il limite.

Se mysqld è in esecuzione in un server systemd, possiamo impostarlo nel file dell'unità systemd che si trova in /usr/lib/systemd/system/mysqld.service e modificare il seguente valore con un valore inferiore (il valore predefinito di systemd è 6000):

# Sets open_files_limit
LimitNOFILE = 30

Applica le modifiche a systemd e riavvia il server MySQL:

$ systemctl daemon-reload
$ systemctl restart mysqld

Quindi, inizia a inviare nuove connessioni/query che contano in database e tabelle diversi in modo che mysqld debba aprire più file. Noterai il seguente errore:

2017-11-16T04:43:26.179295Z 4 [ERROR] InnoDB: Operating system error number 24 in a file operation.
2017-11-16T04:43:26.179342Z 4 [ERROR] InnoDB: Error number 24 means 'Too many open files'
2017-11-16T04:43:26.179354Z 4 [Note] InnoDB: Some operating system error numbers are described at http://dev.mysql.com/doc/refman/5.7/en/operating-system-error-codes.html
2017-11-16T04:43:26.179363Z 4 [ERROR] InnoDB: File ./sbtest/sbtest9.ibd: 'open' returned OS error 124. Cannot continue operation
2017-11-16T04:43:26.179371Z 4 [ERROR] InnoDB: Cannot continue operation.
2017-11-16T04:43:26.372605Z 0 [Note] InnoDB: FTS optimize thread exiting.
2017-11-16T04:45:06.816056Z 4 [Warning] InnoDB: 3 threads created by InnoDB had not exited at shutdown!

A questo punto, quando viene raggiunto il limite, MySQL si bloccherà e non sarà in grado di eseguire alcuna operazione. Quando tenti di connetterti, dopo un po' vedrai quanto segue:

$ mysql -uroot -p
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 104

Disordine con le autorizzazioni

Il processo mysqld viene eseguito dall'utente "mysql", il che significa che tutti i file e le directory a cui deve accedere sono di proprietà dell'utente/gruppo mysql. Incasinando i permessi e la proprietà, possiamo rendere inutilizzabile il server MySQL:

$ chown root:root /var/lib/mysql
$ chmod 600 /var/lib/mysql

Genera alcuni carichi sul server, quindi connettiti al server MySQL e scarica tutte le tabelle sul disco:

mysql> FLUSH TABLES WITH READ LOCK; UNLOCK TABLES;

In questo momento, mysqld è ancora in esecuzione ma è un po' inutile. Puoi accedervi tramite un client mysql ma non puoi fare nessuna operazione:

mysql> SHOW DATABASES;
ERROR 1018 (HY000): Can't read dir of '.' (errno: 13 - Permission denied)

Per ripulire il pasticcio, imposta le autorizzazioni corrette:

$ chown mysql:mysql /var/lib/mysql
$ chmod 750 /var/lib/mysql
$ systemctl restart mysqld

Bloccalo

FLUSH TABLE CON READ LOCK (FTWRL) può essere distruttivo in un certo numero di condizioni. Ad esempio, in un cluster Galera in cui tutti i nodi sono in grado di elaborare le scritture, puoi utilizzare questa istruzione per bloccare il cluster da uno dei nodi. Questa istruzione interrompe semplicemente le altre query che devono essere elaborate da mysqld durante lo svuotamento fino al rilascio del blocco, il che è molto utile per i processi di backup (tabelle MyISAM) e gli snapshot del file system.

Anche se questa azione non si arresta in modo anomalo o interrompe il server del database durante il blocco, le conseguenze possono essere enormi se la sessione che mantiene il blocco non lo rilascia. Per provare questo, semplicemente:

mysql> FLUSH TABLES WITH READ LOCK;
mysql> exit

Quindi invia un sacco di nuove query a mysqld finché non raggiunge max_connections valore. Ovviamente, una volta fuori, non puoi tornare alla stessa sessione della precedente. Quindi il blocco verrà eseguito all'infinito e l'unico modo per rilasciare il blocco è uccidere la query, da un altro utente con privilegi SUPER (utilizzando un'altra sessione). Oppure uccidi il processo mysqld stesso o esegui un riavvio forzato.

Disclaimer

Questo blog è stato scritto per fornire alternative agli amministratori di sistema e ai DBA per simulare scenari di errore con MySQL. Non provarli sul tuo server di produzione :-)