Linhas quentes são registros de banco de dados modificados com alta frequência. Em cenários de alta concorrência, a atualização dessas linhas causa contenção severa de bloqueios de linha e longos tempos de espera, o que degrada o desempenho do sistema. Para resolver esse problema, o PolarDB implementa otimizações no nível do kernel do banco de dados e melhora significativamente o desempenho do sistema.
Contexto
As linhas quentes apresentam os seguintes desafios:
Quando uma transação atualiza uma linha de dados, ela adquire um bloqueio na linha de destino e não o libera até a confirmação ou reversão da transação. Durante esse período, apenas uma transação pode atualizar a linha, enquanto as demais precisam aguardar. Consequentemente, as solicitações de atualização para uma única linha quente são executadas serialmente. Estratégias tradicionais de fragmentação de banco de dados e tabelas oferecem melhorias limitadas de desempenho nesse cenário.
Nesses cenários, um grande volume de solicitações de atualização para linhas quentes pode atingir o sistema de banco de dados back-end em curto período. Esse comportamento gera contenção grave de bloqueios de linha e longos tempos de espera, degradando o desempenho do sistema. Uma espera prolongada por uma solicitação de atualização pode impactar significativamente as operações de negócios.
Apenas aumentar o hardware não atende a esses requisitos de baixa latência. Para solucionar isso, o PolarDB fornece otimizações inovadoras no nível do kernel do banco de dados. O sistema identifica automaticamente solicitações de atualização de linhas quentes e agrupa atualizações na mesma linha de dados ocorridas dentro de um intervalo de tempo específico. Diferentes grupos são então processados em paralelo usando um pipeline. Essas otimizações melhoram drasticamente o desempenho do sistema.
Solução técnica
-
Do processamento sequencial para pipeline
O processamento paralelo é a maneira mais direta de melhorar o desempenho do banco de dados, mas paralelizar totalmente as operações de atualização na mesma linha quente é difícil. O PolarDB utiliza uma abordagem inovadora de processamento em pipeline para maximizar o paralelismo das operações de atualização de linhas quentes.
Instruções SQL usadas para operações de atualização de linhas quentes são marcadas com
autocommitou COMMIT_ON_SUCCESS. A camada de kernel otimizada do MySQL identifica automaticamente operações de atualização com essas marcas. Dentro de um determinado intervalo de tempo, ela executa umHashnas operações de atualização coletadas com base em sua chave primária ou chave exclusiva. Para operações de atualização mapeadas no mesmo bucket peloHash, o kernel as agrupa e confirma em lotes de acordo com a ordem de chegada.Para processar essas operações de atualização em um pipeline, duas unidades de execução as agrupam. Quando o primeiro grupo é coletado e está pronto para confirmação, o segundo grupo começa imediatamente a coletar operações de atualização. Quando o segundo grupo é coletado e está pronto para confirmação, o primeiro grupo já foi confirmado e começa a coletar um novo lote de operações de atualização. Os dois grupos se alternam e executam em paralelo.
Esse modelo de processamento em pipeline utiliza totalmente os recursos de hardware, aumenta a utilização da CPU e melhora a capacidade de processamento paralelo do sistema, maximizando seu throughput.
-
Eliminação de espera ao solicitar bloqueios de linha
Para garantir a consistência lógica dos dados, uma atualização primeiro adquire um bloqueio na linha de dados de destino. Se a solicitação de bloqueio não puder ser concedida imediatamente, a solicitação entra em estado de espera. Isso não apenas aumenta a latência de processamento, mas também aciona a detecção de deadlock, adicionando sobrecarga extra.
Conforme mencionado anteriormente, as operações de atualização na mesma linha de dados são agrupadas em ordem cronológica. A primeira operação de atualização em um grupo é o Leader. Ela lê a linha de dados de destino e a bloqueia. As operações de atualização subsequentes são Followers. Quando um Follower solicita um bloqueio na linha de dados de destino e verifica que o Leader já possui o bloqueio da linha, ele pode adquirir o bloqueio imediatamente sem esperar.
Essa otimização reduz o número de aquisições de bloqueio de linha e a sobrecarga de tempo associada. Como resultado, o desempenho geral do sistema melhora significativamente.
-
Redução de travessias de índice B-tree
O MySQL gerencia dados usando índices B-tree. Cada consulta deve percorrer o índice para localizar a linha de dados de destino. Quanto maior a tabela de dados e mais profundo o índice, mais longa será a travessia.
No mecanismo de agrupamento descrito anteriormente, apenas o Leader de cada grupo percorre o índice para localizar a linha de dados e, em seguida, armazena em cache a linha atualizada na memória (Row Cache). Depois que um Follower no mesmo grupo adquire o bloqueio com sucesso, ele lê a linha de dados de destino diretamente da memória sem percorrer o índice novamente.
Isso reduz o número total de travessias de índice e sua sobrecarga associada.
Pré-requisitos
-
Seu cluster PolarDB deve executar uma das seguintes versões:
PolarDB for MySQL 5.6, com versão secundária do mecanismo 20200601 ou posterior.
PolarDB for MySQL 5.7, com versão secundária do mecanismo 5.7.1.0.17 ou posterior.
PolarDB for MySQL 8.0, com versão secundária do mecanismo 8.0.1.1.10 ou posterior.
O binlog está habilitado.
-
O parâmetro de cluster
rds_ic_reduce_hint_enabledeve estar desabilitado.Para PolarDB for MySQL 5.6 e PolarDB for MySQL 8.0, esse parâmetro é desabilitado por padrão.
Para PolarDB for MySQL 5.7, esse parâmetro é habilitado por padrão. Antes de ativar a otimização de linhas quentes, altere o valor do parâmetro para OFF.
NotaPara garantir compatibilidade com arquivos de configuração do MySQL, o console do PolarDB adiciona o prefixo loose_ a todos os parâmetros de cluster. Para modificar o parâmetro
rds_ic_reduce_hint_enableno console do PolarDB, selecione o parâmetro com o prefixo loose_, que é loose_rds_ic_reduce_hint_enable.
Limitações
A otimização de linhas quentes não se aplica nos seguintes cenários:
A tabela que contém a linha quente é particionada.
Um trigger está definido na tabela que contém a linha quente.
A linha quente utiliza o mecanismo Statement Queue.
Se o binlog global estiver habilitado, mas o binlog no nível de sessão estiver desabilitado, a otimização de linhas quentes não será aplicada a instruções
UPDATE.Após ativar a otimização de linhas quentes, colunas que dependem exclusivamente do atributo
ON UPDATE CURRENT_TIMESTAMPpara atualizações automáticas deixarão de ser atualizadas automaticamente. Atribua valores explicitamente a essas colunas em suas instruçõesUPDATEusandoSET column_name = CURRENT_TIMESTAMP(n).
Uso
-
Ative a otimização de linhas quentes.
No console do PolarDB, modifique o seguinte parâmetro para ativar ou desativar a otimização de linhas quentes.
Parâmetro
Descrição
hotspot
Define se o recurso de otimização de linhas quentes deve ser ativado. Valores válidos:
ON: ativado.
OFF (padrão): desativado.
NotaPara garantir compatibilidade com arquivos de configuração do MySQL, o console do PolarDB adiciona o prefixo loose_ a todos os parâmetros de cluster. Para modificar o parâmetro hotspot no console do PolarDB, selecione o parâmetro com o prefixo loose_, que é loose_hotspot.
-
Use a sintaxe de hint para aplicar a otimização de linhas quentes.
Hint
Obrigatório
Descrição
Obrigatório
Confirma a transação se a atualização for bem-sucedida.
Opcional
Reverte a transação se a atualização falhar.
Opcional
Especifica que a solicitação deve atualizar apenas uma linha. Se essa condição não for atendida, a atualização falha.
NotaComo este hint confirma automaticamente a transação, inclua-o na última instrução SQL da transação.
Exemplo: Atualize o valor da coluna
cna tabelasbtest.UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c = c + 1 WHERE id = 1;
Operações relacionadas
Parâmetros personalizados
Não é possível modificar os seguintes parâmetros no console do PolarDB. Para modificá-los, acesse o Quota Center, localize o ID de cota polardb_mysql_hotspot e clique em Apply na coluna Actions.
Parâmetro | Descrição |
hotspot_for_autocommit | Define se a otimização de linhas quentes deve ser ativada para instruções
|
hotspot_update_max_wait_time | Tempo máximo que um Leader aguarda para que Followers entrem em um grupo durante uma Group Update.
|
hotspot_lock_type | Define se um novo tipo de bloqueio de linha deve ser ativado para Group Update. Valores válidos:
Nota
|
Configurações de parâmetros
Execute o seguinte comando para visualizar as configurações de parâmetros da otimização de linhas quentes.
SHOW variables LIKE "hotspot%";
Resultado de exemplo:
+------------------------------+-------+
|Variable_name | Value |
+------------------------------+-------+
|hotspot | OFF |
|hotspot_for_autocommit | OFF |
|hotspot_lock_type | OFF |
|hotspot_update_max_wait_time | 100 |
+------------------------------+-------+
Estatísticas de uso
Execute o seguinte comando para visualizar as estatísticas de uso da otimização de linhas quentes.
SHOW GLOBAL status LIKE 'Group_update%';
Teste de desempenho
Ferramenta de teste
O Sysbench é uma ferramenta de teste de desempenho de código aberto e multiplataforma. É usada principalmente para benchmarks de banco de dados, como MySQL, e para testes de desempenho do sistema em componentes como CPU, memória, I/O e threads. Suporta testes multithread e usa scripts Lua para controlar flexivelmente a lógica de teste. É adequada para cenários como avaliação de desempenho de banco de dados e testes de estresse.
Tabela e instrução de teste
-
Definição da tabela
CREATE TABLE sbtest (id INT UNSIGNED NOT NULL, c BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id)); -
Instrução de teste
UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ sbtest SET c = c + 1 WHERE id = 1;
Resultados do teste
PolarDB for MySQL 5.6
Cenário de teste
Uma única linha quente em uma CPU de 8 núcleos.
Resultados do teste
Neste cenário de teste, ativar a otimização de linhas quentes melhora o desempenho de atualização em linhas quentes relacionadas a estoque em quase 50 vezes sob alta concorrência.
Dados do teste (QPS)

|
Concorrência |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Otimização de linhas quentes desativada |
1365.31 |
1863.94 |
1866.6 |
1862.64 |
1867.32 |
1832.51 |
1838.31 |
1819.52 |
1833.2 |
|
Otimização de linhas quentes ativada |
1114.79 |
7000.19 |
12717.32 |
22029.48 |
43096.06 |
61349.7 |
83098.69 |
90860.94 |
87689 |
PolarDB for MySQL 5.7
Cenário de teste
Uma única linha quente em uma CPU de 8 núcleos.
Resultados do teste
Neste cenário de teste, ativar a otimização de linhas quentes melhora o desempenho de atualização em linhas quentes relacionadas a estoque em quase 35 vezes sob alta concorrência.
Dados do teste
QPS

|
Concorrência |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Otimização de linhas quentes desativada |
1348.49 |
1892.29 |
1889.77 |
1895.86 |
1875.2 |
1850.26 |
1843.62 |
1849.92 |
1835.68 |
|
Otimização de linhas quentes ativada |
1104.9 |
6886.89 |
12485.17 |
16003.23 |
16460.31 |
16548.86 |
27920.89 |
47893.96 |
66500.92 |
Latência do percentil 95

|
Concorrência |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Otimização de linhas quentes desativada |
0,9 |
5,47 |
9,91 |
18,95 |
36,89 |
73,13 |
164,45 |
297,92 |
590,56 |
|
Otimização de linhas quentes ativada |
1,08 |
1,44 |
1,58 |
3,25 |
5,28 |
9,56 |
12,08 |
13,22 |
18,28 |
PolarDB for MySQL 8.0
Cenário de teste
Uma única linha quente em uma CPU de 8 núcleos.
Resultados do teste
Neste cenário de teste, ativar a otimização de linhas quentes melhora o desempenho de atualização em linhas quentes relacionadas a estoque em quase 26 vezes sob alta concorrência.
Dados do teste
QPS

|
Concorrência |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Otimização de linhas quentes desativada |
1559.14 |
2103.82 |
2116.4 |
2082.1 |
2079.74 |
2031.64 |
1993.09 |
1977.6 |
1983.61 |
|
Otimização de linhas quentes ativada |
1237.28 |
7443.04 |
12244.19 |
15529.52 |
23041.15 |
33931.18 |
53924.24 |
54598.6 |
50988.22 |
Latência do percentil 95

|
Concorrência |
1 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
|
Otimização de linhas quentes desativada |
0,8 |
5 |
8,9 |
17,32 |
33,12 |
66,84 |
153,02 |
287,38 |
549,52 |
|
Otimização de linhas quentes ativada |
0,97 |
1,34 |
1,89 |
3,19 |
4,82 |
5,88 |
7,17 |
13,46 |
28,16 |
Etapas do teste de desempenho
Prepare uma instância ECS e instale o Sysbench.
-
Coloque o seguinte arquivo
oltp_inventory.luano diretóriosrc/luado código-fonte do Sysbench.#!/usr/bin/env sysbench -- it is to test inventory_hotspot performance sysbench.cmdline.options= { inventory_hotspot = {"enable ali inventory hotspot", 'off'}, tables = {"table number", 1}, table_size = {"table size", 1}, oltp_skip_trx = {'skip trx', true}, hotspot_rows = {'hotspot row number', 1} } function cleanup() drv = sysbench.sql.driver() con = drv:connect() for i = 1, sysbench.opt.tables do print(string.format("drop table sbtest%d ...", i)) drop_table(drv, con, i) end end function drop_table(drv, con, table_id) local query query = string.format("drop table if exists sbtest%d ", table_id) con:query(query) end function create_table(drv, con, table_id) local query query = string.format("CREATE TABLE sbtest%d (id INT UNSIGNED NOT NULL, c BIGINT UNSIGNED NOT NULL, PRIMARY KEY (id))", table_id) con:query(query) for i=1, sysbench.opt.table_size do con:query("INSERT INTO sbtest" .. table_id .. "(id, c) values (" ..i.. ", 1)") end end function prepare() drv = sysbench.sql.driver() con = drv:connect() for i = 1, sysbench.opt.tables do print(string.format("Creating table sbtest%d ...", i)) create_table(drv, con, i) end end function thread_init() drv = sysbench.sql.driver() con = drv:connect() begin_query = 'BEGIN' commit_query = 'COMMIT' end function event() local table_name table_name = "sbtest" .. sysbench.rand.uniform(1, sysbench.opt.tables) local min_line = math.min(sysbench.opt.table_size, sysbench.opt.hotspot_rows) local row_id = sysbench.rand.uniform(1, min_line) if not sysbench.opt.oltp_skip_trx then con:query(begin_query) end if (sysbench.opt.inventory_hotspot == "on") then con:query("UPDATE /*+ COMMIT_ON_SUCCESS ROLLBACK_ON_FAIL TARGET_AFFECT_ROW(1) */ " .. table_name .. " SET c=c+1 WHERE id =" .. row_id) else con:query("UPDATE " .. table_name .. " SET c=c+1 WHERE id = " .. row_id) end if not sysbench.opt.oltp_skip_trx then if (sysbench.opt.inventory_hotspot == "on") then con:query(commit_query) end end end function thread_done() con:disconnect() end -
Execute o teste do Sysbench.
-
Prepare os dados.
sysbench --hotspot_rows=1 --histogram=on --mysql-user=<user> --inventory_hotspot=on --mysql-host=<host> --threads=1 --report-interval=1 --mysql-password=<password> --tables=1 --table-size=1 --oltp_skip_trx=true --db-driver=mysql --percentile=95 --time=300 --mysql-port=<port> --events=0 --mysql-db=<database> oltp_inventory prepare -
Execute o teste.
sysbench --db-driver=mysql --mysql-host=<host> --mysql-port=<port> --mysql-user=<user> --mysql-password=<password> --mysql-db=<database> --range-selects=0 --table_size=25000 --tables=250 --events=0 --time=600 --rand-type=uniform --threads=<threads> oltp_inventory run
Parâmetros de entrada
Parâmetro
Descrição
mysql-host
O endpoint do cluster.
mysql-port
A porta do endpoint do cluster.
mysql-user
O nome de usuário da conta do banco de dados.
mysql-password
A senha da conta do banco de dados.
mysql-db
O nome do banco de dados.
Parâmetros de saída
Parâmetro
Métrica
Descrição
tables
Número de tabelas
O número total de tabelas usadas no teste.
table_size
Número de linhas por tabela
O número de registros em cada tabela.
Tamanho dos dados
O tamanho dos dados da tabela, medido em unidades como MB ou GB.
threads
Número de threads concorrentes
O número de threads configuradas.
Status da thread
O status em tempo real das threads em execução.
-