Indice dei contenuti dell'articolo:
Quando ci sono problemi di performance MySQL, la prima soluzione che sentiamo proporre è quella di separare la parte applicativa o frontend dal DB MySQL Server. Questo approccio è sicuramente accademicamente e teoricamente corretto ed apprezzabile, tuttavia come in molti altri ambiti la teoria non coincide sempre con la pratica e nel tentativo di fare bene o meglio si finisce col disastro.
Comprendere i vantaggi di avere un server MySQL locale piuttosto che remoto serve a ponderare coscientemente il da farsi ed a quali trappole e leggende metropolitane bisogna stare attenti.
Anche in casi in cui il Server MySQL e l’applicativo si trovi sulla stessa macchina, bisogna dare necessariamente attenzione ed un occhio di riguardo a come verra instaurata la connessione, se tramite socket ad esempio, oppure tramite connessione TCP/IP sulla porta 3306.
Cos’è un socket Unix?
Un socket, in informatica, indica un’astrazione software progettata per utilizzare delle API standard e condivise per la trasmissione e la ricezione di dati attraverso una rete oppure come meccanismo di IPC. È il punto in cui il codice applicativo di un processo accede al canale di comunicazione per mezzo di una porta, ottenendo una comunicazione tra processi che lavorano su due macchine fisicamente separate. Dal punto di vista di un programmatore un socket è un particolare oggetto sul quale leggere e scrivere i dati da trasmettere o ricevere.
Famiglie di socket
I tipi di protocolli utilizzati dal socket, ne definiscono la famiglia (o dominio). Possiamo distinguere, ad esempio, due importanti famiglie:
- AF_INET: comunicazione tra host remoti, tramite Internet;
- AF_UNIX: comunicazione tra processi locali, su macchine Unix. Questa famiglia è anche chiamata Unix Domain Socket.
Unix Domain Socket
Un socket di dominio locale o socket locale, in informatica, si intende un’estremità di un canale di comunicazione interprocesso con funzionamento analogo a una connessione di rete, ma basato sulla condivisione di dati nel file system. Poiché tale canale di comunicazione non è implementato su protocolli di rete, può essere usato solo per la comunicazione fra processi residenti sullo stesso host. Socket locali di questo genere si trovano nei sistemi operativi POSIX e Unix, e sono note in ciascuno di questi contesti con denominazioni specifiche, come socket di dominio Unix (Unix domain socket) o socket di IPC locale POSIX (POSIX local IPC socket).
Socket IP o TCP
I socket IP (soprattutto i socket TCP/IP) sono un meccanismo che consente la comunicazione tra processi sulla rete. In alcuni casi, è possibile utilizzare i socket TCP/IP per comunicare con i processi in esecuzione sullo stesso computer (utilizzando l’interfaccia di loopback), ovvero il famoso indirizzo 127.0.0.1
Le principali differenze tra le famiglie di Socket Unix Domain e quelli TCP
I socket di dominio UNIX sanno che stanno eseguendo e comunicando sullo stesso sistema, quindi possono evitare alcuni controlli e operazioni (come il routing); il che li rende più veloci e leggeri dei socket IP.
Inoltre i socket di dominio UNIX sono soggetti alle autorizzazioni del file system, mentre i socket TCP possono essere controllati solo a livello di filtro dei pacchetti.
Quindi, se prevedi di comunicare con i processi sullo stesso host, utilizzare Socket Unix è senza dubbio un’opzione migliore rispetto ai socket IP.
Quando ti connetti a un’istanza MySQL locale, hai insomma due metodi comunemente usati: usa il protocollo TCP/IP per connetterti all’indirizzo locale – “localhost” o 127.0.0.1 – o usa Unix Domain Socket .
Se hai una scelta (se la tua applicazione supporta entrambi i metodi), usa Unix Domain Socket poiché è sia più sicuro che più efficiente.
Quanto più efficiente, però? Non ho esaminato questo argomento per anni, quindi vediamo come funziona una versione moderna di MySQL su hardware relativamente moderno ed un Linux moderno.
Obiettivo del test
Determinare se c’è una differenza significativa quando si utilizza il TCP locale rispetto alla connessione socket locale a MySQL.
Cosa testeremo
- MySQL 8.0 (Percona Server 8.0.22-13)
Verranno eseguiti i seguenti test:
- locale sul socket
/var/run/mysqld/mysqld.sock
- locale con connessione TCP
- TCP remoto
- socket falsa remota con
socat
In merito al tool di proxy forwarding Socat ne abbiamo già parlato nella guida.
Il test stesso verrà eseguito con sysbench .
#!/usr/bin/env bash
sysbench \ /usr/share/sysbench/oltp_read_only.lua \ --threads="2" \ --tables=10 \ --table-size=1000000 \ --report-interval=5 \ --rand-type=pareto \ --forced-shutdown=1 \ --time=300 \ --events=0 \ --point-selects=25 \ --range_size=5 \ --skip_trx=on \ --percentile=95 \ --mysql-host=mysql.to.test \ --mysql-port=3306 \ --mysql-user=bench \ --mysql-password=our-fancy-bench-password \ --mysql-db=bench \ --mysql-storage-engine=INNODB \ run
Eseguiremo solo con 2 thread, quindi confrontiamo sempre le stesse informazioni.
Esamineremo nuovamente le transazioni/query al secondo e qual è la latenza del 95 percentile per le connessioni utilizzate.
utilizzo di socat
Sulla macchina del server MySQL disattiviamo la rete sul server MySQL.
Commenteremo le impostazioni di ascolto TCP e aggiungeremo skip-networking
nella nostra [mysqld]
sezione del file config.
#port = 3306
#bind-address = 0.0.0.0
skip-networking
Sulla macchina del server MySQL eseguiremo socat per esporre il nostro socket sulla porta 3306.
socat TCP-LISTEN:3306,reuseaddr,fork \ UNIX-CONNECT:/var/run/mysqld/mysqld.sock
Sulla macchina remota useremo anche socat così possiamo fingere di connetterci al socket della macchina locale.
socat UNIX-LISTEN:/var/run/mysqld/mysqld.sock,\ fork,reuseaddr,unlink-early,\ user=mysql,group=mysql,mode=777 \ TCP:mysql.to.test:3306
Nota : il linewrapping della stringa separata da virgole non deve avere spazi bianchi!
In questo modo possiamo fingere di connetterci al socket localhost. Vedremo come si comporta bene.
locale vs remoto
Confrontiamo i risultati locali e remoti. Useremo la connessione socket locale come linea di base e confronteremo il resto con quello.
Risultati socket locali :
SQL statistics:
queries performed:
read: 10084576
write: 0
other: 0
total: 10084576
transactions: 347744 (1159.13 per sec.)
queries: 10084576 (33614.79 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
General statistics:
total time: 300.0029s
total number of events: 347744
Latency (ms):
min: 1.04
avg: 1.72
max: 20.57
95th percentile: 2.18
sum: 599516.93
Threads fairness:
events (avg/stddev): 173872.0000/208.00
execution time (avg/stddev): 299.7585/0.00
Risultati TCP locali :
SQL statistics:
queries performed:
read: 7832697
write: 0
other: 0
total: 7832697
transactions: 270093 (900.29 per sec.)
queries: 7832697 (26108.54 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
General statistics:
total time: 300.0039s
total number of events: 270093
Latency (ms):
min: 1.36
avg: 2.22
max: 20.91
95th percentile: 2.81
sum: 599599.56
Threads fairness:
events (avg/stddev): 135046.5000/33.50
execution time (avg/stddev): 299.7998/0.00
Risultati TCP remoti :
SQL statistics:
queries performed:
read: 2839042
write: 0
other: 0
total: 2839042
transactions: 97898 (326.32 per sec.)
queries: 2839042 (9463.20 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
General statistics:
total time: 300.0074s
total number of events: 97898
Latency (ms):
min: 3.33
avg: 6.13
max: 225.58
95th percentile: 7.84
sum: 599783.11
Threads fairness:
events (avg/stddev): 48949.0000/446.00
execution time (avg/stddev): 299.8916/0.00
Risultati del socket remota :
SQL statistics:
queries performed:
read: 2255707
write: 0
other: 0
total: 2255707
transactions: 77783 (259.16 per sec.)
queries: 2255707 (7515.59 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
General statistics:
total time: 300.1356s
total number of events: 77783
Latency (ms):
min: 4.43
avg: 7.71
max: 241.09
95th percentile: 9.73
sum: 599962.48
Threads fairness:
events (avg/stddev): 38891.5000/228.50
execution time (avg/stddev): 299.9812/0.07
Conclusioni sulle performance
genere | transazioni / sec | query / sec | 95% di latenza (ms) | percentuale |
---|---|---|---|---|
Socket locale | 1159.13 | 33614.79 | 2.18 | 100% |
TCP locale | 900.29 | 26108.54 | 2.81 | 77,7% |
TCP remoto | 326.32 | 9463.20 | 7.84 | 28,2% |
“socket” remota | 259.16 | 7515.59 | 9.73 | 22,4% |
Quando abbiamo guardato per la prima volta i grafici è sembrato sin da subito “ok, TCP locale è ragionevolmente ok”.
Ma se osserviamo i numeri più dettagliatamente, vediamo che abbiamo un throughput aggiuntivo del 20% se ci connettiamo ad un socket locale.
Anche in questo caso, una volta che introduciamo una connessione TCP sul cavo, il throughput diminuisce drasticamente.
Poiché gli Unix Domain Sockets sono molto più semplici e ottimizzati per la comunicazione di processo locale, ci si aspetterebbe che funzionino meglio di TCP/IP sull’interfaccia di loopback. Effettivamente si comportano molto meglio! Quindi, se hai una scelta, usa Unix Domain Sockets per connetterti al tuo sistema MySQL locale se vuoi davvero il massimo del throughput dal tuo database MySQL.