Na primeira parte deste blog, fornecemos uma visão geral do novo recurso Streaming Replication no MySQL Galera Cluster. Neste blog, mostraremos como habilitá-lo e dar uma olhada nos resultados.
Ativando a replicação de streaming
É altamente recomendável habilitar a Replicação de Streaming em nível de sessão para as transações específicas que interagem com seu aplicativo/cliente.
Como dito no blog anterior, Galera registra seus conjuntos de gravação na tabela wsrep_streaming_log no banco de dados MySQL. Isso tem o potencial de criar um gargalo de desempenho, especialmente quando uma reversão é necessária. Isso não significa que você não pode usar a Replicação de Streaming, significa apenas que você precisa projetar seu cliente de aplicativo com eficiência ao usar a Replicação de Streaming para obter melhor desempenho. Ainda assim, é melhor ter Replicação de Streaming para lidar e reduzir grandes transações.
Ativar a replicação de streaming requer que você defina a unidade de replicação e o número de unidades a serem usadas na formação dos fragmentos de transação. Dois parâmetros controlam essas variáveis:wsrep_trx_fragment_unit e wsrep_trx_fragment_size.
Abaixo está um exemplo de como definir esses dois parâmetros:
SET SESSION wsrep_trx_fragment_unit='statements';
SET SESSION wsrep_trx_fragment_size=3;
Neste exemplo, o fragmento é definido com três instruções. Para cada três instruções de uma transação, o nó irá gerar, replicar e certificar um fragmento.
Você pode escolher entre algumas unidades de replicação ao formar fragmentos:
- bytes - Isso define o tamanho do fragmento em bytes.
- linhas - Isso define o tamanho do fragmento como o número de linhas que o fragmento atualiza.
- declarações - Isso define o tamanho do fragmento como o número de instruções em um fragmento.
Escolha a unidade de replicação e o tamanho do fragmento que melhor se adequa à operação específica que deseja executar.
Replicação de streaming em ação
Conforme discutido em nosso outro blog sobre como lidar com grandes transações no Mariadb 10.4, realizamos e testamos o desempenho da Replicação de Streaming quando habilitada com base neste critério...
- Linha de base, definir global wsrep_trx_fragment_size=0;
- definir global wsrep_trx_fragment_unit='rows'; definir global wsrep_trx_fragment_size=1;
- definir global wsrep_trx_fragment_unit='statements'; definir global wsrep_trx_fragment_size=1;
- definir global wsrep_trx_fragment_unit='statements'; definir global wsrep_trx_fragment_size=5;
E os resultados são
Transactions: 82.91 per sec., queries: 1658.27 per sec. (100%)
Transactions: 54.72 per sec., queries: 1094.43 per sec. (66%)
Transactions: 54.76 per sec., queries: 1095.18 per sec. (66%)
Transactions: 70.93 per sec., queries: 1418.55 per sec. (86%)
Para este exemplo, estamos usando o Percona XtraDB Cluster 8.0.15 diretamente de sua ramificação de teste usando o Percona-XtraDB-Cluster_8.0.15.5-27dev.4.2_Linux.x86_64.ssl102.tar.gz construir.
Tentamos então um cluster Galera de 3 nós com informações de hosts abaixo:
testnode11 = 192.168.10.110
testnode12 = 192.168.10.120
testnode13 = 192.168.10.130
Preenchemos uma tabela do meu banco de dados sysbench e tentamos excluir linhas muito grandes.
[email protected][sbtest]#> select count(*) from sbtest1;
+----------+
| count(*) |
+----------+
| 12608218 |
+----------+
1 row in set (25.55 sec)
No início, executando sem Streaming Replication,
[email protected][sbtest]#> select @@wsrep_trx_fragment_unit, @@wsrep_trx_fragment_size, @@innodb_lock_wait_timeout;
+---------------------------+---------------------------+----------------------------+
| @@wsrep_trx_fragment_unit | @@wsrep_trx_fragment_size | @@innodb_lock_wait_timeout |
+---------------------------+---------------------------+----------------------------+
| bytes | 0 | 50000 |
+---------------------------+---------------------------+----------------------------+
1 row in set (0.00 sec)
Em seguida, execute,
[email protected][sbtest]#> delete from sbtest1 where id >= 2000000;
No entanto, acabamos recebendo uma reversão...
---TRANSACTION 648910, ACTIVE 573 sec rollback
mysql tables in use 1, locked 1
ROLLING BACK 164858 lock struct(s), heap size 18637008, 12199395 row lock(s), undo log entries 11961589
MySQL thread id 183, OS thread handle 140041167468288, query id 79286 localhost 127.0.0.1 root wsrep: replicating and certifying write set(-1)
delete from sbtest1 where id >= 2000000
Usando o ClusterControl Dashboards para obter uma visão geral de qualquer indicação de controle de fluxo, já que a transação é executada apenas no nó mestre (gravador ativo) até o momento do commit, não há nenhuma indicação de atividade para controle de fluxo:
Caso você esteja se perguntando, a versão atual do ClusterControl ainda não tem suporte direto para PXC 8.0 com Galera Cluster 4 (pois ainda é experimental). Você pode, no entanto, tentar importá-lo... mas ele precisa de pequenos ajustes para que seus painéis funcionem corretamente.
Volte ao processo de consulta. Ele falhou quando rolou para trás!
[email protected][sbtest]#> delete from sbtest1 where id >= 2000000;
ERROR 1180 (HY000): Got error 5 - 'Transaction size exceed set threshold' during COMMIT
independentemente de wsrep_max_ws_rows ou wsrep_max_ws_size,
[email protected][sbtest]#> select @@global.wsrep_max_ws_rows, @@global.wsrep_max_ws_size/(1024*1024*1024);
+----------------------------+---------------------------------------------+
| @@global.wsrep_max_ws_rows | @@global.wsrep_max_ws_size/(1024*1024*1024) |
+----------------------------+---------------------------------------------+
| 0 | 2.0000 |
+----------------------------+---------------------------------------------+
1 row in set (0.00 sec)
Ele, eventualmente, atingiu o limite.
Durante este tempo a tabela de sistema mysql.wsrep_streaming_log está vazia, o que indica que a Replicação de Streaming não está acontecendo ou habilitada,
[email protected][sbtest]#> select count(*) from mysql.wsrep_streaming_log;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.01 sec)
[email protected][sbtest]#> select count(*) from mysql.wsrep_streaming_log;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
e isso é verificado nos outros 2 nós (testnode12 e testnode13).
Agora, vamos tentar ativá-lo com a replicação de streaming,
[email protected][sbtest]#> select @@wsrep_trx_fragment_unit, @@wsrep_trx_fragment_size, @@innodb_lock_wait_timeout;
+---------------------------+---------------------------+----------------------------+
| @@wsrep_trx_fragment_unit | @@wsrep_trx_fragment_size | @@innodb_lock_wait_timeout |
+---------------------------+---------------------------+----------------------------+
| bytes | 0 | 50000 |
+---------------------------+---------------------------+----------------------------+
1 row in set (0.00 sec)
[email protected][sbtest]#> set wsrep_trx_fragment_unit='rows'; set wsrep_trx_fragment_size=100;
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
[email protected][sbtest]#> select @@wsrep_trx_fragment_unit, @@wsrep_trx_fragment_size, @@innodb_lock_wait_timeout;
+---------------------------+---------------------------+----------------------------+
| @@wsrep_trx_fragment_unit | @@wsrep_trx_fragment_size | @@innodb_lock_wait_timeout |
+---------------------------+---------------------------+----------------------------+
| rows | 100 | 50000 |
+---------------------------+---------------------------+----------------------------+
1 row in set (0.00 sec)
O que esperar quando o Galera Cluster Streaming Replication está habilitado?
Quando a consulta foi realizada em testnode11,
[email protected][sbtest]#> delete from sbtest1 where id >= 2000000;
O que acontece é que ele fragmenta a transação peça por peça dependendo do valor definido da variável wsrep_trx_fragment_size. Vamos verificar isso nos outros nós:
Host testnode12
[email protected][sbtest]#> pager sed -n '/TRANSACTIONS/,/FILE I\/O/p'; show engine innodb status\G nopager; show global status like 'wsrep%flow%'; select count(*) from mysql.wsrep_streaming_log;
PAGER set to 'sed -n '/TRANSACTIONS/,/FILE I\/O/p''
TRANSACTIONS
------------
Trx id counter 567148
Purge done for trx's n:o < 566636 undo n:o < 0 state: running but idle
History list length 44
LIST OF TRANSACTIONS FOR EACH SESSION:
..
...
---TRANSACTION 421740651985200, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 553661, ACTIVE 190 sec
18393 lock struct(s), heap size 2089168, 1342600 row lock(s), undo log entries 1342600
MySQL thread id 898, OS thread handle 140266050008832, query id 216824 wsrep: applied write set (-1)
--------
FILE I/O
1 row in set (0.08 sec)
PAGER set to stdout
+----------------------------------+--------------+
| Variable_name | Value |
+----------------------------------+--------------+
| wsrep_flow_control_paused_ns | 211197844753 |
| wsrep_flow_control_paused | 0.133786 |
| wsrep_flow_control_sent | 633 |
| wsrep_flow_control_recv | 878 |
| wsrep_flow_control_interval | [ 173, 173 ] |
| wsrep_flow_control_interval_low | 173 |
| wsrep_flow_control_interval_high | 173 |
| wsrep_flow_control_status | OFF |
+----------------------------------+--------------+
8 rows in set (0.00 sec)
+----------+
| count(*) |
+----------+
| 13429 |
+----------+
1 row in set (0.04 sec)
Host testnode13
[email protected][sbtest]#> pager sed -n '/TRANSACTIONS/,/FILE I\/O/p'; show engine innodb status\G nopager; show global status like 'wsrep%flow%'; select count(*) from mysql.wsrep_streaming_log;
PAGER set to 'sed -n '/TRANSACTIONS/,/FILE I\/O/p''
TRANSACTIONS
------------
Trx id counter 568523
Purge done for trx's n:o < 567824 undo n:o < 0 state: running but idle
History list length 23
LIST OF TRANSACTIONS FOR EACH SESSION:
..
...
---TRANSACTION 552701, ACTIVE 216 sec
21587 lock struct(s), heap size 2449616, 1575700 row lock(s), undo log entries 1575700
MySQL thread id 936, OS thread handle 140188019226368, query id 600980 wsrep: applied write set (-1)
--------
FILE I/O
1 row in set (0.28 sec)
PAGER set to stdout
+----------------------------------+--------------+
| Variable_name | Value |
+----------------------------------+--------------+
| wsrep_flow_control_paused_ns | 210755642443 |
| wsrep_flow_control_paused | 0.0231273 |
| wsrep_flow_control_sent | 1653 |
| wsrep_flow_control_recv | 3857 |
| wsrep_flow_control_interval | [ 173, 173 ] |
| wsrep_flow_control_interval_low | 173 |
| wsrep_flow_control_interval_high | 173 |
| wsrep_flow_control_status | OFF |
+----------------------------------+--------------+
8 rows in set (0.01 sec)
+----------+
| count(*) |
+----------+
| 15758 |
+----------+
1 row in set (0.03 sec)
Notavelmente, o controle de fluxo acabou de entrar em ação!
E as filas WSREP enviadas/recebidas também estão em alta:
Host testnode12 (192.168.10.120) Host testnode13 (192.168.10.130)
Agora, vamos elaborar mais sobre o resultado da tabela mysql.wsrep_streaming_log,
[email protected][sbtest]#> pager sed -n '/TRANSACTIONS/,/FILE I\/O/p'|tail -8; show engine innodb status\G nopager;
PAGER set to 'sed -n '/TRANSACTIONS/,/FILE I\/O/p'|tail -8'
MySQL thread id 134822, OS thread handle 140041167468288, query id 0 System lock
---TRANSACTION 649008, ACTIVE 481 sec
mysql tables in use 1, locked 1
53104 lock struct(s), heap size 6004944, 3929602 row lock(s), undo log entries 3876500
MySQL thread id 183, OS thread handle 140041167468288, query id 105367 localhost 127.0.0.1 root updating
delete from sbtest1 where id >= 2000000
--------
FILE I/O
1 row in set (0.01 sec)
então pegando o resultado de,
[email protected][sbtest]#> select count(*) from mysql.wsrep_streaming_log;
+----------+
| count(*) |
+----------+
| 38899 |
+----------+
1 row in set (0.40 sec)
Informa quanto fragmento foi replicado usando Streaming Replication. Agora, vamos fazer algumas contas básicas:
[email protected][sbtest]#> select 3876500/38899.0;
+-----------------+
| 3876500/38899.0 |
+-----------------+
| 99.6555 |
+-----------------+
1 row in set (0.03 sec)
Estou pegando as entradas de log de undo do resultado SHOW ENGINE INNODB STATUS\G e então divido a contagem total dos registros mysql.wsrep_streaming_log. Como configurei anteriormente, defini wsrep_trx_fragment_size=100. O resultado mostrará quanto o total de logs replicados está sendo processado pelo Galera.
É importante observar o que o Streaming Replication está tentando alcançar... "o nó quebra a transação em fragmentos, depois os certifica e os replica nos escravos enquanto a transação ainda está em progresso. Uma vez certificado, o fragmento não pode mais ser abortado por transações conflitantes."
Os fragmentos são considerados transações, que foram passadas para os nós restantes dentro do cluster, certificando a transação fragmentada e, em seguida, aplicando os conjuntos de gravação. Isso significa que, uma vez que sua grande transação tenha sido certificada ou priorizada, todas as conexões de entrada que possam ter um deadlock precisarão esperar até que as transações sejam concluídas.
Agora, o veredicto de deletar uma tabela enorme?
[email protected][sbtest]#> delete from sbtest1 where id >= 2000000;
Query OK, 12034538 rows affected (30 min 36.96 sec)
Ele termina com sucesso sem nenhuma falha!
Como é nos outros nós? Em testnode12,
[email protected][sbtest]#> pager sed -n '/TRANSACTIONS/,/FILE I\/O/p'|tail -8; show engine innodb status\G nopager; show global status like 'wsrep%flow%'; select count(*) from mysql.wsrep_streaming_log;
PAGER set to 'sed -n '/TRANSACTIONS/,/FILE I\/O/p'|tail -8'
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 421740651985200, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 553661, ACTIVE (PREPARED) 2050 sec
165631 lock struct(s), heap size 18735312, 12154883 row lock(s), undo log entries 12154883
MySQL thread id 898, OS thread handle 140266050008832, query id 341835 wsrep: preparing to commit write set(215510)
--------
FILE I/O
1 row in set (0.46 sec)
PAGER set to stdout
+----------------------------------+--------------+
| Variable_name | Value |
+----------------------------------+--------------+
| wsrep_flow_control_paused_ns | 290832524304 |
| wsrep_flow_control_paused | 0 |
| wsrep_flow_control_sent | 0 |
| wsrep_flow_control_recv | 0 |
| wsrep_flow_control_interval | [ 173, 173 ] |
| wsrep_flow_control_interval_low | 173 |
| wsrep_flow_control_interval_high | 173 |
| wsrep_flow_control_status | OFF |
+----------------------------------+--------------+
8 rows in set (0.53 sec)
+----------+
| count(*) |
+----------+
| 120345 |
+----------+
1 row in set (0.88 sec)
Ele para em um total de 120345 fragmentos, e se fizermos as contas novamente nas últimas entradas de log de desfazer capturadas (os logs de desfazer são os mesmos do mestre também),
[email protected][sbtest]#> select 12154883/120345.0; +-------------------+
| 12154883/120345.0 |
+-------------------+
| 101.0003 |
+-------------------+
1 row in set (0.00 sec)
Então tivemos um total de 120345 transações sendo fragmentadas para excluir 12034538 linhas.
Ao terminar de usar ou habilitar a Replicação de Stream, não se esqueça de desativá-la, pois ela sempre registrará grandes transações e adiciona muita sobrecarga de desempenho ao cluster. Para desativá-lo, basta executar
[email protected][sbtest]#> set wsrep_trx_fragment_size=0;
Query OK, 0 rows affected (0.04 sec)
Conclusão
Com a Replicação de Streaming habilitada, é importante que você consiga identificar o tamanho do seu fragmento e qual unidade você deve escolher (bytes, linhas, instruções).
Também é muito importante que você precise executá-lo no nível da sessão e, claro, identificar quando você só precisa usar a Replicação de Streaming.
Ao realizar esses testes, a exclusão de um grande número de linhas em uma tabela enorme com a Replicação de streaming habilitada causou um alto pico de utilização do disco e da CPU. A RAM era mais estável, mas isso pode, devido à declaração que realizamos, não ser uma contenção de memória muito alta.
É seguro dizer que a Replicação de Streaming pode causar gargalos de desempenho ao lidar com registros grandes, portanto, seu uso deve ser feito com cuidado e decisão adequados.
Por último, se você estiver usando a Replicação de Streaming, não se esqueça de sempre desativá-la uma vez feito na sessão atual para evitar problemas indesejados.