La capacità di modellare il traffico che va al database è una delle più importanti. Negli ultimi giorni non avevi molto controllo su di esso:le applicazioni inviavano il traffico al database e questo è tutto. HAProxy, che era comunemente usato, inoltre non ha un'opzione per il controllo granulare sul traffico. Con l'introduzione dei proxy compatibili con SQL, come ProxySQL, sono diventate disponibili più possibilità per gli amministratori di database. Diamo un'occhiata alla gestione della connessione e alle possibilità di limitazione in ProxySQL.
Gestione della connessione in ProxySQL
Come forse saprai, il modo in cui ProxySQL funziona è attraverso le regole di query. È un elenco di regole in base alle quali viene testata ogni query e che regolano esattamente il modo in cui ProxySQL gestirà la query. A partire dall'inizio, l'applicazione si connette a ProxySQL. Si autenticherà su ProxySQL (questo è il motivo per cui ProxySQL deve memorizzare tutti gli utenti e gli hash delle password) e quindi ProxySQL lo eseguirà attraverso le regole di query per determinare a quale gruppo host deve essere inviata la query.
ProxySQL apre un pool di connessioni ai server back-end. Non è una mappatura 1 a 1, per impostazione predefinita tenta di riutilizzare una connessione back-end per il maggior numero possibile di connessioni front-end. Questo è chiamato multiplexing della connessione. I dettagli dipendono dal traffico esatto che deve gestire. Ogni transazione aperta deve essere gestita all'interno della stessa connessione. Se è stata definita una sorta di variabile locale, questa connessione non può essere riutilizzata. Essere in grado di riutilizzare una singola connessione back-end da più connessioni front-end riduce significativamente il carico sul database back-end.
Una volta stabilita la connessione al ProxySQL, come accennato in precedenza, verrà elaborata secondo le regole di query. Qui potrebbe aver luogo il traffic shaping. Diamo un'occhiata alle opzioni
Limitazione della connessione in ProxySQL
Per prima cosa, lasciamo cadere tutti i SELECT. Stiamo eseguendo la nostra "applicazione", Sysbench, nel modo seguente:
[email protected]:~# sysbench /root/sysbench/src/lua/oltp_read_only.lua --threads=4 --events=200 --time=0 --mysql-host=10.0.0.101 --mysql-user=sbtest --mysql-password=sbtest --mysql-port=6033 --tables=32 --report-interval=1 --skip-trx=on --table-size=100000 --db-ps-mode=disable --rate=10 run
sysbench 1.1.0-bbee5d5 (using bundled LuaJIT 2.1.0-beta3)
Running the test with following options:
Number of threads: 4
Target transaction rate: 10/sec
Report intermediate results every 1 second(s)
Initializing random number generator from current time
Initializing worker threads...
Threads started!
[ 1s ] thds: 4 tps: 5.97 qps: 103.49 (r/w/o: 103.49/0.00/0.00) lat (ms,95%): 244.38 err/s: 0.00 reconn/s: 0.00
[ 1s ] queue length: 0, concurrency: 4
[ 2s ] thds: 4 tps: 13.02 qps: 181.32 (r/w/o: 181.32/0.00/0.00) lat (ms,95%): 580.02 err/s: 0.00 reconn/s: 0.00
[ 2s ] queue length: 5, concurrency: 4
[ 3s ] thds: 4 tps: 14.99 qps: 228.81 (r/w/o: 228.81/0.00/0.00) lat (ms,95%): 669.89 err/s: 0.00 reconn/s: 0.00
[ 3s ] queue length: 1, concurrency: 4
[ 4s ] thds: 4 tps: 16.99 qps: 232.88 (r/w/o: 232.88/0.00/0.00) lat (ms,95%): 350.33 err/s: 0.00 reconn/s: 0.00
[ 4s ] queue length: 0, concurrency: 3
[ 5s ] thds: 4 tps: 8.99 qps: 99.91 (r/w/o: 99.91/0.00/0.00) lat (ms,95%): 369.77 err/s: 0.00 reconn/s: 0.00
[ 5s ] queue length: 0, concurrency: 1
[ 6s ] thds: 4 tps: 3.99 qps: 55.81 (r/w/o: 55.81/0.00/0.00) lat (ms,95%): 147.61 err/s: 0.00 reconn/s: 0.00
[ 6s ] queue length: 0, concurrency: 1
[ 7s ] thds: 4 tps: 11.06 qps: 162.89 (r/w/o: 162.89/0.00/0.00) lat (ms,95%): 173.58 err/s: 0.00 reconn/s: 0.00
[ 7s ] queue length: 0, concurrency: 2
[ 8s ] thds: 4 tps: 7.99 qps: 112.88 (r/w/o: 112.88/0.00/0.00) lat (ms,95%): 200.47 err/s: 0.00 reconn/s: 0.00
[ 8s ] queue length: 0, concurrency: 2
[ 9s ] thds: 4 tps: 9.01 qps: 110.09 (r/w/o: 110.09/0.00/0.00) lat (ms,95%): 71.83 err/s: 0.00 reconn/s: 0.00
[ 9s ] queue length: 0, concurrency: 0
[ 10s ] thds: 4 tps: 9.99 qps: 143.87 (r/w/o: 143.87/0.00/0.00) lat (ms,95%): 153.02 err/s: 0.00 reconn/s: 0.00
[ 10s ] queue length: 0, concurrency: 1
[ 11s ] thds: 4 tps: 12.02 qps: 177.28 (r/w/o: 177.28/0.00/0.00) lat (ms,95%): 170.48 err/s: 0.00 reconn/s: 0.00
[ 11s ] queue length: 0, concurrency: 1
[ 12s ] thds: 4 tps: 5.00 qps: 70.95 (r/w/o: 70.95/0.00/0.00) lat (ms,95%): 231.53 err/s: 0.00 reconn/s: 0.00
[ 12s ] queue length: 0, concurrency: 2
[ 13s ] thds: 4 tps: 10.00 qps: 137.01 (r/w/o: 137.01/0.00/0.00) lat (ms,95%): 223.34 err/s: 0.00 reconn/s: 0.00
[ 13s ] queue length: 0, concurrency: 1
[ 14s ] thds: 4 tps: 11.01 qps: 143.14 (r/w/o: 143.14/0.00/0.00) lat (ms,95%): 130.13 err/s: 0.00 reconn/s: 0.00
[ 14s ] queue length: 0, concurrency: 0
[ 15s ] thds: 4 tps: 5.00 qps: 100.99 (r/w/o: 100.99/0.00/0.00) lat (ms,95%): 297.92 err/s: 0.00 reconn/s: 0.00
[ 15s ] queue length: 0, concurrency: 4
[ 16s ] thds: 4 tps: 10.98 qps: 122.82 (r/w/o: 122.82/0.00/0.00) lat (ms,95%): 344.08 err/s: 0.00 reconn/s: 0.00
[ 16s ] queue length: 0, concurrency: 0
[ 17s ] thds: 4 tps: 3.00 qps: 59.01 (r/w/o: 59.01/0.00/0.00) lat (ms,95%): 287.38 err/s: 0.00 reconn/s: 0.00
[ 17s ] queue length: 0, concurrency: 2
[ 18s ] thds: 4 tps: 13.01 qps: 165.14 (r/w/o: 165.14/0.00/0.00) lat (ms,95%): 173.58 err/s: 0.00 reconn/s: 0.00
[ 18s ] queue length: 0, concurrency: 0
[ 19s ] thds: 4 tps: 6.99 qps: 98.79 (r/w/o: 98.79/0.00/0.00) lat (ms,95%): 253.35 err/s: 0.00 reconn/s: 0.00
[ 19s ] queue length: 0, concurrency: 1
[ 20s ] thds: 4 tps: 9.98 qps: 164.60 (r/w/o: 164.60/0.00/0.00) lat (ms,95%): 590.56 err/s: 0.00 reconn/s: 0.00
[ 20s ] queue length: 0, concurrency: 3
SQL statistics:
queries performed:
read: 2800
write: 0
other: 0
total: 2800
transactions: 200 (9.64 per sec.)
queries: 2800 (134.89 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
Throughput:
events/s (eps): 9.6352
time elapsed: 20.7573s
total number of events: 200
Latency (ms):
min: 44.36
avg: 202.66
max: 726.59
95th percentile: 590.56
sum: 40531.73
Threads fairness:
events (avg/stddev): 50.0000/0.71
execution time (avg/stddev): 10.1329/0.05
È un traffico completamente di sola lettura, dovrebbe avere una media di 10 transazioni (140 query) al secondo. Poiché questi sono solo SELECT, possiamo facilmente modificare una delle regole di query esistenti e bloccare il traffico:
Ciò risulterà nel seguente errore sul lato dell'applicazione:
[email protected]:~# sysbench /root/sysbench/src/lua/oltp_read_only.lua --threads=4 --events=200 --time=0 --mysql-host=10.0.0.101 --mysql-user=sbtest --mysql-password=sbtest --mysql-port=6033 --tables=32 --report-interval=1 --skip-trx=on --table-size=100000 --db-ps-mode=disable --rate=10 run
sysbench 1.1.0-bbee5d5 (using bundled LuaJIT 2.1.0-beta3)
Running the test with following options:
Number of threads: 4
Target transaction rate: 10/sec
Report intermediate results every 1 second(s)
Initializing random number generator from current time
Initializing worker threads...
Threads started!
FATAL: mysql_drv_query() returned error 1148 (SELECT queries are not allowed!!!) for query 'SELECT c FROM sbtest25 WHERE id=83384'
FATAL: `thread_run' function failed: /usr/local/share/sysbench/oltp_common.lua:426: SQL error, errno = 1148, state = '42000': SELECT queries are not allowed!!!
Ora, questo è ovviamente duro. Possiamo essere più educati e aumentare il ritardo per le query SELECT.
Ciò, ovviamente, influisce sulle prestazioni delle query poiché vengono aggiunti 10 millisecondi a ogni SELECT che viene eseguito.
SQL statistics:
queries performed:
read: 2800
write: 0
other: 0
total: 2800
transactions: 200 (5.60 per sec.)
queries: 2800 (78.44 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
Throughput:
events/s (eps): 5.6030
time elapsed: 35.6952s
total number of events: 200
Latency (ms):
min: 622.04
avg: 7957.01
max: 18808.60
95th percentile: 15934.78
sum: 1591401.12
Threads fairness:
events (avg/stddev): 50.0000/36.01
execution time (avg/stddev): 397.8503/271.50
Impostiamo ritardi per ogni query SELECT, il che non ha necessariamente alcun senso se non mostrare che puoi farlo. In genere si desidera utilizzare il ritardo su alcune query offensive. Diciamo che abbiamo una query molto pesante e che aggiunge un carico significativo sulla CPU del database. Quel che è peggio, è stato introdotto da una recente modifica del codice e proviene da tutti gli host dell'applicazione. Certo, puoi aspettare che gli sviluppatori annullino la modifica o inviino una correzione, ma con ProxySQL puoi prendere il controllo nelle tue mani e semplicemente bloccare la query o ridurne l'impatto anche in modo abbastanza significativo.
Supponiamo che il nostro database stia procedendo bene quando i campanelli d'allarme iniziano a suonare.
Una rapida occhiata alle metriche ci dice che il numero di query eseguite da ProxySQL si interrompe mentre l'utilizzo della CPU aumenta. Possiamo esaminare le principali query in ProxySQL per vedere se possiamo notare qualcosa di insolito.
In effetti è insolito:una nuova query che non fa parte del mix di query regolari che abbiamo osservato sul nostro sistema. Possiamo usare l'opzione per creare la regola di query.
Aggiungeremo un ritardo di 50 secondi alla query impostando Delay su 50000 ms.
Possiamo confermare che la regola di query è in uso e che le query la stanno raggiungendo .
Dopo poco possiamo anche notare che il carico diminuisce e il numero di query eseguite è di nuovo nell'intervallo previsto. Ovviamente, invece di aggiungere il ritardo alla query, potremmo semplicemente bloccarla. Sarebbe stato ancora più facile da realizzare per noi, ma il blocco totale della query potrebbe avere un impatto significativo sull'applicazione.
Ci auguriamo che questo breve post sul blog ti dia un'idea di come ProxySQL può aiutarti a modellare il tuo traffico e ridurre il calo delle prestazioni introdotto dalle query incontrollate.