La capacité à façonner le trafic qui va vers la base de données est l'une des plus importantes. Au cours des derniers jours, vous n'aviez pas beaucoup de contrôle dessus - les applications envoyaient le trafic vers la base de données et c'est à peu près tout. HAProxy, qui était couramment utilisé, n'a pas non plus d'option pour un contrôle précis du trafic. Avec l'introduction des proxies compatibles SQL, comme ProxySQL, davantage de possibilités sont devenues disponibles pour les administrateurs de bases de données. Examinons les possibilités de gestion et de limitation des connexions dans ProxySQL.
Gestion des connexions dans ProxySQL
Comme vous le savez peut-être, le fonctionnement de ProxySQL passe par les règles de requête. Il s'agit d'une liste de règles sur lesquelles chaque requête est testée et qui régissent exactement la manière dont ProxySQL traitera la requête. Dès le début, l'application se connecte à ProxySQL. Il s'authentifiera auprès de ProxySQL (c'est pourquoi ProxySQL doit stocker tous les utilisateurs et les hachages de mots de passe), puis ProxySQL l'exécutera à travers les règles de requête pour déterminer à quel groupe d'hôtes la requête doit être envoyée.
ProxySQL ouvre un pool de connexions aux serveurs principaux. Il ne s'agit pas d'un mappage 1 à 1, par défaut, il essaie de réutiliser une connexion backend pour autant de connexions frontend que possible. C'est ce qu'on appelle le multiplexage de connexion. Les détails dépendent du trafic exact qu'il doit gérer. Chaque transaction ouverte doit être traitée au sein de la même connexion. Si une sorte de variable locale est définie, cette connexion ne peut pas être réutilisée. La possibilité de réutiliser une connexion backend unique par plusieurs connexions frontend réduit considérablement la charge sur la base de données backend.
Une fois la connexion établie au ProxySQL, comme nous l'avons mentionné précédemment, elle sera traitée conformément aux règles de requête. Ici, la mise en forme du trafic peut avoir lieu. Jetons un coup d'œil aux options
Limitation des connexions dans ProxySQL
Tout d'abord, supprimons tous les SELECT. Nous exécutons notre "application", Sysbench, de la manière suivante :
[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
Il s'agit d'un trafic entièrement en lecture seule, il devrait être en moyenne de 10 transactions (140 requêtes) par seconde. Comme ce ne sont que des SELECT, nous pouvons facilement modifier l'une des règles de requête existantes et bloquer le trafic :
Cela entraînera l'erreur suivante du côté de l'application :
[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!!!
Maintenant, c'est évidemment dur. Nous pouvons être plus polis et simplement augmenter le délai pour les requêtes SELECT.
Ceci, évidemment, a un impact sur les performances des requêtes car 10 millisecondes sont ajoutées à chaque SELECT exécuté.
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
Nous configurons des délais pour chaque requête SELECT, ce qui n'a pas nécessairement de sens autre que de montrer que vous pouvez le faire. En règle générale, vous souhaitez utiliser le délai pour certaines requêtes incriminées. Supposons que nous ayons une requête très lourde et qu'elle ajoute une charge importante au processeur de la base de données. Pire encore, il a été introduit par un changement de code récent et il provient de tous les hôtes d'application. Bien sûr, vous pouvez attendre que les développeurs annulent la modification ou appliquent un correctif, mais avec ProxySQL, vous pouvez prendre le contrôle entre vos mains et bloquer la requête ou réduire son impact, même de manière assez significative.
Supposons que notre base de données avance bien lorsque la sonnette d'alarme commence à sonner.
Un rapide coup d'œil aux métriques nous indique que le nombre de requêtes exécutées par ProxySQL s'arrête tandis que l'utilisation du processeur augmente. Nous pouvons regarder les principales requêtes dans ProxySQL pour voir si nous pouvons remarquer quelque chose d'inhabituel.
C'est en effet inhabituel - une nouvelle requête qui ne fait pas partie du combinaison de requêtes régulières que nous avons observées sur notre système. Nous pouvons utiliser l'option pour créer la règle de requête.
Nous ajouterons un délai de 50 secondes à la requête en définissant le délai sur 50 000 Mme.
Nous pouvons confirmer que la règle de requête est utilisée et que les requêtes l'atteignent .
Après un court instant, nous pouvons également remarquer que la charge chute et que le nombre des requêtes exécutées est à nouveau dans la plage attendue. Bien sûr, au lieu d'ajouter le délai à la requête, nous pourrions simplement la bloquer. Cela aurait été encore plus facile à réaliser pour nous, mais le blocage total de la requête peut avoir un impact significatif sur l'application.
Nous espérons que ce court article de blog vous donnera un aperçu de la manière dont ProxySQL peut vous aider à façonner votre trafic et à réduire l'impact sur les performances provoqué par les requêtes incontrôlables.