Estudo de caso sobre atraso no banco de dados

Introdução ao histórico

Num sistema empresarial recente, a base de dados secundária está num estado atrasado e não consegue acompanhar a base de dados principal, resultando em maiores riscos empresariais. Do ponto de vista dos recursos, o uso de CPU, E/S e rede da biblioteca escrava é baixo e não há situação em que a reprodução seja retardada por pressão excessiva do servidor. A reprodução paralela está habilitada na biblioteca escrava. biblioteca mostra que não há threads de reprodução Bloqueio, a reprodução continua; analisando o arquivo de log de retransmissão, verifica-se que não há reprodução de transações grandes.

análise de processo

Confirmação do fenômeno

Recebi feedback de meus colegas de operação e manutenção de que um conjunto de bibliotecas escravas estava muito atrasado. Forneci show slave statusinformações de captura de tela sobre o atraso.

arquivo

Depois de continuar observando show slave statusas mudanças por um tempo, descobri que as informações do ponto POS mudavam constantemente, Seconds_Behind_master também mudava constantemente e a tendência geral continuava a crescer.

Uso de recursos

Depois de observar o uso de recursos do servidor, podemos perceber que o uso é muito baixo.

arquivo

Observando o processo escravo, você basicamente pode ver apenas um thread reproduzindo o trabalho.

arquivo

Descrição do parâmetro de reprodução paralela

Situado na biblioteca principalbinlog_transaction_dependency_tracking=WRITESET

Na biblioteca de escravos, slave_parallel_type=LOGICAL_CLOCKo eslave_parallel_workers=64

comparação de log de erros

Obtenha o log de reprodução paralela do log de erros para análise

$ grep 010559 100werror3306.log | tail -n 3
2024-01-31T14:07:50.172007+08:00 6806 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel 'cluster': seconds elapsed = 120; events assigned = 3318582273; worker queues filled over overrun level = 207029; waite
d due a Worker queue full = 238; waited due the total size = 0; waited at clock conflicts = 348754579743300 waited (count) when Workers occupied = 34529247 waited when Workers occupied = 76847369713200

2024-01-31T14:09:50.078829+08:00 6806 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel 'cluster': seconds elapsed = 120; events assigned = 3319256065; worker queues filled over overrun level = 207029; waite
d due a Worker queue full = 238; waited due the total size = 0; waited at clock conflicts = 348851330164000 waited (count) when Workers occupied = 34535857 waited when Workers occupied = 76866419841900

2024-01-31T14:11:50.060510+08:00 6806 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel 'cluster': seconds elapsed = 120; events assigned = 3319894017; worker queues filled over overrun level = 207029; waite
d due a Worker queue full = 238; waited due the total size = 0; waited at clock conflicts = 348943740455400 waited (count) when Workers occupied = 34542790 waited when Workers occupied = 76890229805500

Para obter uma explicação detalhada das informações acima, consulte Monitoramento de desempenho do MTS Quanto você sabe?

Removidas as estatísticas que ocorriam com menos frequência e mostramos a comparação de alguns dados importantes.

arquivo

Pode-se descobrir que no tempo natural de 120, o encadeamento de coordenação de reprodução espera mais de 90 segundos porque não pode reproduzir em paralelo e quase 20 segundos porque não há encadeamentos de trabalho ociosos para esperar. Isso se traduz em apenas cerca de 10 segundos. para que o thread de coordenação funcione.

Estatísticas de paralelismo

Como todos sabemos, a reprodução paralela do MySQL da biblioteca depende principalmente do last_committed no binlog para fazer julgamentos. Se o last_committed da transação for o mesmo, pode-se basicamente considerar que essas transações podem ser reproduzidas em paralelo. as estatísticas aproximadas de obtenção de um log de retransmissão do ambiente para reprodução paralela.

$ mysqlsqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1
; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>=1 && $2 <11){sum+=$2}} END {print sum}' 
235703
$ mysqlsqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1
; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>10){sum+=$2}} END {print sum}'
314694

O primeiro comando acima conta o número de transações com o mesmo last_commit entre 1 e 10, ou seja, o grau de reprodução paralela é baixo ou não pode ser reproduzido em paralelo. O número total dessas transações é 235703, representando 43%. Análise detalhada das transações com um grau relativamente baixo de reprodução paralela A partir da distribuição da transação, pode-se ver que esta parte do last_committed é basicamente uma única transação. Eles precisam aguardar a conclusão da reprodução da transação pré-encomendada antes de poderem reproduzi-lo. Isso fará com que a espera do encadeamento de coordenação observada no log anterior seja incapaz de reproduzir em paralelo e entre no estado de espera. Quando o tempo for relativamente longo

$ mysqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>=1 && $2 <11) {print $2}}' | sort | uniq -c
 200863 1
  17236 2
     98 3
     13 4
      3 5
      1 7

O segundo comando conta o número total de transações com mais de 10 transações last_committed iguais. O número é 314694, representando 57%. Ele analisa essas transações com um grau relativamente alto de reprodução paralela em detalhes. entre 6.500 e 9.000. número de transações

$ mysqlsqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1
; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>11){print $0}}' | column -t
last_commited  group_count  Percentage
1              7340         1.33%
11938          7226         1.31%
23558          7249         1.32%
35248          6848         1.24%
46421          7720         1.40%
59128          7481         1.36%
70789          7598         1.38%
82474          6538         1.19%
93366          6988         1.27%
104628         7968         1.45%
116890         7190         1.31%
128034         6750         1.23%
138849         7513         1.37%
150522         6966         1.27%
161989         7972         1.45%
175599         8315         1.51%
189320         8235         1.50%
202845         8415         1.53%
218077         8690         1.58%
234248         8623         1.57%
249647         8551         1.55%
264860         8958         1.63%
280962         8900         1.62%
297724         8768         1.59%
313092         8620         1.57%
327972         9179         1.67%
344435         8416         1.53%
359580         8924         1.62%
375314         8160         1.48%
390564         9333         1.70%
407106         8637         1.57%
422777         8493         1.54%
438500         8046         1.46%
453607         8948         1.63%
470939         8553         1.55%
486706         8339         1.52%
503562         8385         1.52%
520179         8313         1.51%
535929         7546         1.37%

Introdução ao mecanismo last_commited

Os parâmetros da biblioteca principal binlog_transaction_dependency_trackingsão usados ​​para especificar como gerar as informações de dependência gravadas no log binário para ajudar a biblioteca escrava a determinar quais transações podem ser executadas em paralelo. Ou seja, este parâmetro é usado para controlar o mecanismo de geração de last_committed. Os valores opcionais do parâmetro são COMMIT_ORDER, WRITESET e SESSION_WRITESET. No código a seguir, é fácil ver os três relacionamentos de parâmetros:

  1. O algoritmo básico é COMMIT_ORDER
  2. O algoritmo WRITESET é calculado novamente com base em COMMIT_ORDER
  3. O algoritmo SESSION_WRITESET é calculado novamente com base em WRITESET

arquivo

Como minha instância está definida como WRITESET, concentre-se apenas no algoritmo COMMIT_ORDER e no algoritmo WRITESET.

COMMIT_ORDER

Regra de cálculo COMMIT_ORDER: Se duas transações forem enviadas ao mesmo tempo no nó mestre, significa que não há conflito entre os dados das duas transações, então elas também podem ser executadas em paralelo no nó escravo. Um caso típico ideal. é o seguinte.

sessão 1 sessão-2
COMEÇAR COMEÇAR
INSERIR valores t1 (1)
INSERIR valores t2 (2)
confirmar (grupo_commit) confirmar (grupo_commit)

Mas para o MySQL, group_commit é um comportamento interno, desde que a sessão 1 e a sessão 2 executem o commit ao mesmo tempo, independentemente de serem mescladas internamente no group_commit, os dados das duas transações são essencialmente livres de conflitos; um passo atrás, contanto que a sessão 1 execute o commit e nenhum dado novo seja gravado na sessão 2, as duas transações ainda não terão conflitos de dados e ainda poderão ser replicadas em paralelo.

sessão 1 sessão-2
COMEÇAR COMEÇAR
INSERIR valores t1 (1)
INSERIR valores t2 (2)
comprometer-se
comprometer-se

Para cenários com mais threads simultâneos, esses threads podem não conseguir replicar em paralelo ao mesmo tempo, mas algumas transações podem. Tomando a sequência de execução a seguir como exemplo, após a confirmação da sessão 3, a sessão 2 não tem novas gravações, portanto as duas transações podem ser replicadas em paralelo; após a confirmação da sessão 3, a sessão 1 insere novos dados, o conflito de dados não pode ser determinado neste momento, portanto, as transações da sessão 3 e da sessão 1 não podem ser replicadas em paralelo; mas após o envio da sessão 2, nenhum dado novo é gravado após a sessão 1, portanto, a sessão 2 e a sessão 1 são novamente Podem ser replicados em paralelo. Portanto, neste cenário, a sessão 2 pode ser replicada em paralelo com a sessão 1 e a sessão 3 respectivamente, mas as três transações não podem ser replicadas em paralelo ao mesmo tempo.

sessão 1 sessão-2 sessão-3
COMEÇAR COMEÇAR COMEÇAR
INSERIR valores t1 (1) INSERIR valores t2 (1) INSERIR valores t3 (1)
INSERIR valores t1 (2) INSERIR valores t2 (2)
comprometer-se
INSERIR valores t1 (3)
comprometer-se
comprometer-se

ESCREVER

Na verdade, é uma combinação de commit_order + writeset. Ele primeiro calculará um valor last_committed por meio de commit_order, depois calculará um novo valor por meio de writeset e, finalmente, tomará o valor menor entre os dois como o valor last_committed da transação final gtid.

No MySQL, writeset é essencialmente um valor hash calculado para schema_name + table_name + Primary_key/unique_key Durante a execução da instrução DML, antes de gerar row_event por meio de binlog_log_row, todas as chaves primárias/chaves exclusivas na instrução DML terão valores de hash calculados. separadamente e adicionado à lista writeset da própria transação. E se houver uma tabela sem chave primária/índice exclusivo, has_missing_keys=true também será definido para a transação.

O parâmetro está definido como WRITESET, mas não pode ser usado. As restrições são as seguintes.

  1. Instruções ou tabelas não DDL com chaves primárias ou chaves exclusivas ou transações vazias
  2. O algoritmo hash usado pela sessão atual é consistente com o do mapa hash.
  3. Nenhuma chave estrangeira usada
  4. A capacidade do mapa hash não excede a configuração de binlog_transaction_dependency_history_size Quando as quatro condições acima forem atendidas, o algoritmo WRITESET pode ser usado. Se alguma das condições não for atendida, ele degenerará para o método de cálculo COMMIT_ORDER.

arquivo

O algoritmo WRITESET específico é o seguinte, quando a transação é enviada:

  1. last_committed está definido como m_writeset_history_start, este valor é o menor número_sequencial na lista m_writeset_history

  2. Percorra a lista de transações do writeset

    a Se um writeset não existir no m_writeset_history global, construa um objeto pair<writeset, sequence_number> da transação atual e insira-o na lista global m_writeset_history

    b. Se existir, last_committed=max(last_committed, o valor de sequence_number do writeset histórico) e, ao mesmo tempo, atualize o sequence_number correspondente ao writeset em m_writeset_history para o valor da transação atual

  3. Se has_missing_keys=false, ou seja, todas as tabelas de dados da transação contêm chaves primárias ou índices exclusivos, então o valor mínimo calculado por commit_order e writeset será usado como o valor final last_committed.

arquivo

DICAS: Com base nas regras WRITESET acima, haverá uma situação em que o last_committed da transação enviada posteriormente será menor do que a transação enviada primeiro.

Análise de conclusão

Descrição da conclusão

De acordo com as restrições de uso do WRITESET, comparamos o log de retransmissão e as estruturas da tabela envolvidas na transação, analisamos a composição da transação do único last_committed e encontramos as duas situações a seguir:

  1. Há um conflito de dados entre os dados envolvidos na única transação last_committed e no sequence_number.
  2. A tabela envolvida em uma única transação last_committed não possui chave primária e existem muitas dessas transações.

A partir da análise acima, pode-se concluir que há muitas transações na tabela sem uma chave primária, fazendo com que WRITESET degenere em COMMIT_ORDER.Como o banco de dados é um aplicativo TP, as transações são enviadas rapidamente e o envio de múltiplas transações não pode ser garantido estar dentro de um ciclo de confirmação, resultando em COMMIT_ORDER As leituras repetidas last_committed geradas pelo mecanismo são muito baixas. A biblioteca escrava só pode reproduzir essas transações em série, causando atrasos na reprodução.

Medidas de otimização

  1. Modifique as tabelas do lado comercial e adicione chaves primárias às tabelas relacionadas sempre que possível.
  2. Tente aumentar os parâmetros binlog_group_commit_sync_delay e binlog_group_commit_sync_no_delay_count de 0 a 10.000. Devido a restrições ambientais especiais, esse ajuste não entra em vigor. Cenários diferentes podem ter desempenhos diferentes.

Aproveite o GreatSQL :)

Sobre GreatSQL

GreatSQL é um banco de dados doméstico independente de código aberto adequado para aplicativos de nível financeiro. Possui muitos recursos básicos, como alto desempenho, alta confiabilidade, alta facilidade de uso e alta segurança. Ele pode ser usado como um substituto opcional para MySQL ou Percona Server e é utilizado em ambientes de produção online, totalmente gratuito e compatível com MySQL ou Percona Server.

Links relacionados: Guia da comunidade GreatSQL GitHub Bilibili

Comunidade GreatSQL:

imagem

Sugestões e feedback de recompensas da comunidade: https://greatsql.cn/thread-54-1-1.html

Detalhes do envio do prêmio do blog da comunidade: https://greatsql.cn/thread-100-1-1.html

(Se você tiver alguma dúvida sobre o artigo ou tiver ideias exclusivas, você pode acessar o site oficial da comunidade para perguntar ou compartilhá-las ~)

Grupo de intercâmbio técnico:

Grupo WeChat e QQ:

Grupo QQ: 533341697

Grupo WeChat: Adicione o GreatSQL Community Assistant (WeChat ID:) wanlidbccomo amigo e espere que o assistente da comunidade adicione você ao grupo.

A primeira grande atualização de versão do JetBrains 2024 (2024.1) é de código aberto. Até a Microsoft planeja pagar por isso. Por que ainda está sendo criticado por ser de código aberto? [Recuperado] O back-end do Tencent Cloud travou: um grande número de erros de serviço e nenhum dado após o login no console. A Alemanha também precisa ser "controlável de forma independente". O governo estadual migrou 30.000 PCs do Windows para o Linux deepin-IDE e finalmente conseguiu inicialização! O Visual Studio Code 1.88 foi lançado. Bom rapaz, a Tencent realmente transformou o Switch em uma "máquina de aprendizagem pensante". A área de trabalho remota RustDesk inicia e reconstrói o cliente Web. O banco de dados de terminal de código aberto do WeChat baseado em SQLite, WCDB, recebeu uma grande atualização.
{{o.nome}}
{{m.nome}}

Acho que você gosta

Origin my.oschina.net/GreatSQL/blog/11052043
Recomendado
Clasificación