Com a chegada das versões LTS, upgrades para versões mais recentes se tornam cada vez mais comuns, visto que, em média, a cada dois anos teremos uma nova versão LTS. Com novas versões e novos comandos, o mais importante são os comandos obsoletos ou novas palavras reservadas, que causarão problemas se não forem verificados previamente.
Comumente, replicações fazem um pouco desse trabalho com os DDL e DML executados via replicação, mas e os selects e comandos que não são escritos no binlog? Como verificar?
Neste blog, iremos explicar como podemos testar diferentes tipos de queries antes de realizar a migração utilizando o ProxySQL mirror.
Ambiente / Premissas
Para esse blog a seguir, favor considerar as seguintes informações de servidor:
|
1 2 3 4 5 6 7 8 |
ProxySQL > SELECT hostgroup_id,hostname,comment FROM mysql_servers; +--------------+-------------+-----------+ | hostgroup_id | hostname | comment. | +--------------+-------------+-----------+ | 1 | 172.31.16.2 | MySQL 8.0 | | 2 | 172.31.16.3 | MySQL 8.4 | +--------------+-------------+-----------+ 2 rows in set (0.00 sec) |
- MySQL 8.0 no hostgroup_id=1 — produção, recebe e responde a aplicação.
- MySQL 8.4 no hostgroup_id=2 — servidor rodando a nova versão, recebe os selects do tráfego via mirror.
- ProxySQL ≥ 2.7 na frente da aplicação.
- Réplica 8.4 sincronizada: replicação async do 8.0 para o 8.4.
- Ressalva: configuração básica do ProxySQL (instalação, mysql_users, mysql_servers) não será coberta neste post.
Como o ProxySQL Mirror funciona
O ProxySQL permite via configuração na tabela mysql_query_rules duplicar uma query que será roteada para o hostgroup de produção para outro hostgroup. O cliente irá eceber apenas a resposta do hostgroup principal, nesse caso o valor cadastrado na coluna destination_hostgroup, a cópia é executada mas o ProxySQL não irá devolver o resultado da execução para o cliente.
Configurando a regra de mirror
Para configurar as regras de mirror no ProxySQL existem 2 colunas na tabela mysql_query_rules:
mirror_hostgroup: hostgroup que receberá a cópia da query.
mirror_flagOUT: |Opcional| Define a flagIN que a query mirrorada vai assumir ao reentrar na engine de mysql_query_rules — útil para aplicar rewrites ou regras específicas só no espelho.
No exemplo abaixo iremos apenas utilizar a coluna mirror_hostgroup:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ProxySQL > INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,mirror_hostgroup) VALUES (1,1,' ^SELECT .* ',1,2); Query OK, 1 row affected (0.00 sec) MySQL > LOAD MYSQL QUERY RULES TO RUNTIME;SAVE MYSQL QUERY RULES TO DISK; Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.05 sec) ProxySQL > SELECT rule_id,active,match_pattern,mirror_hostgroup FROM runtime_mysql_query_rules WHERE rule_id=1; +---------+--------+---------------+------------------+ | rule_id | active | match_pattern | mirror_hostgroup | +---------+--------+---------------+------------------+ | 1 | 1 | ^SELECT .* | 2 | +---------+--------+---------------+------------------+ 1 row in set (0.00 sec) |
Para verificar a execução em ambos nós, iremos utilizar o slow query log com as seguintes configurações:
|
1 2 3 4 5 6 7 |
MySQL > SELECT @@slow_query_log,@@long_query_time; +------------------+-------------------+ | @@slow_query_log | @@long_query_time | +------------------+-------------------+ | 1 | 0.000000 | +------------------+-------------------+ 1 row in set (0.00 sec) |
Após executar o seguinte select via ProxySQL:
|
1 2 3 |
MySQL > SELECT * FROM test.joinit; [...] 32 rows in set (0.00 sec) |
É possível verificar o mesmo select no slow query log de ambos servers:
MySQL 8.0:
|
1 2 3 4 |
User@Host: root[root] @ blog-roberto-garciadebem-node2.blog-roberto-garciadebem-anydbver [172.31.16.4] Id: 327896 Schema: information_schema Last_errno: 0 Killed: 0 Query_time: 0.000242 Lock_time: 0.000003 Rows_sent: 32 Rows_examined: 32 Rows_affected: 0 Bytes_sent: 1971 SET timestamp=1777420505; SELECT * FROM test.joinit; |
MySQL 8.4:
|
1 2 3 4 |
User@Host: root[root] @ blog-roberto-garciadebem-node2.blog-roberto-garciadebem-anydbver [172.31.16.4] Id: 76 Schema: information_schema Last_errno: 0 Killed: 0 Query_time: 0.000266 Lock_time: 0.000002 Rows_sent: 32 Rows_examined: 32 Rows_affected: 0 Bytes_sent: 1971 SET timestamp=1777420505; SELECT * FROM test.joinit; |
No exemplo acima, conseguimos ver que o espelhamento está funcionando corretamente, porém, o que acontece se executarmos uma query que funcionará no MySQL 8.0, mas não funcionará no 8.4?
|
1 2 |
MySQL via ProxySQL > SELECT id,parallel FROM test.t; Empty set (0.00 sec) |
Iremos encontrar a query no slow query log do 8.0, mas não iremos encontrar a mesma no 8.4:
|
1 2 3 4 |
Time: 2026-04-29T00:15:12.961106Z User@Host: root[root] @ blog-roberto-garciadebem-node2.blog-roberto-garciadebem-anydbver [172.31.16.4] Id: 327896 Schema: information_schema Last_errno: 0 Killed: 0 Query_time: 0.000222 Lock_time: 0.000003 Rows_sent: 0 Rows_examined: 0 Rows_affected: 0 Bytes_sent: 100 SET timestamp=1777421712; SELECT id,parallel FROM test.t; |
Podemos então verificar nas tabelas de status do ProxySQL se houve algum erro nessa query:
|
1 2 3 4 5 6 7 |
mysql> select hostgroup,hostname,errno,last_error from stats_mysql_errors where hostgroup=2; +-----------+-------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | hostgroup | hostname | errno | last_error | +-----------+-------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | 2 | 172.31.16.3 | 1064 | You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'parallel FROM test.t' at line 1 | +-----------+-------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 rows in set (0.00 sec) |
Conforme visto acima, é possível verificar que houve um erro de sintaxe devido à palavra parallel, que no MySQL 8.4 se tornou uma palavra reservada.
Ok, encontramos o erro e tenho conhecimento de uma possível solução. Neste caso, podemos utilizar a configuração mirror_flagOUT para testar como a solução se comportaria no MySQL 8.4, mantendo os testes de workload. Por exemplo:
|
1 2 3 4 5 6 7 8 9 |
MySQL > UPDATE mysql_query_rules SET mirror_flagOUT=100,mirror_hostgroup=NULL WHERE rule_id=1; Query OK, 1 row affected (0.01 sec) MySQL > INSERT INTO mysql_query_rules (rule_id,active,flagIN,match_pattern,replace_pattern,destination_hostgroup) VALUES (3,1,100,'parallel','`parallel`',2); Query OK, 1 row affected (0.00 sec) MySQL > LOAD MYSQL QUERY RULES TO RUNTIME;SAVE MYSQL QUERY RULES TO DISK; Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) |
#Note que a coluna mirror_flagOUT precisará de uma query rule em que a coluna flagIn combine, para que a regra com flagIn seja utilizada na query espelhada. No exemplo abaixo, a query espelhada será executada no destination_hostgroup 2 e, onde estiver a palavra parallel, será reescrita com parallel, para que possamos utilizar a palavra reservada no MySQL 8.4.
|
1 2 3 4 5 6 7 8 |
MySQL > SELECT rule_id,active,flagIN,mirror_flagOUT,match_pattern,replace_pattern,mirror_hostgroup,destination_hostgroup FROM runtime_mysql_query_rules WHERE rule_id IN (1,3); +---------+--------+--------+----------------+---------------+-----------------+------------------+-----------------------+ | rule_id | active | flagIN | mirror_flagOUT | match_pattern | replace_pattern | mirror_hostgroup | destination_hostgroup | +---------+--------+--------+----------------+---------------+-----------------+------------------+-----------------------+ | 1 | 1 | 0 | 100 | ^SELECT .* | NULL | NULL | 1 | | 3 | 1 | 100 | NULL | parallel | `parallel` | NULL | 2 | +---------+--------+--------+----------------+---------------+-----------------+------------------+-----------------------+ 2 rows in set (0.00 sec) |
Feita a configuração, este é o resultado pós-execução do mesmo select no slow query log de ambos os serviços MySQL:
8.0:
|
1 2 3 4 |
# Time: 2026-04-29T00:30:38.017824Z # User@Host: root[root] @ blog-roberto-garciadebem-node2.blog-roberto-garciadebem-anydbver [172.31.16.4] Id: 327896 # Schema: information_schema Last_errno: 0 Killed: 0 # Query_time: 0.000180 Lock_time: 0.000003 Rows_sent: 0 Rows_examined: 0 Rows_affected: 0 Bytes_sent: 100 SET timestamp=1777422638; SELECT id,parallel FROM test.t; |
8.4:
|
1 2 3 4 |
# Time: 2026-04-29T00:30:38.018334Z # User@Host: root[root] @ blog-roberto-garciadebem-node2.blog-roberto-garciadebem-anydbver [172.31.16.4] Id: 76 # Schema: information_schema Last_errno: 0 Killed: 0 # Query_time: 0.000944 Lock_time: 0.000008 Rows_sent: 0 Rows_examined: 0 Rows_affected: 0 Bytes_sent: 100 SET timestamp=1777422638; SELECT id,`parallel` FROM test.t; |
Como possível ver acima a regra alterou como esperada a escrita da palavra parallel, o que permitiu a execução da query no MySQL 8.4.
Conclusão
ProxySQL Mirror não substitui a utilização do pt-upgrade ou do util.checkForServerUpgrade() no MySQL Shell, mas cobre uma lacuna importante: o tráfego real, incluindo comandos que não são replicados pelo binlog. Em poucas linhas de configuração você ganha um canal de teste contínuo contra a versão alvo do upgrade, e descobre problemas como o caso acima utilizado a palavra agora reservada ‘parallel’ ou outras sintaxes deprecadas quebrando no 8.4 antes do dia da virada.
Combine mirror + análise de stats_mysql_errors + checkForServerUpgrade e você irá cobrir possíveis surpresas em um upgrade no MySQL: schema, queries de aplicação e tráfego operacional.
Fonte:
https://dev.mysql.com/doc/refman/9.7/en/keywords.html
https://proxysql.com/documentation/mirroring/
https://proxysql.com/documentation/main-runtime/mysql-tables#mysql_query_rules
https://proxysql.com/documentation/the-admin-schemas/stats/stats-mysql#stats_mysql_errors
|
1 |





