Die Fähigkeit, den Datenverkehr zu formen, der zur Datenbank geht, ist eine der wichtigsten. In den vergangenen Tagen hatten Sie nicht so viel Kontrolle darüber - Anwendungen haben den Datenverkehr an die Datenbank gesendet und das war es auch schon. Auch das bisher weit verbreitete HAProxy verfügt nicht über eine Möglichkeit zur feingranularen Kontrolle des Datenverkehrs. Mit der Einführung von SQL-fähigen Proxys wie ProxySQL wurden Datenbankadministratoren mehr Möglichkeiten eröffnet. Werfen wir einen Blick auf die Verbindungsbehandlung und Drosselungsmöglichkeiten in ProxySQL.
Verbindungsbehandlung in ProxySQL
Wie Sie vielleicht wissen, funktioniert ProxySQL über die Abfrageregeln. Es ist eine Liste von Regeln, gegen die jede Abfrage getestet wird und die genau bestimmen, wie ProxySQL die Abfrage behandelt. Von Anfang an verbindet sich die Anwendung mit ProxySQL. Es authentifiziert sich bei ProxySQL (deshalb muss ProxySQL alle Benutzer- und Passwort-Hashes speichern) und ProxySQL führt es dann durch die Abfrageregeln, um zu bestimmen, an welche Hostgruppe die Abfrage gesendet werden soll.
ProxySQL öffnet einen Pool von Verbindungen zu den Backend-Servern. Es handelt sich nicht um eine 1-zu-1-Zuordnung, sondern versucht standardmäßig, eine Backend-Verbindung für so viele Frontend-Verbindungen wie möglich wiederzuverwenden. Dies wird als Verbindungsmultiplexing bezeichnet. Details hängen von dem genauen Datenverkehr ab, den es verarbeiten muss. Jede offene Transaktion muss innerhalb derselben Verbindung abgewickelt werden. Wenn eine Art lokaler Variable definiert ist, kann diese Verbindung nicht wiederverwendet werden. Die Möglichkeit, eine einzelne Backend-Verbindung durch mehrere Frontend-Verbindungen wiederzuverwenden, reduziert die Belastung der Backend-Datenbank erheblich.
Sobald die Verbindung zum ProxySQL hergestellt ist, wie wir zuvor erwähnt haben, wird sie gemäß den Abfrageregeln verarbeitet. Hier kann das Traffic Shaping stattfinden. Werfen wir einen Blick auf die Optionen
Verbindungsdrosselung in ProxySQL
Lassen Sie uns zuerst alle SELECTs fallen lassen. Wir führen unsere „Anwendung“, Sysbench, auf folgende Weise aus:
[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
Es handelt sich um einen vollständig schreibgeschützten Datenverkehr, er sollte durchschnittlich 10 Transaktionen (140 Abfragen) pro Sekunde umfassen. Da dies nur SELECTs sind, können wir einfach eine der vorhandenen Abfrageregeln ändern und den Datenverkehr blockieren:
Dies führt auf der Anwendungsseite zu folgendem Fehler:
[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!!!
Nun, das ist offensichtlich hart. Wir können höflicher sein und einfach die Verzögerung für die SELECT-Abfragen erhöhen.
Dies wirkt sich offensichtlich auf die Leistung der Abfragen aus, da 10 Millisekunden hinzugefügt werden zu jedem SELECT, das ausgeführt wird.
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
Wir richten Verzögerungen für jede SELECT-Abfrage ein, was nicht unbedingt einen Sinn ergibt, außer zu zeigen, dass Sie es können. Normalerweise möchten Sie die Verzögerung bei einigen anstößigen Abfragen verwenden. Nehmen wir an, wir haben eine Abfrage, die sehr schwer ist und die CPU der Datenbank erheblich belastet. Was noch schlimmer ist, es wurde durch eine kürzlich erfolgte Codeänderung eingeführt und kommt von allen Anwendungshosts. Sicher, Sie können warten, bis die Entwickler die Änderung rückgängig machen oder einen Fix bereitstellen, aber mit ProxySQL können Sie die Kontrolle selbst in die Hand nehmen und die Abfrage entweder blockieren oder ihre Auswirkungen sogar erheblich reduzieren.
Nehmen wir an, dass sich unsere Datenbank gut entwickelt, wenn die Alarmglocken zu läuten beginnen.
Ein kurzer Blick auf die Metriken sagt uns, dass die Anzahl der ausgeführten Abfragen von ProxySQL fällt aus, während die CPU-Auslastung steigt. Wir können uns die häufigsten Abfragen in ProxySQL ansehen, um zu sehen, ob uns etwas Ungewöhnliches auffällt.
Ungewöhnlich ist es in der Tat - eine neue Abfrage, die nicht Teil der ist regelmäßiger Abfragemix, den wir auf unserem System beobachtet haben. Wir können die Option verwenden, um die Abfrageregel zu erstellen.
Wir fügen der Abfrage eine Verzögerung von 50 Sekunden hinzu, indem wir Delay auf 50000 setzen Frau.
Wir können bestätigen, dass die Abfrageregel verwendet wird und Abfragen sie treffen .
Nach kurzer Zeit können wir auch feststellen, dass die Last sinkt und die Anzahl der ausgeführten Abfragen liegt wieder im erwarteten Bereich. Anstatt die Verzögerung der Abfrage hinzuzufügen, könnten wir sie natürlich auch einfach blockieren. Das wäre für uns noch einfacher gewesen, aber das vollständige Blockieren der Abfrage kann erhebliche Auswirkungen auf die Anwendung haben.
Wir hoffen, dass dieser kurze Blogbeitrag Ihnen einen Einblick gibt, wie ProxySQL Ihnen helfen kann, Ihren Datenverkehr zu gestalten und die durch außer Kontrolle geratene Abfragen verursachten Leistungseinbußen zu reduzieren.