A capacidade de moldar o tráfego que vai para o banco de dados é uma das mais importantes. Nos últimos dias, você não tinha tanto controle sobre isso - os aplicativos enviavam o tráfego para o banco de dados e é isso. O HAProxy, que era comumente usado, também não possui uma opção de controle refinado sobre o tráfego. Com a introdução dos proxies com reconhecimento de SQL, como o ProxySQL, mais possibilidades se tornaram disponíveis para administradores de banco de dados. Vamos dar uma olhada nas possibilidades de manipulação e limitação de conexão no ProxySQL.
Manuseio de conexão no ProxySQL
Como você deve saber, a forma como o ProxySQL funciona é através das regras de consulta. É uma lista de regras que cada consulta é testada e que governa exatamente como o ProxySQL irá lidar com a consulta. Desde o início, o aplicativo se conecta ao ProxySQL. Ele será autenticado no ProxySQL (é por isso que o ProxySQL precisa armazenar todos os usuários e hashes de senha) e, em seguida, o ProxySQL o executará através das regras de consulta para determinar para qual grupo de hosts a consulta deve ser enviada.
O ProxySQL abre um pool de conexões com os servidores back-end. Não é um mapeamento de 1 para 1, por padrão, ele tenta reutilizar uma conexão de back-end para o maior número possível de conexões de front-end. Isso é chamado de multiplexação de conexão. Os detalhes dependem do tráfego exato com o qual ele precisa lidar. Cada transação aberta deve ser tratada dentro da mesma conexão. Se houver algum tipo de variável local definida, essa conexão não poderá ser reutilizada. Ser capaz de reutilizar uma única conexão de back-end por várias conexões de front-end reduz significativamente a carga no banco de dados de back-end.
Uma vez feita a conexão com o ProxySQL, como mencionamos anteriormente, ela será processada de acordo com as regras de consulta. Aqui a modelagem de tráfego pode ocorrer. Vamos dar uma olhada nas opções
Limitação de conexão no ProxySQL
Primeiro, vamos eliminar todos os SELECTs. Estamos executando nosso “aplicativo”, Sysbench, da seguinte maneira:
[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
É um tráfego totalmente somente leitura, devendo em média 10 transações (140 consultas) por segundo. Como esses são apenas SELECTs, podemos facilmente modificar uma das regras de consulta existentes e bloquear o tráfego:
Isso resultará no seguinte erro no lado do aplicativo:
[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!!!
Agora, isso é obviamente duro. Podemos ser mais educados e apenas aumentar o atraso para as consultas SELECT.
Isso, obviamente, afeta o desempenho das consultas, pois 10 milissegundos são adicionados para cada SELECT que é executado.
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
Nós configuramos atrasos para cada consulta SELECT, o que não necessariamente faz sentido além de mostrar que você pode fazer isso. Normalmente, você gostaria de usar o atraso em algumas consultas ofensivas. Digamos que temos uma consulta muito pesada e que adiciona uma carga significativa na CPU do banco de dados. O que é pior, ele foi introduzido pela recente mudança de código e vem de todos os hosts do aplicativo. Claro, você pode esperar que os desenvolvedores revertam a mudança ou enviem uma correção, mas com o ProxySQL você pode assumir o controle em suas próprias mãos e simplesmente bloquear a consulta ou reduzir seu impacto de forma bastante significativa.
Vamos supor que nosso banco de dados esteja funcionando bem quando os alarmes começarem a tocar.
Uma rápida olhada nas métricas nos diz que o número de consultas executadas por O ProxySQL fica inativo enquanto a utilização da CPU aumenta. Podemos examinar as principais consultas no ProxySQL para ver se podemos notar algo incomum.
Incomum é de fato - uma nova consulta que não faz parte do combinação de consulta regular que observamos em nosso sistema. Podemos usar a opção para criar a regra de consulta.
Adicionaremos um atraso de 50 segundos à consulta configurando Delay para 50000 EM.
Podemos confirmar que a regra de consulta está em uso e as consultas estão atingindo-a .
Depois de um tempo, também podemos notar que a carga cai e o número de consultas executadas está novamente no intervalo esperado. Claro, em vez de adicionar o atraso à consulta, poderíamos simplesmente bloqueá-lo. Isso teria sido ainda mais fácil para nós, mas bloquear totalmente a consulta pode ter um impacto significativo no aplicativo.
Esperamos que esta breve postagem no blog forneça algumas dicas sobre como o ProxySQL pode ajudá-lo a moldar seu tráfego e reduzir o impacto no desempenho introduzido por consultas descontroladas.