Il carico di lavoro del database MySQL è determinato dal numero di query che elabora. Ci sono diverse situazioni in cui può avere origine la lentezza di MySQL. La prima possibilità è se sono presenti query che non utilizzano l'indicizzazione corretta. Quando una query non può utilizzare un indice, il server MySQL deve utilizzare più risorse e tempo per elaborare quella query. Monitorando le query, hai la possibilità di individuare il codice SQL che è la causa principale di un rallentamento e risolverlo prima che le prestazioni complessive diminuiscano.
In questo post del blog, evidenzieremo la funzionalità Query Outlier disponibile in ClusterControl e vedremo come può aiutarci a migliorare le prestazioni del database. In generale, ClusterControl esegue il campionamento delle query MySQL in due modi:
- Recupera le query dallo schema delle prestazioni (consigliato ).
- Analizza il contenuto di MySQL Slow Query.
Se lo schema delle prestazioni è disabilitato, ClusterControl utilizzerà per impostazione predefinita il registro di query lente. Per saperne di più su come ClusterControl esegue questa operazione, dai un'occhiata a questo post del blog, Come utilizzare ClusterControl Query Monitor per MySQL, MariaDB e Percona Server.
Cosa sono i valori anomali della query?
Un valore anomalo è una query che richiede più tempo del normale tempo di query di quel tipo. Non prenderlo letteralmente come query "scritte male". Dovrebbe essere trattato come potenziali query comuni non ottimali che potrebbero essere migliorate. Dopo un numero di campioni e quando ClusterControl ha avuto statistiche sufficienti, può determinare se la latenza è superiore al normale (2 sigma + average_query_time), quindi è un valore anomalo e verrà aggiunto al Query Outlier.
Questa funzione dipende dalla funzione Query principali. Se il monitoraggio delle query è abilitato e le query principali vengono acquisite e popolate, i valori anomali della query le riepilogheranno e forniranno un filtro basato sul timestamp. Per vedere l'elenco delle query che richiedono attenzione, vai su ClusterControl -> Query Monitor -> Query Outliers e dovresti vedere alcune query elencate (se presenti):
Come puoi vedere dallo screenshot qui sopra, i valori anomali sono fondamentalmente query che ha richiesto almeno 2 volte più tempo del tempo medio di query. Innanzitutto la prima voce, il tempo medio è 34,41 ms mentre il tempo di query dell'outlier è 140 ms (più di 2 volte superiore al tempo medio). Allo stesso modo, per le voci successive, le colonne Query Time e Avg Query Time sono due elementi importanti per giustificare i valori in sospeso di una particolare query anomala.
È relativamente facile trovare uno schema di una particolare query anomala osservando un periodo di tempo più ampio, ad esempio una settimana fa, come evidenziato nella schermata seguente:
Facendo clic su ogni riga, puoi vedere la query completa che è davvero utile per individuare e comprendere il problema, come illustrato nella sezione successiva.
Correzione dei valori anomali della query
Per correggere i valori anomali, è necessario comprendere la natura della query, il motore di archiviazione delle tabelle, la versione del database, il tipo di clustering e l'impatto della query. In alcuni casi, la query anomala non sta davvero degradando le prestazioni complessive del database. Come in questo esempio, abbiamo visto che la query è rimasta in evidenza per l'intera settimana ed è stato l'unico tipo di query acquisito, quindi è probabilmente una buona idea correggere o migliorare questa query, se possibile.
Come nel nostro caso, la query anomala è:
SELECT i2l.country_code AS country_code, i2l.country_name AS country_name
FROM ip2location i2l
WHERE (i2l.ip_to >= INET_ATON('104.144.171.139')
AND i2l.ip_from <= INET_ATON('104.144.171.139'))
LIMIT 1
OFFSET 0;
E il risultato della query è:
+--------------+---------------+
| country_code | country_name |
+--------------+---------------+
| US | United States |
+--------------+---------------+
Utilizzo di EXPLAIN
La query è una query di selezione dell'intervallo di sola lettura per determinare le informazioni sulla posizione geografica dell'utente (codice del paese e nome del paese) per un indirizzo IP nella tabella ip2location. L'utilizzo dell'istruzione EXPLAIN può aiutarci a comprendere il piano di esecuzione della query:
mysql> EXPLAIN SELECT i2l.country_code AS country_code, i2l.country_name AS country_name
FROM ip2location i2l
WHERE (i2l.ip_to>=INET_ATON('104.144.171.139')
AND i2l.ip_from<=INET_ATON('104.144.171.139'))
LIMIT 1 OFFSET 0;
+----+-------------+-------+------------+-------+--------------------------------------+-------------+---------+------+-------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+--------------------------------------+-------------+---------+------+-------+----------+------------------------------------+
| 1 | SIMPLE | i2l | NULL | range | idx_ip_from,idx_ip_to,idx_ip_from_to | idx_ip_from | 5 | NULL | 66043 | 50.00 | Using index condition; Using where |
+----+-------------+-------+------------+-------+--------------------------------------+-------------+---------+------+-------+----------+------------------------------------+
La query viene eseguita con una scansione dell'intervallo sulla tabella utilizzando l'indice idx_ip_from con il 50% di righe potenziali (filtrate).
Motore di archiviazione corretto
Osservando la struttura della tabella di ip2location:
mysql> SHOW CREATE TABLE ip2location\G
*************************** 1. row ***************************
Table: ip2location
Create Table: CREATE TABLE `ip2location` (
`ip_from` int(10) unsigned DEFAULT NULL,
`ip_to` int(10) unsigned DEFAULT NULL,
`country_code` char(2) COLLATE utf8_bin DEFAULT NULL,
`country_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
KEY `idx_ip_from` (`ip_from`),
KEY `idx_ip_to` (`ip_to`),
KEY `idx_ip_from_to` (`ip_from`,`ip_to`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Questa tabella si basa sul database IP2location e viene raramente aggiornata/scritta, di solito solo il primo giorno del mese di calendario (consigliato dal fornitore). Quindi un'opzione è convertire la tabella nel motore di archiviazione MyISAM (MySQL) o Aria (MariaDB) con un formato di riga fisso per ottenere prestazioni di sola lettura migliori. Si noti che questo è applicabile solo se si esegue MySQL o MariaDB standalone o replica. Su Galera Cluster e Group Replication, attieniti al motore di archiviazione InnoDB (a meno che tu non sappia cosa stai facendo).
Ad ogni modo, per convertire la tabella da InnoDB a MyISAM con formato riga fisso, esegui semplicemente il seguente comando:
ALTER TABLE ip2location ENGINE=MyISAM ROW_FORMAT=FIXED;
Nella nostra misurazione, con 1000 test di ricerca di indirizzi IP casuali, le prestazioni della query sono migliorate di circa il 20% con MyISAM e il formato riga fisso:
- Tempo medio (InnoDB):21,467823 ms
- Tempo medio (MyISAM fisso):17,175942 ms
- Miglioramento:19,992157565301 %
Puoi aspettarti che questo risultato sia immediato dopo che la tabella è stata modificata. Non è necessaria alcuna modifica al livello superiore (applicazione/bilanciamento del carico).
Ottimizzazione della query
Un altro modo consiste nell'esaminare il piano di query e utilizzare un approccio più efficiente per un migliore piano di esecuzione delle query. La stessa query può essere scritta anche utilizzando la sottoquery come di seguito:
SELECT `country_code`, `country_name` FROM
(SELECT `country_code`, `country_name`, `ip_from`
FROM `ip2location`
WHERE ip_to >= INET_ATON('104.144.171.139')
LIMIT 1)
AS temptable
WHERE ip_from <= INET_ATON('104.144.171.139');
La query ottimizzata ha il seguente piano di esecuzione della query:
mysql> EXPLAIN SELECT `country_code`,`country_name` FROM
(SELECT `country_code`, `country_name`, `ip_from`
FROM `ip2location`
WHERE ip_to >= INET_ATON('104.144.171.139')
LIMIT 1)
AS temptable
WHERE ip_from <= INET_ATON('104.144.171.139');
+----+-------------+--------------+------------+--------+---------------+-----------+---------+------+-------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+--------+---------------+-----------+---------+------+-------+----------+-----------------------+
| 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| 2 | DERIVED | ip2location | NULL | range | idx_ip_to | idx_ip_to | 5 | NULL | 66380 | 100.00 | Using index condition |
+----+-------------+--------------+------------+--------+---------------+-----------+---------+------+-------+----------+-----------------------+
Utilizzando la sottoquery, possiamo ottimizzare la query utilizzando una tabella derivata incentrata su un indice. La query deve restituire solo 1 record in cui il valore ip_to è maggiore o uguale al valore dell'indirizzo IP. Ciò consente alle righe potenziali (filtrate) di raggiungere il 100%, che è la più efficiente. Quindi, controlla che ip_from sia minore o uguale al valore dell'indirizzo IP. Se lo è, allora dovremmo trovare il record. In caso contrario, l'indirizzo IP non esiste nella tabella ip2location.
Nella nostra misurazione, le prestazioni della query sono migliorate di circa il 99% utilizzando una sottoquery:
- Tempo medio (InnoDB + scansione dell'intervallo):22,87112 ms
- Tempo medio (InnoDB + subquery):0,14744 ms
- Miglioramento:99,355344207017 %
Con l'ottimizzazione di cui sopra, possiamo vedere un tempo di esecuzione della query inferiore al millisecondo di questo tipo di query, che è un enorme miglioramento considerando che il tempo medio precedente è di 22 ms. Tuttavia, dobbiamo apportare alcune modifiche al livello superiore (applicazione/bilanciamento del carico) per beneficiare di questa query ottimizzata.
Patch o riscrittura di query
Patch le tue applicazioni per utilizzare la query ottimizzata o riscrivere la query anomala prima che raggiunga il server di database. Possiamo raggiungere questo obiettivo utilizzando un sistema di bilanciamento del carico MySQL come ProxySQL (regole di query) o MariaDB MaxScale (filtro di riscrittura delle istruzioni) o utilizzando il plug-in MySQL Query Rewriter. Nell'esempio seguente, utilizziamo ProxySQL davanti al nostro cluster di database e possiamo semplicemente creare una regola per riscrivere la query più lenta in quella più veloce, ad esempio:
Salva la regola di query e monitora la pagina Query Outliers in ClusterControl. Questa correzione rimuoverà ovviamente le query anomale dall'elenco dopo l'attivazione della regola di query.
Conclusione
Query outliers è uno strumento di monitoraggio delle query proattivo che può aiutarci a comprendere e risolvere il problema delle prestazioni prima che vada fuori controllo. Man mano che la tua applicazione cresce e diventa più esigente, questo strumento può aiutarti a mantenere prestazioni decenti del database lungo il percorso.