1. Etapas de otimização do servidor de banco de dados
Quando encontramos problemas de ajuste de banco de dados, como devemos pensar? Aqui o processo de pensamento está organizado na imagem abaixo.
Todo o processo é dividido em 观察(Show status)
duas 行动(Action)
partes. A parte da letra S representa observação (serão utilizadas as ferramentas de análise correspondentes), e a parte da letra A representa ação (correspondente às ações que podem ser tomadas pela análise).
Podemos entender o status geral de execução do banco de dados por meio da observação. As ferramentas de análise de desempenho podem nos informar o que o SQL está executando lentamente, verificar o plano de execução específico do SQL e até mesmo o custo de cada etapa da execução do SQL, para que possamos localizar o problema. Encontre o problema e tome as medidas apropriadas.
Explique esta imagem em detalhes:
Primeiramente, na parte S1, precisamos observar se há oscilações periódicas no status do servidor. Se sim 存在周期性波动
, pode ser devido a nós cíclicos, como Double Eleven, promoções, etc. Neste caso, podemos resolver através do passo A1, ou seja, adicionar cache, ou alterar a política de invalidação de cache.
Se a estratégia de cache não for resolvida ou não for a causa das flutuações periódicas, precisamos ir mais longe 分析查询延迟和卡顿的原因
. Em seguida, precisamos inserir
S2 开启慢查询
. A consulta lenta pode nos ajudar a localizar instruções SQL que são executadas lentamente. Podemos long_query_time
definir o limite "lento" definindo parâmetros. Se o tempo de execução do SQL exceder long_query_time
, será considerada uma consulta lenta. Depois de coletar essas consultas lentas, podemos analisar os logs de consultas lentas por meio de ferramentas de análise.
Na etapa S3, conhecemos o SQL que é executado lentamente, para que possamos EXPLAIN
visualizar o plano de execução da instrução SQL correspondente de maneira direcionada ou visualizar show profile
o custo de tempo de cada etapa do SQL. Desta forma, podemos entender se a consulta SQL está lenta devido ao longo tempo de execução ou ao longo tempo de espera.
Se o tempo de espera do SQL for longo, entramos na etapa A2. Nesta etapa, podemos 调优服务器的参数
, por exemplo, aumentar o buffer pool do banco de dados de forma adequada. Se a execução do SQL demorar muito, vá para a etapa A3. Nesta etapa, precisamos considerar se é um problema com o design do índice? Ou há muitas tabelas de dados associadas à consulta? Ou esse fenômeno é causado por problemas de design de campo nas tabelas de dados. Em seguida, faça os ajustes correspondentes nessas dimensões.
Se nem A2 nem A3 puderem resolver o problema, precisamos considerar se o desempenho da consulta SQL do próprio banco de dados atingiu o gargalo.Se for confirmado que o gargalo de desempenho não foi atingido, precisamos verificar novamente e repetir as etapas acima . Se você atingiu 性能瓶颈
o estágio A4, você precisa considerar a 增加服务器
adoção de uma arquitetura de separação de leitura e gravação ou considerar a fragmentação do banco de dados 分库分表
, como fragmentação vertical, fragmentação vertical e fragmentação horizontal.
A descrição acima é a ideia do processo de ajuste do banco de dados. Se descobrirmos que há atrasos ou congelamentos irregulares na execução do SQL, podemos usar ferramentas de análise para nos ajudar a localizar o SQL problemático.Você pode entender essas três ferramentas de análise como as três etapas do ajuste do SQL: 慢查询、
EXPLAIN
e SHOWPROFILING
.
resumo:
2. Visualize os parâmetros de desempenho do sistema
No MySQL, você pode usar instruções SHOW STATUS
para consultar alguns servidores de banco de dados MySQL 性能参数
.执行频率
A sintaxe da instrução SHOW STATUS é a seguinte:
SHOW [GLOBAL|SESSION] STATUS LIKE'参数';
Alguns parâmetros de desempenho comumente usados são os seguintes:
-
Conexões: O número de vezes para se conectar ao servidor MySQL.
-
Tempo de atividade: O tempo online do servidor MySQL.
-
Slow_queries: o número de consultas lentas.
- O padrão é mais de dez segundos
-
Innodb_rows_read: Número de linhas retornadas pela consulta Select
-
Innodb_rows_inserted: Número de linhas inseridas executando a operação INSERT
-
Innodb_rows_updated: Número de linhas atualizadas pela operação UPDATE
-
Innodb_rows_deleted: Número de linhas excluídas pela operação DELETE
-
Com_select: O número de operações de consulta.
-
Com_insert: O número de operações de inserção. Para operações INSERT de inserção em lote, apenas um acúmulo é executado.
-
Com_update: O número de operações de atualização.
-
Com_delete: o número de operações de exclusão.
Por exemplo:
# 慢查询次数
show status like 'Slow_queries';
表结构优化
O parâmetro de contagem de consulta lenta pode ser combinado com o log de consulta lenta para descobrir a instrução de consulta lenta e, em seguida, executar OR na instrução de consulta lenta 查询语句优化
. Para outro exemplo, as instruções a seguir podem ser usadas para visualizar instruções relacionadas:
show status like 'Innodb_rows_%';
3. Estatísticas de custo de consulta SQL: last_query_cost
Uma instrução de consulta SQL precisa determinar o plano de execução da consulta antes de executá-la. Se houver vários planos de execução, o MySQL calculará o custo de cada plano de execução e selecionará um deles como plano de execução final 成本最小
.
last_query_cost
Se quisermos verificar o custo da consulta de uma determinada instrução SQL, podemos obter o custo da consulta atual verificando os valores das variáveis na sessão atual após executar a instrução SQL . 评价一个查询的执行效率
Muitas vezes também é uma métrica comum para nós. Este custo de consulta corresponde a SQL语句所需要读取的页的数量
.
Ainda usamos a tabela student_info no Capítulo 8 como exemplo:
CREATE TABLE `student_info` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`student_id` INT NOT NULL ,
`name` VARCHAR(20) DEFAULT NULL, `course_id` INT NOT NULL ,
`class_id` INT(11) DEFAULT NULL,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Se quisermos consultar o registro com id=900001 e depois observar o custo da consulta, podemos pesquisar diretamente no índice clusterizado:
SELECT student_id, class_id, NAME, create_time FROM student_info WHERE id = 900001 ;
Resultados de execução (1 registro, tempo de execução 0.042s
)
Em seguida, observe o custo do otimizador de consulta. Na verdade, só precisamos recuperar uma página:
mysql> SHOW STATUS LIKE 'last_query_cost';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| Last_query_cost | 1.000000 |
+-----------------+----------+
E se quisermos consultar os registros dos alunos com IDs entre 900001 e 9000100?
SELECT student_id, class_id, NAME, create_time FROM student_info
WHERE id BETWEEN 900001 AND 900100;
Resultados de execução (100 registros, tempo de execução 0.046s
)
Em seguida, observe o custo do otimizador de consulta. Neste momento precisamos consultar cerca de 20 páginas.
mysql> SHOW STATUS LIKE 'last_query_cost';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| Last_query_cost | 21.134453 |
+-----------------+-----------+
Você pode ver que o número de páginas é 20 vezes maior que antes, mas a eficiência da consulta não mudou significativamente. Na verdade, o tempo das duas consultas SQL é basicamente o mesmo, porque a página é carregada de uma só vez usando leitura sequencial no buffer pool e, em seguida, pesquise novamente. Embora 页数量(last_query_cost)增加了不少
, por meio do mecanismo de buffer pool, 并没有增加多少查询时间
o .
**Cenários de uso:** É muito útil para comparar custos, especialmente quando temos vários métodos de consulta para escolher.
A consulta SQL é um processo dinâmico. Do ponto de vista do carregamento da página, podemos tirar as duas conclusões a seguir:
位置决定效率
. Se a página estiver no banco de dados缓冲池
, então a eficiência é maior, caso contrário ela precisa磁盘
ser
lida a partir dela. Claro, para ler uma única página, se a página existir na memória, será muito mais eficiente do que ler de O disco.
批量决定效率
. Se lermos aleatoriamente uma única página do disco, a eficiência será muito baixa (quase 10ms). Se usarmos a leitura
sequencial para ler páginas em lotes, a eficiência média de leitura de uma página será muito melhorada., ainda mais rápido do que uma página. leitura aleatória de uma única página na memória.Portanto, não há necessidade de se preocupar ao encontrar / O. Se você encontrar o método certo, a eficiência ainda será muito alta. Devemos primeiro considerar o local de armazenamento de dados. Se forem dados usados com frequência, devemos tentar colocá-los no meio
缓冲池
. Em segundo lugar, podemos aproveitar ao máximo a capacidade de transferência do disco e ler os dados em lotes de uma só vez. , para que a eficiência de leitura de uma única página também seja melhorada.
4. Localize SQL que é executado lentamente: log de consulta lento
O log de consultas lentas do MySQL é usado para registrar 响应时间超过阀值
instruções no MySQL. Especificamente, long_query_time
o SQL cujo tempo de execução excede o valor será registrado no log de consultas lentas. O valor padrão de long_query_time é 10
, o que significa que as instruções executadas por mais de 10 segundos (excluindo 10 segundos) são consideradas como excedendo nosso valor de tempo máximo de tolerância. |
Sua principal função é nos ajudar a descobrir as consultas SQL que levam tempos de execução particularmente longos e otimizá-las de maneira direcionada, melhorando assim a eficiência geral do sistema. Quando nosso servidor de banco de dados está bloqueado e funciona lentamente, verificar o log de consultas lentas e encontrar essas consultas lentas é muito útil para resolver o problema. Por exemplo, se um sq| for executado por mais de 5 segundos, consideraremos que é um SQL lento. Esperamos coletar o sql que excede 5 segundos e realizar uma análise abrangente com base na explicação.
Por padrão, o banco de dados MySQL 没有开启慢查询日志
exige que definamos esse parâmetro manualmente. Se não for necessário para ajuste, geralmente não é recomendado habilitar esse parâmetro , porque habilitar logs de consulta lentos terá um impacto mais ou menos certo no desempenho.
O log de consulta lenta oferece suporte à gravação de registros de log em um arquivo.
4.1 Habilitar parâmetros de log de consulta lenta
1. Ative slow_query_log
mysql > show variables like '%slow_query_log%';
mysql > set global slow_query_log='ON';
Em seguida, vamos verificar se o log de consulta lenta está ativado e a localização do arquivo de log de consulta lenta:
Você pode ver que a análise de consulta lenta foi ativada e o arquivo foi salvo em /var/lib/mysql/atguigu02-slow.log
File
.
2. Modifique o limite long_query_time
A seguir, vamos dar uma olhada na configuração do limite de tempo para consultas lentas, usando o seguinte comando:
mysql > show variables like '%long_query_time%';
# 测试发现:设置global的方式对当前session的long_query_time失效。对新连接的客户端有效。所以可以一并执行下述语句
mysql> set global long_query_time = 1 ;
mysql> show global variables like ' %long-query_time% ';
# 即更改global 也更改了session变量
mysql> set long_query_time=1;
mysql> show variables like '%long_query_time%';
Suplemento: Defina parâmetros no arquivo de configuração juntos
Comparado com o método de linha de comando anterior, o método a seguir pode ser considerado um método de configuração permanente.
Modifique my.cnf
o arquivo, adicione [mysqld]下
ou modifique parâmetros long_query_time
e reinicie o servidor MySQL. |slow_query_log
slow_query_log_file
[mysqld]
slow_query_log=ON #开启慢查询日志的开关
slow_query_log_file=/var/lib/mysql/my-slow.log #慢查询日志的目录和文件名信息
long_query_time=3 #设置慢查询的阈值为3秒,超出此设定值的SQL即被记录到慢查询日志
log_output=FILE
Se você não especificar um caminho de armazenamento, o log de consulta lenta será armazenado na pasta de dados do banco de dados MySQL por padrão. Se você não especificar um nome de arquivo, o nome de arquivo padrão será hostname-slow.log.
4.2 Verifique o número de consultas lentas
Consultar quantos registros de consulta lenta existem no sistema atual
SHOW GLOBAL STATUS LIKE '%Slow_queries%';
4.3 Demonstração de caso
Passo 1. Criar tabela
CREATE TABLE `student` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`stuno` INT NOT NULL ,
`name` VARCHAR(20) DEFAULT NULL,
`age` INT(3) DEFAULT NULL,
`classId` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Etapa 2: definir o parâmetro log_bin_trust_function_creators
Crie uma função, se um erro for relatado:
This function has none of DETERMINISTIC......
Comando ativado: Permitir a criação de configurações de função:
set global log_bin_trust_function_creators=1; # 不加global只是当前窗口有效。
Etapa 3: crie a função
Gere strings aleatoriamente: (igual ao capítulo anterior)
DELIMITER //
CREATE FUNCTION rand_string(n INT)
RETURNS VARCHAR(255) #该函数会返回一个字符串
BEGIN
DECLARE chars_str VARCHAR(100) DEFAULT
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE return_str VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
SET i = i + 1;
END WHILE;
RETURN return_str;
END //
DELIMITER ;
Gere valores aleatórios: (igual ao capítulo anterior)
#测试
SELECT rand_string(10);
DELIMITER //
CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11)
BEGIN
DECLARE i INT DEFAULT 0;
SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ;
RETURN i;
END //
DELIMITER ;
#测试:
SELECT rand_num(10,100);
Etapa 4: criar procedimento armazenado
DELIMITER //
CREATE PROCEDURE insert_stu1( START INT , max_num INT )
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0; #设置手动提交事务
REPEAT #循环
SET i = i + 1; #赋值
INSERT INTO student (stuno, NAME ,age ,classId ) VALUES
((START+i),rand_string(6),rand_num(10,100),rand_num(10,1000));
UNTIL i = max_num
END REPEAT;
COMMIT; #提交事务
END //
DELIMITER ;
Etapa 5: chame o procedimento armazenado
#调用刚刚写好的函数, 4000000条记录,从100001号开始
CALL insert_stu1(100001,4000000);
4.4 Teste e Análise
1. Teste
mysql> SELECT * FROM student WHERE stuno = 3455655;
+---------+---------+--------+------+---------+
| id | stuno | name | age | classId |
+---------+---------+--------+------+---------+
| 3523633 | 3455655 | oQmLUr | 19 | 39 |
+---------+---------+--------+------+---------+
1 row in set (2.09 sec)
mysql> SELECT * FROM student WHERE name = 'oQmLUr';
+---------+---------+--------+------+---------+
| id | stuno | name | age | classId |
+---------+---------+--------+------+---------+
| 1154002 | 1243200 | OQMlUR | 266 | 28 |
| 1405708 | 1437740 | OQMlUR | 245 | 439 |
| 1748070 | 1680092 | OQMlUR | 240 | 414 |
| 2119892 | 2051914 | oQmLUr | 17 | 32 |
| 2893154 | 2825176 | OQMlUR | 245 | 435 |
| 3523633 | 3455655 | oQmLUr | 19 | 39 |
+---------+---------+--------+------+---------+
6 rows in set (2.39 sec)
Como pode ser visto nos resultados acima, são necessários 2,09 segundos para consultar as informações do aluno com o número de aluno “3455655”. Demora 2,39 segundos para consultar as informações do aluno com o nome do aluno "oQmLUr". Atingiu a ordem de segundos, indicando que a eficiência da consulta atual é relativamente baixa.
Analisaremos os motivos na seção a seguir.
2.Análise
show status like 'slow_queries';
Informações adicionais:
Além das variáveis acima, há também uma variável de sistema que controla o log de consulta lenta: min_examined_row_limit. O significado desta variável é query
扫描过的最少记录数
. Essa variável e o tempo de execução da consulta juntos formam a condição para determinar se uma consulta é lenta. Se o número de registros verificados pela consulta for maior ou igual ao valor desta variável, e o tempo de execução da consulta exceder o valor de long_query_time, então a consulta será registrada no log de consulta lenta; caso contrário, não será registrado no log de consulta lenta.mysql> show variables like 'min%'; +------------------------+-------+ | Variable_name | Value | +------------------------+-------+ | min_examined_row_limit | 0 | +------------------------+-------+ 1 row in set (0.00 sec)
Você também pode modificar o valor de "min_examined_row_limit" modificando o arquivo "my.ini" conforme necessário.
4.5 Ferramenta de análise de log de consulta lenta: mysqldumpslow
Em um ambiente de produção, se você deseja analisar logs manualmente, localizar e analisar SQL, é obviamente uma tarefa trabalhosa. O MySQL fornece ferramentas de análise de log mysqldumpslow
.
Veja as informações de ajuda do mysqldumpslow
mysqldumpslow --help
Os parâmetros específicos do comando mysqldumpslow são os seguintes:
- -a: Não abstraia números em N e strings em S
- -s: indica como classificar:
- c: número de visitas
- l: tempo de bloqueio
- r: registro de retorno
- t: tempo de consulta
- al: tempo médio de bloqueio
- ar: número médio de registros retornados
- at: tempo médio de consulta (modo padrão)
- ac: número médio de consultas
- -t: Retorna o número anterior de dados;
- -g: seguido por um padrão de correspondência regular, sem distinção entre maiúsculas e minúsculas
Exemplo: Queremos classificar por tempo de consulta e visualizar as cinco primeiras instruções SQL, para que possamos escrever:
mysqldumpslow -s t -t 5 /var/lib/mysql/atguigu01-slow.log
[root@bogon ~]# mysqldumpslow -s t -t 5 /var/lib/mysql/atguigu01-slow.log
Reading mysql slow query log from /var/lib/mysql/atguigu01-slow.log
Count: 1 Time=2.39s (2s) Lock=0.00s (0s) Rows=13.0 (13), root[root]@localhost
SELECT * FROM student WHERE name = 'S'
Count: 1 Time=2.09s (2s) Lock=0.00s (0s) Rows=2.0 (2), root[root]@localhost
SELECT * FROM student WHERE stuno = N
Died at /usr/bin/mysqldumpslow line 162, <> chunk 2.
Referências comuns para trabalho:
#得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log
#得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log
#得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log
#另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more
4.6 Fechar log de consulta lenta
Exceto que precisa ser ligado para ajuste, é melhor não ligá-lo normalmente.
Existem duas maneiras de interromper a função de log de consulta lenta do servidor MySQL:
Método 1: método permanente
[mysqld]
slow_query_log=OFF
Ou comente ou exclua o item slow_query_log
mysqld]
#slow_query_log =OFF
Reinicie o serviço MySQL e execute a seguinte instrução para consultar a função de log lento.
SHOW VARIABLES LIKE '%slow%'; #查询慢查询日志所在目录
SHOW VARIABLES LIKE '%long_query_time%'; #查询超时时长
Método 2: método temporário
Use a instrução SET para definir. (1) Pare a função de log de consulta lenta do MySQL. As instruções SQL específicas são as seguintes.
SET GLOBAL slow_query_log=off;
(2) Reinicie o serviço MySQL e use a instrução SHOW para consultar as informações da função de log de consulta lenta. A instrução SQL específica é a seguinte
SHOW VARIABLES LIKE '%slow%';
#以及
SHOW VARIABLES LIKE '%long_query_time%';
4.7 Excluir logs de consulta lenta
mysql> show variables like '%slow_query_log%';
+---------------------+----------------------------+
| Variable_name | Value |
+---------------------+----------------------------+
| slow_query_log | ON |
| slow_query_log_file | /var/lib/mysql/my-slow.log |
+---------------------+----------------------------+
2 rows in set (0.07 se
Pode-se observar pelos resultados da execução que o diretório do log de consulta lenta é padronizado como o diretório de dados do MySQL e pode estar neste diretório 手动删除慢查询日志文件
. Use o comando mysqladmin flush-logs
para regenerar o arquivo de log de consulta. O comando específico é o seguinte: Após a execução, o arquivo de log de consulta lenta será regenerado no diretório de dados.
# 不使用这个命令,没办法自己创建
mysqladmin -uroot -p flush-logs slow
## 这个命令可以重置其他日志 例如undo日志
dica
Os logs de consultas lentas são excluídos e reconstruídos usando o comando mysqladmin flush-logs. Ao usá-lo, certifique-se de observar que, uma vez executado este comando, o
log de consulta lenta só existirá no novo arquivo de log. Se o log de consulta antigo for necessário, será necessário fazer backup dele com antecedência.
5. Visualize o custo de execução do SQL: MOSTRAR PERFIL
Mostrar perfil é discutido no capítulo "Arquitetura Lógica" e está aqui como uma revisão.
Show Profile é uma ferramenta fornecida pelo MySQL que pode ser usada para analisar o que o SQL fez na sessão atual e o consumo de recursos de execução. Ele pode ser usado para medir o ajuste do SQL. 默认情况下处于关闭状态
e salve os últimos 15 resultados em execução.
Podemos ativar esse recurso no nível da sessão
mysql> show variables like 'profiling';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling | OFF |
+---------------+-------+
1 row in set (0.34 sec)
profiling='ON’
Ative mostrar perfil configurando :
mysql> set profiling = 'ON';
Query OK, 0 rows affected, 1 warning (0.06 sec)
mysql> show variables like 'profiling';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling | ON |
+---------------+-------+
1 row in set (0.13 sec)
Em seguida, execute as instruções de consulta relevantes. Então, para ver quais perfis a sessão atual possui, use o seguinte comando:
mysql> show profiles;
+----------+------------+-------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+-------------------------------------+
| 1 | 0.13515975 | show variables like 'profiling' |
| 2 | 0.06386950 | select * from student_info limit 10 |
+----------+------------+-------------------------------------+
2 rows in set, 1 warning (0.01 sec)
Você pode ver que há um total de 2 consultas na sessão atual. Se quisermos ver o custo da consulta mais recente, podemos usar:
mysql> show profile;
+--------------------------------+----------+
| Status | Duration |
+--------------------------------+----------+
| starting | 0.029330 |
| Executing hook on transaction | 0.001174 |
| starting | 0.002804 |
| checking permissions | 0.002918 |
| Opening tables | 0.009026 |
| init | 0.001605 |
| System lock | 0.000503 |
| optimizing | 0.000013 |
| statistics | 0.007651 |
| preparing | 0.000084 |
| executing | 0.005307 |
| end | 0.000017 |
| query end | 0.000178 |
| waiting for handler commit | 0.000028 |
| closing tables | 0.001087 |
| freeing items | 0.000399 |
| cleaning up | 0.001748 |
+--------------------------------+----------+
17 rows in set, 1 warning (0.04 sec)
Também podemos verificar o custo do ID de consulta especificado, por exemplo, show profile for query 2
os resultados da consulta são os mesmos. No SHOWPROFILE podemos visualizar o overhead de diferentes partes, como cpu, block.io, etc.:
mysql> show profile cpu,block io for query 2;
+------------------------------+--------+----------+------------+-------------+
| Status |Duration| CPU_user |Block_ops_in|Block_ops_out|
+------------------------------+--------+----------+------------+-------------+
| starting |0.029330| 0.017180 | 49712| 0|
| Executing hook on transaction|0.001174| 0.001079 | 3624| 0|
| starting |0.002804| 0.002169 | 4728| 0|
| checking permissions |0.002918| 0.002437 | 8168| 0|
| Opening tables |0.009026| 0.005841 | 14120| 0|
| init |0.001605| 0.000392 | 80| 0|
| System lock |0.000503| 0.000130 | 24| 0|
| optimizing |0.000013| 0.000010 | 0| 0|
| statistics |0.007651| 0.003072 | 4160| 0|
| preparing |0.000084| 0.000071 | 0| 0|
| executing |0.005307| 0.001609 | 568| 0|
| end |0.000017| 0.000011 | 0| 0|
| query end |0.000178| 0.000047 | 8| 0|
| waiting for handler commit |0.000028| 0.000025 | 0| 0|
| closing tables |0.001087| 0.000279 | 56| 0|
| freeing items |0.000399| 0.000259 | 8| 0|
| cleaning up |0.001748| 0.000381 | 56| 0|
+------------------------------+--------+----------+------------+-------------+
17 rows in set, 1 warning (0.01 sec)
Se a execução for relativamente longa, pode ser que o código não esteja bem escrito. Use explicar para continuar a consultar o problema.
Parâmetros de consulta comuns para mostrar perfil:
① TODOS: Exibe todas as informações gerais.
② BLOCK IO: Exibe a sobrecarga de IO do bloco.
③ INTERRUPTORES DE CONTEXTO: sobrecarga de alternância de contexto.
④ CPU: Exibe informações de sobrecarga da CPU.
⑤ IPC: Exibe o envio e recebimento de informações indiretas.
⑥ MEMÓRIA: Exibe informações de sobrecarga de memória.
⑦ FALHAS DE PÁGINA: Exibe informações de sobrecarga de erro de página.
⑧ FONTE: Exibe informações gerais relacionadas a Função_fonte, Arquivo_fonte, Linha_fonte.
⑨ SWAPS: Exibe as informações de custo do número de swaps.
Conclusões que precisam ser prestadas atenção no desenvolvimento diário:
converting HEAP to MyISAM
: o resultado da consulta é muito grande, a memória não é suficiente e os dados foram movidos para o disco.creating tmp table:
Crie uma tabela temporária. Copie os dados primeiro para a tabela temporária e, em seguida, exclua a tabela temporária após o uso.Copying to tmp table on disk
:Copie a tabela temporária da memória para o disco, tome cuidado!locked
.
Se algum dos quatro resultados acima aparecer nos resultados de diagnóstico do perfil show, a instrução SQL precisará ser otimizada.
Perceber:
No entanto, o comando SHOW PROFILE ficará obsoleto e poderemos visualizá-lo na tabela de dados de criação de perfil em information_schema.
6. Analise a instrução da consulta: EXPLAIN
6.1 Visão Geral
**Após localizar a consulta lenta SQL, podemos usar a ferramenta EXPLAIN ou DESCRIBE para analisar a instrução da consulta de maneira direcionada. **O uso da instrução DESCRIBE é igual ao da instrução EXPLAIN e os resultados da análise também são os mesmos.
执行计划
Existe um módulo otimizador no MySQL que é especificamente responsável por otimizar as instruções SELECT. Sua principal função é fornecer à Consulta solicitada pelo cliente o método de recuperação de dados que ele considera ideal (ele acha que é o método ideal de recuperação de dados) calculando e analisar as informações estatísticas coletadas no sistema ( pode não ser o que o DBA considera ideal, esta parte é a mais demorada).
Este plano de execução mostra como executar especificamente a consulta, como qual é a ordem das conexões de várias tabelas, qual método de acesso é usado para cada tabela para executar especificamente a consulta, etc. O MySQL nos fornece EXPLAIN
instruções para nos ajudar a visualizar o plano de execução específico de uma determinada instrução de consulta. Compreender os EXPLAIN
vários itens de saída da instrução pode melhorar o desempenho de nossas instruções de consulta de maneira direcionada.
1.O que pode ser feito?
- Ordem de leitura da tabela
- O tipo de operação da operação de leitura de dados.
- Quais índices podem ser usados
- Quais índices são realmente usados
- Referências entre tabelas
- Quantas linhas em cada tabela são consultadas pelo otimizador
Introdução ao site oficial
https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
Status da versão
- Antes do MySQL 5.6.3, apenas EXPLAIN SELECT podia ser usado; depois do MYSQL 5.6.3, ele pode ser usado
EXPLAIN
SELECT
.UPDATE
DELETE
- Nas versões anteriores à 5.7, você
partitions
precisa usarexplain partitions
o comando se quiser exibi-lo; você precisafiltered
usarexplain extended
o comando se quiser exibi-lo. Após a versão 5.7, o explicar exibe diretamente as informações nas partições e é filtrado por padrão.
6.2 Sintaxe básica
A sintaxe da instrução EXPLAIN ou DESCRIBE é a seguinte:
EXPLAIN SELECT select_options
# 或者 两个是一样的
DESCRIBE SELECT select_options
Se quisermos ver o plano de execução de uma determinada consulta, podemos adicionar um na frente da instrução de consulta específica EXPLAIN
, assim:
mysql> EXPLAIN SELECT 1;
EXPLAIN
As funções de cada coluna gerada pela instrução são as seguintes:
Lista | descrever |
---|---|
eu ia | Em uma instrução de consulta grande, cada palavra-chave SELECT corresponde a um ID exclusivo. |
selecione o tipo | O tipo de consulta correspondente à palavra-chave SELECT |
mesa | Nome da tabela |
partições | Informações de partição correspondentes |
tipo | Método de acesso para tabela única (importante) |
chaves_possíveis | Índices possíveis |
chave | índice real usado |
key_len | O comprimento real do índice usado |
referência | Ao usar a consulta de equivalência de coluna de índice, as informações do objeto que correspondem à coluna de índice para equivalência. |
linhas | Número estimado de registros a serem lidos |
filtrado | Porcentagem de registros restantes em uma tabela após filtragem por condições de pesquisa |
Extra | algumas informações adicionais |
6.3 Preparação de dados
1. Crie uma mesa
CREATE TABLE s1 (
id INT AUTO_INCREMENT,
key1 VARCHAR(100),
key2 INT,
key3 VARCHAR(100),
key_part1 VARCHAR(100),
key_part2 VARCHAR(100),
key_part3 VARCHAR(100),
common_field VARCHAR(100),
PRIMARY KEY (id),
INDEX idx_key1 (key1),
UNIQUE INDEX idx_key2 (key2),
INDEX idx_key3 (key3),
INDEX idx_key_part(key_part1, key_part2, key_part3)
) ENGINE=INNODB CHARSET=utf8;
CREATE TABLE s2 (
id INT AUTO_INCREMENT,
key1 VARCHAR(100),
key2 INT,
key3 VARCHAR(100),
key_part1 VARCHAR(100),
key_part2 VARCHAR(100),
key_part3 VARCHAR(100),
common_field VARCHAR(100),
PRIMARY KEY (id),
INDEX idx_key1 (key1),
UNIQUE INDEX idx_key2 (key2),
INDEX idx_key3 (key3),
INDEX idx_key_part(key_part1, key_part2, key_part3)
) ENGINE=INNODB CHARSET=utf8;
2. Defina o parâmetro log_bin_trust_function_creators
Crie uma função. Se um erro for relatado, você precisará ativar o seguinte comando: Permitir a criação de configurações de função:
set global log_bin_trust_function_creators=1; # 不加global只是当前窗口有效。
3. Crie funções
DELIMITER //
CREATE FUNCTION rand_string1 ( n INT )
RETURNS VARCHAR ( 255 ) #该函数会返回一个字符串
BEGIN
DECLARE
chars_str VARCHAR ( 100 ) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
DECLARE
return_str VARCHAR ( 255 ) DEFAULT '';
DECLARE
i INT DEFAULT 0;
WHILE
i < n DO
SET return_str = CONCAT(
return_str,
SUBSTRING( chars_str, FLOOR( 1+RAND ()* 52 ), 1 ));
SET i = i + 1;
END WHILE;
RETURN return_str;
END //
DELIMITER ;
4. Crie um procedimento armazenado
Crie um procedimento armazenado para inserir dados na tabela s1:
DELIMITER //
CREATE PROCEDURE insert_s1 (IN min_num INT (10),IN max_num INT (10))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO s1 VALUES(
(min_num + i),
rand_string1(6),
(min_num + 30 * i + 5),
rand_string1(6),
rand_string1(10),
rand_string1(5),
rand_string1(10),
rand_string1(10));
UNTIL i = max_num
END REPEAT;
COMMIT;
END //
DELIMITER ;
Crie um procedimento armazenado para inserir dados na tabela s2:
DELIMITER //
CREATE PROCEDURE insert_s2 (IN min_num INT ( 10 ),IN max_num INT ( 10 ))
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEAT
SET i = i + 1;
INSERT INTO s2 VALUES(
( min_num + i ),
rand_string1 ( 6 ),
( min_num + 30 * i + 5 ),
rand_string1 ( 6 ),
rand_string1 ( 10 ),
rand_string1 ( 5 ),
rand_string1 ( 10 ),
rand_string1 ( 10 ));
UNTIL i = max_num
END REPEAT;
COMMIT;
END //
DELIMITER ;
5. Chame o procedimento armazenado
Adicionando dados da tabela s1: adicionando 10.000 registros:
CALL insert_s1(10001,10000); # id 10002~20001
Adicionando dados da tabela s2: adicionando 10.000 registros:
CALL insert_s2(10001,10000);# id 10002~20001
6.4 As funções de cada coluna EXPLAIN
Para proporcionar uma experiência melhor a todos, ajustamos EXPLAIN
a ordem das colunas de saída.
1. mesa
Nome da tabela
Não importa quão complexa seja nossa instrução de consulta, 包含了多少个表
ela precisa ser executada em cada tabela no final 单表访问
. Portanto,
o MySQL estipula que cada registro gerado pela instrução EXPLAIN corresponde ao método de acesso de uma única tabela . A tabela deste registro A coluna representa o
nome da tabela da tabela (às vezes não é o nome real da tabela, pode ser abreviado)
#1. table:表名
#查询的每一行记录都对应着一个单表
explain select count(*) from s1;
#s1:驱动表 s2:被驱动表
EXPLAIN SELECT * FROM s1 INNER JOIN s2;
# 驱动表和被驱动表是 优化器决定的,他认为哪个比较好久用哪个
Quantas tabelas são usadas, quantos registros haverá
2. identificação
Normalmente, há uma seleção e um ID. Há exceções. O otimizador de consulta o otimizou.
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
Otimização do otimizador de consulta
######查询优化器可能对涉及子查询的查询语句进行重写,转变为多表查询的操作########
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field = 'a');
Resultado da execução: há apenas um ID porque o otimizador de consulta o otimizou.
Desduplicação de união
Originalmente pensei em um select e um ID, mas esperava dois.
#Union去重
# union 去重,union all 不去重
EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
# union all 不去重 所以不需要放在临时表里面
mysql> EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
resumo:
- Se os IDs forem iguais, eles podem ser considerados como um grupo e executados sequencialmente de cima para baixo.
- Em todos os grupos, quanto maior for o valor do id, maior será a prioridade e a execução será executada primeiro.
- Ponto de preocupação: Cada número de ID representa uma consulta independente. Quanto menor o número de consultas em uma instrução SQL, melhor.
3. selecione_tipo
Uma instrução de consulta grande pode conter várias palavras-chave SELECT, 每个SELECT关键字代表着一个小的查询语句
e a cláusula FROM de cada palavra-chave SELECT pode conter várias tabelas (essas tabelas são usadas para consultas de conexão).Para 每一张表都对应着执行计划输出中的一条记录
a mesma palavra-chave SELECT, para as tabelas em , seus valores de id são os mesmo.
O MySQL define um atributo chamado para cada consulta pequena representada pela palavra-chave SELECT select_type
, o que significa que desde que saibamos o valor de uma determinada consulta pequena select_type属性
, sabemos disso 小查询在整个大查询中扮演了一个什么角色
. Vamos ver
select_type
quais valores podem assumir. Consulte o oficial documentação:
nome | descrever |
---|---|
SIMPLES | SELECT simples (sem usar UNION ou subconsultas) |
PRIMÁRIO | SELEÇÃO mais externa |
UNIÃO | Segunda instrução SELECT ou posterior em um UNION |
RESULTADO DA UNIÃO | Resultado de uma UNIÃO |
SUBCONSULTA | Primeiro SELECT na subconsulta |
SUBCONSULTA DEPENDENTE | Primeiro SELECT na subconsulta, dependendo da consulta externa |
UNIÃO DEPENDENTE | Segunda instrução SELECT ou posterior em um UNION, dependendo da consulta externa |
DERIVADO | Tabela derivada |
MATERIALIZADO | Subconsulta materializada |
SUBCONSULTA NÃO CACHEÁVEL | Uma subconsulta cujo resultado não pode ser armazenado em cache e deve ser reavaliado para cada linha da consulta externa |
UNIÃO INCACHEÁVEL | A segunda ou posterior seleção em uma UNION que pertence a uma subconsulta que não pode ser armazenada em cache (consulte SUBQUERY UNCACHÁVEL) |
-
SIMPLE
# 查询语句中不包含`UNION`或者子查询的查询都算作是`SIMPLE`类型 EXPLAIN SELECT * FROM s1; #连接查询也算是`SIMPLE`类型 EXPLAIN SELECT * FROM s1 INNER JOIN s2;
-
PRIMARY
eUNION
comUNION RESULT
-
UNION RESULT
O MySQL opta por usar uma tabela temporária para completar
UNION
a desduplicação das consultas. A consulta para esta tabela temporáriaselect_type
éUNION RESULT
mostrada no exemplo acima.
#对于包含`UNION`或者`UNION ALL`或者子查询的大查询来说,它是由几个小查询组成的,其中最左边的那个 #查询的`select_type`值就是`PRIMARY` #对于包含`UNION`或者`UNION ALL`的大查询来说,它是由几个小查询组成的,其中除了最左边的那个小查询 #以外,其余的小查询的`select_type`值就是`UNION` #`MySQL`选择使用临时表来完成`UNION`查询的去重工作,针对该临时表的查询的`select_type`就是`UNION RESULT`
SQL de teste:
EXPLAIN SELECT * FROM s1 UNION SELECT * FROM s2;
-
EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;
T 关键字代表的那个查询的
select_type 就是
SUBQUERY` EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';
DEPENDENT SUBQUERY
subconsulta dependente
#如果包含子查询的查询语句不能够转为对应的`semi-join`的形式,并且该子查询是相关子查询,
#则该子查询的第一个`SELECT`关键字代表的那个查询的`select_type`就是`DEPENDENT SUBQUERY`
EXPLAIN SELECT * FROM s1
WHERE key1 IN (SELECT key1 FROM s2 WHERE s1.key2 = s2.key2) OR key3 = 'a';
#注意的是,select_type为`DEPENDENT SUBQUERY`的查询可能会被执行多次。
DEPENDENT UNION
#在包含`UNION`或者`UNION ALL`的大查询中,如果各个小查询都依赖于外层查询的话,那除了
#最左边的那个小查询之外,其余的小查询的`select_type`的值就是`DEPENDENT UNION`。
EXPLAIN SELECT * FROM s1
WHERE key1 IN (SELECT key1 FROM s2 WHERE key1 = 'a' UNION SELECT key1 FROM s1 WHERE key1 = 'b');
# 这里优化器会重构成exist
DERIVED
derivado: derivado, derivado
#对于包含`派生表`的查询,该派生表对应的子查询的`select_type`就是`DERIVED`
EXPLAIN SELECT *
FROM (SELECT key1, COUNT(*) AS c FROM s1 GROUP BY key1) AS derived_s1 WHERE c > 1;
MATERIALIZED
materializado: Inglês [məˈtɪəri:əˌlaɪzd] materializado
#当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时,
#该子查询对应的`select_type`属性就是`MATERIALIZED`
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2); #子查询被转为了物化表
-
Não entendo: por que não há atomização na subconsulta acima?
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a'; # 这个怎么不物化
-
UNCACHEABLE SUBQUERY
inacessível
-
UNCACHEABLE UNION
4. partições (opcional)
- Representa a situação de acerto na tabela particionada. Para tabelas não particionadas, este item é NULL. Em circunstâncias normais, o valor da coluna de partições no plano de execução da nossa instrução de consulta é NULL.
- https://dev.mysql.com/doc/refman/5.7/en/alter-table-partition-operations.html
- Se quiser saber mais sobre isso, você pode testá-lo da seguinte maneira. Criar tabela de partição:
-- 创建分区表,
-- 按照id分区,id<100 p0分区,其他p1分区
CREATE TABLE user_partitions (
id INT auto_increment,
NAME VARCHAR(12),PRIMARY KEY(id))
PARTITION BY RANGE(id)(
PARTITION p0 VALUES less than(100),
PARTITION p1 VALUES less than MAXVALUE
);
DESC SELECT * FROM user_partitions WHERE id>200;
Consulte os registros com ID maior que 200 (200>100, partição p1) e visualize o plano de execução. As partições são p1, o que está de acordo com nossas regras de partição.
5. digite ☆
Um registro no plano de execução representa o acesso do MySQL a uma determinada tabela 执行查询时的访问方法
, também conhecida como "tipo de acesso".As colunas nele type
indicam qual é o método de acesso, que é um indicador mais importante. Por exemplo, ver type
um valor de coluna ref
, indica que o MySQL está prestes a usar ref
o método de acesso para executar s1
uma consulta na tabela.
O método de acesso completo é o seguinte: system
, const
, eq_ref
, ref
, fulltext
, ref_or_null
, index_merge
, unique_subquery
, index_subquery
, range
, index
, ALL
.
Vamos explicar em detalhes:
-
system
Quando
只有一条记录
as estatísticas na tabela e o mecanismo de armazenamento usado pela tabela são precisos, como MyISAM e Memória, o método de acesso à tabela ésystem
. Por exemplo, vamos criar uma novaMyISAM
tabela e inserir um registro nela:mysql> CREATE TABLE t(i int) Engine=MyISAM; Query OK, 0 rows affected (0.05 sec) mysql> INSERT INTO t VALUES(1); Query OK, 1 row affected (0.01 sec)
Em seguida, analisamos o plano de execução para consultar esta tabela:
mysql> EXPLAIN SELECT * FROM t; +----+-------------+-------+------------+--------+ | id | select_type | table | partitions | type | +----+-------------+-------+------------+--------+ | 1 | SIMPLE | t | NULL | system | +----+-------------+-------+------------+--------+ 1 row in set, 1 warning (0.00 sec)
Se for innodb, se tornará ALL porque o sistema innodb não armazena o campo numérico. . MyISAM irá armazenar tal campo
-
const
#当我们根据主键或者唯一二级索引列与常数进行等值匹配时,对单表的访问方法就是`const` EXPLAIN SELECT * FROM s1 WHERE id = 10005; EXPLAIN SELECT * FROM s1 WHERE key2 = '10066';
-
eq_ref
#在连接查询时,如果被驱动表是通过主键或者唯一二级索引列等值匹配的方式进行访问的 #(如果该主键或者唯一二级索引是联合索引的话,所有的索引列都必须进行等值比较),则 #对该被驱动表的访问方法就是`eq_ref` EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id; +----+------------+------+--------+---------+---------+------------------+------+ | id | select_type| table| type | key | key_len | ref | rows | +----+------------+------+--------+---------+---------+------------------+------+ | 1 | SIMPLE | s1 | ALL | NULL | NULL | NULL | 9895 | | 1 | SIMPLE | s2 | eq_ref | PRIMARY | 4 | atguigudb1.s1.id | 1 | +----+------------+------+--------+---------+---------+------------------+------+ 2 rows in set, 1 warning (0.00 sec)
Pode-se perceber pelos resultados do plano de execução que o MySQL pretende utilizar s2 como tabela acionadora e s1 como tabela acionada, com foco no
método de acesso de s1, indicando que ele pode ser acessadoeq_ref
ao acessar a tabela s1 .通过主键的等值匹配
-
ref
#当通过普通的二级索引列与常量进行等值匹配时来查询某个表,那么对该表的访问方法就可能是`ref` EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
Resultados do:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a'; +----+-------+------+---------------+----------+---------+ | id | table | type | possible_keys | key | key_len | +----+-------+------+---------------+----------+---------+ | 1 | s1 | ref | idx_key1 | idx_key1 | 303 | +----+-------+------+---------------+----------+---------+ 1 row in set, 1 warning (0.00 sec)
Dicas: A indexação só pode ser feita se os tipos forem iguais.
EXPLAIN SELECT * FROM s1 WHERE key2 = 10066; # 这个是不会走索引的 因为key2 是字符串 # 类型不一样,mysql会加函数,进行隐式转换,一旦加上函数,就不会走索引了。
-
fulltext
Índice de texto completo
-
ref_or_null
#当对普通二级索引进行等值匹配查询,该索引列的值也可以是`NULL`值时,那么对该表的访问方法 #就可能是`ref_or_null` EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key1 IS NULL;
-
index_merge
#单表访问方法时在某些场景下可以使用`Intersection`、`Union`、 #`Sort-Union`这三种索引合并的方式来执行查询 EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
resultado
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a'; +---+-------+------------+-------------------+--------+-------------------------------+ | id| table | type | key | key_len| Extra +---+-------+------------+-------------------+--------+-------------------------------+ | 1| s1 | index_merge| idx_key1,idx_key3 | 303,303| Using union(idx_key1,idx_key3)| +---+-------+------------+-------------------+--------+-------------------------------+ 1 row in set, 1 warning (0.01 sec)
type
Pode-se observar pelo valor da coluna no plano de execuçãoindex_merge
que o MySQL pretende usar a fusão de índices para executar consultas
nas1
tabela. -
unique_subquery
#`unique_subquery`是针对在一些包含`IN`子查询的查询语句中,如果查询优化器决定将`IN`子查询 #转换为`EXISTS`子查询,而且子查询可以使用到主键进行等值匹配的话,那么该子查询执行计划的`type` #列的值就是`unique_subquery` EXPLAIN SELECT * FROM s1 WHERE key2 IN (SELECT id FROM s2 WHERE s1.key1 = s2.key1) OR key3 = 'a';
-
index_subquery
EXPLAIN SELECT * FROM s1 WHERE common_field IN (SELECT key3 FROM s2 where s1.key1 = s2.key1) OR key3 = 'a';
-
range
#如果使用索引获取某些`范围区间`的记录,那么就可能使用到`range`访问方法 EXPLAIN SELECT * FROM s1 WHERE key1 IN ('a', 'b', 'c'); #同上 EXPLAIN SELECT * FROM s1 WHERE key1 > 'a' AND key1 < 'b';
-
index
#当我们可以使用索引覆盖,但需要扫描全部的索引记录时,该表的访问方法就是`index` EXPLAIN SELECT key_part2 FROM s1 WHERE key_part3 = 'a';
Cobertura do índice,
INDEX idx_key_part(key_part1, key_part2, key_part3)
estes 3 constituem um índice compostokey_part3 está no índice composto e o campo consultado também está no índice. Basta percorrer o índice diretamente para encontrar os dados.
Pensando: A vantagem é que o índice armazena menos dados, e quanto menos dados significa menos páginas, o que pode reduzir o IO.
-
ALL
mysql> EXPLAIN SELECT * FROM s1;
De modo geral, exceto All
este método de acesso, todos os outros métodos de acesso podem usar índices. Exceto index_merge
o método de acesso, todos os outros métodos de acesso só podem usar no máximo um índice.
resumo:
Os valores resultantes do melhor para o pior são:
sistema > const > eq_ref > ref >
texto completo > ref_or_null > index_merge >unique_subquery > index_subquery > intervalo >
índice > TODOS
O objetivo da otimização do desempenho do SQL: pelo menos atingir o nível de intervalo, o requisito é o nível ref, de preferência o nível consts. (
Exigido pelo Manual de Desenvolvimento do Alibaba)
6. chaves_possíveis e chave
Na saída do plano de execução pela instrução EXPLAIN, a coluna indica quais índices são executados possible_keys
em uma determinada tabela em uma determinada instrução de consulta . 单表查询时可能用
Se existir um índice em um campo envolvido em uma consulta geral, o índice será listado, mas não poderá ser usado pela consulta. Quais são os índices key
representados pela coluna ?Se for NULL, nenhum índice será usado. 实际用到
Por exemplo, considere a seguinte consulta:
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key3 = 'a';
+----+-------------+------+------+-------------------+----------+
| id | select_type | table| type | possible_keys | key |
+----+-------------+------+------+-------------------+----------+
| 1 | SIMPLE | s1 | ref | idx_key1,idx_key3 | idx_key3 |
+----+-------------+------+------+-------------------+----------+
1 row in set, 1 warning (0.01 sec)
O valor da coluna Possible_Keys do plano de execução acima é idx_key1,idx_key3
, indicando que a consulta pode usar dois índices, e o valor da coluna key é idx_key1
, indicando que após o otimizador de consulta calcular o custo do uso de índices diferentes, ele finalmente decide usaridx_key3
idx_key3
idx_key3
Apenas um índice pode ser usado. Então ele tem que escolher um e usá-lo. Se você observar o item acima
index_merge
ou, passará pela fusão de índices.
7. key_len ☆
-
key_len: o comprimento real do índice usado (ou seja: número de bytes)
-
Quanto menor o key_len, melhor será o efeito de indexação. Isso é o que aprendi anteriormente. Key_len mais curto é mais eficiente.
-
Mas no índice conjunto, key_len é atingido uma vez mais o comprimento. Quanto maior o valor, maior será a precisão e melhor será o efeito.
#7.
EXPLAIN SELECT * FROM s1 WHERE id = 10005;
## 结果key_len =4
mysql> EXPLAIN SELECT * FROM s1 WHERE key2 = 10126;
## 结果key_len = 5
key2 é um índice exclusivo do tipo int. . Como também pode haver um valor nulo, nulo ocupa um campo. 4+1 = 5
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
## 结果key_len = 303
Motivo: idx_key_part(key_part1, key_part2, key_part3)
É a soma de três campos de 100. Cada campo pode estar vazio, então 101*3 = 303
mysql> EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a';
+----+------------+------+---------------+--------------+---------+-------+------+
| id | select_type| type | possible_keys | key | key_len | ref | rows |
+----+------------+------+---------------+--------------+---------+-------+------+
| 1 | SIMPLE | ref | idx_key_part | idx_key_part | 303 | const | 1 |
+----+------------+------+---------------+--------------+---------+-------+------+
1 row in set, 1 warning (0.00 sec)
O resultado key_len é 303
mysql> EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part2 = 'b';
+----+------------+-----+---------------+--------------+---------+------------
| id | select_type|type | possible_keys | key | key_len | ref
+----+------------+-----+---------------+--------------+---------+------------
| 1 | SIMPLE |ref | idx_key_part | idx_key_part | 606 | const,const
+----+------------+-----+---------------+--------------+---------+------------
1 row in set, 1 warning (0.00 sec)
Chave de resultado_606
O índice conjunto é atingido duas vezes aqui, com maior precisão e melhor efeito.
prática:
A fórmula de cálculo do comprimento de key_len:
varchar(10)变长字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)
varchar(10)变长字段且不允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+2(变长字段)
char(10)固定字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)
char(10)固定字段且不允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)
8. referência
# 8. ref:当使用索引列等值查询时,与索引列进行等值匹配的对象信息。
#比如只是一个常数或者是某个列。
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';
+----+-------------+------+------+---------------+----------+---------+-------+
| id | select_type | table| type | possible_keys | key | key_len | ref |
+----+-------------+------+------+---------------+----------+---------+-------+
| 1 | SIMPLE | s1 | ref | idx_key1 | idx_key1 | 303 | const |
+----+-------------+------+------+---------------+----------+---------+-------+
O tipo é type =ref, comparado com const (constante)
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.id = s2.id;
+---+------+--------+---------------+--------+------------------+-----
| id| table| type | possible_keys | key | ref | rows
+---+------+--------+---------------+--------+------------------+-----
| 1| s1 | ALL | PRIMARY | NULL | NULL | 9895
| 1| s2 | eq_ref | PRIMARY | PRIMARY| atguigudb1.s1.id | 1
+---+------+--------+---------------+--------+------------------+-----
O tipo é type =eq_ref, comparado com atguigudb1.s1.id
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s2.key1 = UPPER(s1.key1);
+----+------+------+---------------+----------+---------+------+------+----------------------+
| id | table| type | possible_keys | key | key_len | ref | rows |Extra |
+----+------+------+---------------+----------+---------+------+------+----------------------+
| 1 | s1 | ALL | NULL | NULL | NULL | NULL | 9895 |NULL |
| 1 | s2 | ref | idx_key1 | idx_key1 | 303 | func | 1 |Using index condition |
+----+------+------+---------------+----------+---------+------+------+----------------------+
Compare com um métodofunc
9. linhas ☆
# 9. rows:预估的需要读取的记录条数
# `值越小越好`
# 通常与filtered 一起使用
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z';
Quanto menor o valor das linhas, maior a probabilidade de os dados estarem em uma página, portanto o io será menor.
10. filtrado
Quanto maior melhor
O valor de filtrado refere-se à porcentagem de linhas que retornam resultados para as linhas que precisam ser lidas (o valor da coluna linhas).
Meu próprio entendimento: por exemplo, depois de ler 100 linhas, filtrado é 10%, o que significa que 100 linhas precisam ser filtradas. . . . . . . . . Isso é completamente infundado por causa da minha própria obscenidade, então só posso entender desta forma.
Se você estiver usando uma única varredura de tabela executada por um índice, ao calcular, será necessário estimar quantos registros atendem a outras condições de pesquisa, exceto as condições de pesquisa usando o índice correspondente.
EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND common_field = 'a';
Para consultas de tabela única, o valor desta coluna filtrada não tem sentido . Para nós 更关注在连接查询中驱动表对应的执行计划记录的filtered值
, ela determina o número de vezes que a tabela controlada deve ser executada (ou seja: linhas * filtradas)
EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'a';
11. Extra ☆
Como o nome sugere, Extra
as colunas são usadas para descrever algumas informações adicionais, incluindo informações adicionais que não são adequadas para exibição em outras colunas, mas são muito importantes. Podemos usar essas informações adicionais 更准确的理解MySQL到底将如何执行给定的查询语句
. Existem dezenas de informações adicionais fornecidas pelo MySQL. Vamos dar uma olhada nos destaques.
-
No tables used
Essas informações adicionais serão solicitadas quando a instrução de consulta não tiver uma cláusula FROM, como:
mysql> EXPLAIN SELECT 1;
mysql> EXPLAIN SELECT * FROM s1 WHERE 1 != 1;
-
Usando onde
Quando usamos uma varredura completa da tabela para executar uma consulta em uma determinada tabela, e
WHERE
há condições de pesquisa para a tabela nas cláusulas da instrução,Extra
as informações adicionais acima serão solicitadas na coluna.
EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';
Quando houver outras condições além do índice, este prompt também aparecerá.
#当使用索引访问来执行对某个表的查询,并且该语句的`WHERE`子句中
#有除了该索引包含的列之外的其他搜索条件时,在`Extra`列中也会提示上述额外信息。
explain SELECT * FROM s1 WHERE key1 = 'fUhcQU' and common_field = 'uDHCOnalcF';
- Nenhuma linha mínima/máxima correspondente`
Quando houver uma função agregada na lista de consulta MIN
, MAX
mas não houver WHERE
registros que correspondam às condições de pesquisa na cláusula, essas informações adicionais serão solicitadas.
# 数据库不存在 QLjKYOx
EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'QLjKYOx';
# 数据库存在 QLjKYO
EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'QLjKYO';
Using index
Quando nossa lista de consulta e condições de pesquisa contêm apenas colunas que pertencem a um determinado índice, ou seja, quando um índice de cobertura pode ser utilizado, Extra
serão solicitadas informações adicionais para a coluna.
Por exemplo, a consulta a seguir só precisa ser usada idx_key1
e não requer operações de retorno de tabela:
EXPLAIN SELECT key1 FROM s1 WHERE key1 = 'a';
Using index condition
Embora colunas de índice apareçam em algumas condições de pesquisa, o índice não pode ser usado para visualizar o material didático e compreender as condições do índice.
SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';
mysql> EXPLAIN SELECT * FROM s1 WHERE key1 > 'z' AND key1 LIKE '%a';
-
Etapa 1. Aqui key1 > 'z' percorreu o índice e encontrou 378 dados. . .
Passo 2. key1 LIKE '%b';Esta condição ainda é o índice key1, então a seguir só precisamos percorrer esses 378 índices. Quais correspondem a '%a'
Etapa 3. Filtre os índices válidos na etapa 2. . Isto está usando a condição de índice.
Passo 4. Retorne os índices que atendem às condições para consultar a tabela.
Descrição completa:
O
key1 > 'z'
índice pode ser usado, maskey1 LIKE '%a '
o índice não pode ser usado. Nas versões anteriores do MySQL, esta consulta era executada de acordo com os seguintes passos:- Primeiro, de acordo com a condição key1 > 'z',
idx_key1
obtenha o registro do índice secundário correspondente do índice secundário. - Retorne à tabela com base no valor da chave primária no registro do índice secundário obtido na etapa anterior, encontre o registro completo do usuário, verifique se o registro atende a essa condição
key1 LIKE '%a'
e adicione o registro qualificado ao conjunto de resultados final.
No entanto, embora
key1 LIKE ‘%a'
um intervalo de intervalo não possa ser formado para participarrange
da execução do método de acesso, esta condição envolve apenaskey1
colunas, então o MySQL melhorou as etapas acima:-
Primeiro, com base
key1 > 'z'
nesta condição, localizeidx_key1
o registro do índice secundário correspondente no índice secundário. -
Para o registro de índice secundário especificado, em vez de voltar correndo para a tabela, primeiro verifique se o registro atende a
key1 LIKE ‘%a'
essa condição.Se essa condição não for atendida, não há necessidade de retornar o registro de índice secundário à tabela. -
key1 LIKE '%a'
Execute operações de retorno de tabela para registros de índice secundário que atendam a essa condição.
Dizemos que a operação de retorno da tabela é na verdade um IO aleatório, que consome relativamente tempo, portanto, embora as modificações acima só melhorem um pouco, podem economizar muitos custos na operação de retorno da tabela. MySQL chama sua melhoria
索引条件下推
(nome em inglês: Index Condition Pushdown). Caso este recurso seja utilizado durante a execução da instrução de consulta索引条件下推
, ele será exibido na coluna ExtraUsing index condition
- Primeiro, de acordo com a condição key1 > 'z',
-
Using join buffer (Block Nested Loop)
Campos sem índices estão associados a tabelas.
Durante a execução da consulta de junção, quando a tabela dirigida não consegue usar efetivamente o índice para acelerar o acesso, o MySQL geralmente alocará um
join buffer
bloco de memória nomeado para acelerar a consulta, que é disso que estamos falando.基于块的嵌套循环算法
mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;
-
Not exists
Quando usamos uma junção esquerda (externa), se a cláusula contém uma condição de pesquisa
WHERE
que exige que uma coluna da tabela controlada seja igual a um valor, e essa coluna não tem permissão para armazenar valores, então na coluna Extra da execução plano da mesa Informações adicionaisNULL
NULL
Not exists
EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.id IS NULL; # 都表关联了,,关联字段怎么会等于 is null
Using intersect(...) 、 Using union(...) 和 Using sort_union(...)
-
Extra
Se um prompt aparecer na coluna do plano de execuçãoUsing intersect(...)
, significa que você está pronto para usarIntersect
o índice. -
Execute a consulta mesclando. O nome entre parênteses
...
indica o nome do índice que precisa ser mesclado; -
Se um prompt aparecer
Using union(...)
, significa que você está pronto para usarUnion
a mesclagem de índices para executar a consulta; -
Um prompt aparece
Using sort_union(...)
indicando queSort-Union
a consulta deve ser executada usando a mesclagem de índices.EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
-
Zero limit
-
Using filesort
Em alguns casos, os índices podem ser usados para classificar registros no conjunto de resultados, como a seguinte consulta:
EXPLAIN SELECT * FROM s1 ORDER BY key1 LIMIT 10;
Esta instrução de consulta pode usar
idx_key1
o índice para recuperar diretamente os 10 registros da coluna key1 e, em seguida, executar a operação da tabela. Porém, em muitos casos, a operação de classificação não pode usar o índice e só pode ser classificada na memória (quando há poucos registros) ou no disco (quando há muitos registros).O MySQL usa este método de classificação na memória ou em disco.Chamado coletivamente de classificação de arquivos (nome em inglês:filesort
).Using filesort
Se uma consulta exigir a execução da classificação de arquivos, um prompt será exibido na coluna Extra do plano de execução.EXPLAIN SELECT * FROM s1 ORDER BY common_field LIMIT 10;
-
Using temporary
Durante a execução de muitas consultas, o MySQL pode usar tabelas temporárias para completar algumas funções, como desduplicação, classificação, etc. Por exemplo, quando executamos muitas consultas contendo, e outras cláusulas, se não pudermos usar efetivamente o índice para Após
DISTINCT
aGROUP BY
conclusãoUNION
Se uma tabela temporária interna for usada na consulta, um promptExtra
será exibido na coluna do plano de execução.Using temporary
EXPLAIN SELECT DISTINCT common_field FROM s1;
#执行计划中出现`Using temporary`并不是一个好的征兆,因为建立与维护临时表要付出很大成本的,所以
#我们`最好能使用索引来替代掉使用临时表`。比如:扫描指定的索引idx_key1即可
EXPLAIN SELECT key1, COUNT(*) AS amount FROM s1 GROUP BY key1;
12. Resumo
- EXPLAIN não considera vários caches
- EXPLAIN não pode exibir o trabalho de otimização feito pelo MySQL ao executar a consulta
- EXPLAIN não informa sobre gatilhos, procedimentos armazenados ou como as funções definidas pelo usuário afetam sua consulta.
- Algumas estatísticas são estimativas, não valores exatos
7. Uso adicional de EXPLAIN
7.1 EXPLICAR quatro formatos de saída
Vamos falar sobre o formato de saída de EXPLAIN. EXPLAIN pode gerar quatro formatos : 传统格式
, JSON格式
e TREE格式
. 可
视化输出
Os usuários podem escolher o formato que mais lhes convém de acordo com suas necessidades.
1. Formato tradicional
O formato tradicional é simples e direto, tendo como saída uma tabela que resume o plano de consulta.
mysql> EXPLAIN SELECT s1.key1, s2.key1 FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE
s2.common_field IS NOT NULL;
+----+-------------+-------+------------+------+---------------+-------
| id | select_type | table | partitions | type | possible_keys | key
+----+-------------+-------+------------+------+---------------+-------
| 1 | SIMPLE | s2 | NULL | ALL | idx_key1 | NULL
| 1 | SIMPLE | s1 | NULL | ref | idx_key1 | idx_ke
+----+-------------+-------+------------+------+---------------+-------
2 rows in set, 1 warning (0.00 sec)
2. Formato JSON
A saída da instrução introduzida no primeiro formato EXPLAIN
carece de um atributo importante que mede a qualidade do plano de execução - 成本
. O formato JSON são as informações de saída em quatro formatos 最详尽的格式
, que contêm informações de custo de execução.
-
Formato JSON: adicione-o entre a palavra EXPLAIN e a instrução de consulta real
FORMAT=JSON
.EXPLAIN FORMAT=JSON SELECT ....
-
Correspondência entre a coluna EXPLAIN e JSON: (da documentação do MySQL 5.7)
Desta forma, podemos obter um plano de execução em formato json, que contém o custo do plano, como este:
mysql> EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a' \G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1360.07"
},
"nested_loop": [
{
"table": {
"table_name": "s1",
"access_type": "ALL",
"possible_keys": [
"idx_key1"
],
"rows_examined_per_scan": 9895,
"rows_produced_per_join": 989,
"filtered": "10.00",
"cost_info": {
"read_cost": "914.80",
"eval_cost": "98.95",
"prefix_cost": "1013.75",
"data_read_per_join": "1M"
},
"used_columns": [
"id",
"key1",
"key2",
"key3",
"key_part1",
"key_part2",
"key_part3",
"common_field"
],
"attached_condition": "((`atguigudb1`.`s1`.`common_field` = 'a') and (`atguigudb1`.`s1`.`key1` is not null))"
}
},
{
"table": {
"table_name": "s2",
"access_type": "eq_ref",
"possible_keys": [
"idx_key2"
],
"key": "idx_key2",
"used_key_parts": [
"key2"
],
"key_length": "5",
"ref": [
"atguigudb1.s1.key1"
],
"rows_examined_per_scan": 1,
"rows_produced_per_join": 989,
"filtered": "100.00",
"index_condition": "(cast(`atguigudb1`.`s1`.`key1` as double) = cast(`atguigudb1`.`s2`.`key2` as double))",
"cost_info": {
"read_cost": "247.38",
"eval_cost": "98.95",
"prefix_cost": "1360.08",
"data_read_per_join": "1M"
},
"used_columns": [
"id",
"key1",
"key2",
"key3",
"key_part1",
"key_part2",
"key_part3",
"common_field"
]
}
}
]
}
}
1 row in set, 2 warnings (0.01 sec)
Usamos #
o formulário seguido de comentários para explicar EXPLAIN FORMAT=JSON
o conteúdo do extrato, mas você pode
ter dúvidas. cost_info
Os custos em " " parecem estranhos. Como são calculados? Vejamos primeiro a parte “ ” s1
da tabela :cost_info
"cost_info": {
"read_cost": "914.80",
"eval_cost": "98.95",
"prefix_cost": "1013.75",
"data_read_per_join": "1M"
}
-
read_cost
É composto pelas seguintes duas partes:- Custo de IO
- Custo de CPU para detecção
rows × (1 - filter)
de registros
Dicas: linhas e filtros são as colunas de saída do plano de execução que apresentamos anteriormente. No plano de execução no formato JSON, as linhas são equivalentes a rows_examined_per_scan e o nome filtrado permanece inalterado.
-
eval_cost
É assim que é calculadoO custo de detecção
rows × filter
de registros. -
prefix_cost
Esse é o custo de consultar apenas a tabelas1
, ou seja:read_cost + eval_cost
-
data_read_per_join
Indica a quantidade de dados que precisam ser lidos nesta consulta.
A parte "cost_info" da tabela s2 tem esta aparência:
"cost_info": {
"read_cost": "247.38",
"eval_cost": "98.95",
"prefix_cost": "1360.08",
"data_read_per_join": "1M"
}
Como s2
a tabela é uma tabela controlada, ela pode ser lida várias vezes. read_cost
E aqui eval_cost
estão os valores acumulados após acessar s2
a tabela várias vezes
. Todos estão preocupados principalmente com o valor interno prefix_cost
que representa o custo estimado de toda a consulta de conexão, isto é , a soma dos custos após uma única consulta da tabela e múltiplas consultas da tabela,
seja s1
:s2
247.38 + 98.95 + 1013.75 = 1360.08
3. Formato ÁRVORE
O formato TREE é um novo formato introduzido após a versão 8.0.16. Ele descreve principalmente como consultar com base na consulta 各个部分之间的关系
e各部分的执行顺序
mysql> EXPLAIN FORMAT=tree SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'\G
*************************** 1. row ***************************
EXPLAIN: -> Nested loop inner join (cost=1360.08 rows=990)
-> Filter: ((s1.common_field = 'a') and (s1.key1 is not null)) (cost=1013.75 rows=990)
-> Table scan on s1 (cost=1013.75 rows=9895)
-> Single-row index lookup on s2 using idx_key2 (key2=s1.key1), with index condition: (cast(s1.key1 as double) = cast(s2.key2 as double)) (cost=0.25 rows=1)
1 row in set, 1 warning (0.00 sec)
4. Saída visual
Saída visual, você pode visualizar visualmente o plano de execução do MySQL por meio do MySQL Workbench. Ao clicar no ícone da lupa
do Workbench, você pode gerar um plano de consulta visual.
A figura acima mostra as tabelas em ordem de junção, da esquerda para a direita. As caixas vermelhas indicam 全表扫描
, enquanto as caixas verdes indicam o uso 索引查找
. Para cada tabela,
são exibidos os índices utilizados. Observe também que acima da caixa de cada tabela estão estimativas do número de linhas descobertas por cada acesso à tabela e o
custo de acesso a essa tabela.
7.2 Uso de SHOW WARNINGS
mysql> EXPLAIN SELECT s1.key1, s2.key1 FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE
s2.common_field IS NOT NULL;
Use explicar imediatamente após usar explicarSHOW WARNINGS \G
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select `atguigudb1`.`s1`.`key1` AS `key1`,`atguigudb1`.`s2`.`key1` AS `key1` from `atguigudb1`.`s1` join `atguigudb1`.`s2` where ((`atguigudb1`.`s1`.`key1` = `atguigudb1`.`s2`.`key1`) and (`atguigudb1`.`s2`.`common_field` is not null))
1 row in set (0.00 sec)
Você pode ver as instruções realmente executadas pelo otimizador de consulta
Resistir pode não necessariamente permitir que ele funcione
Você pode ver que SHOW WARNINGS
as informações exibidas possuem três campos, a saber Level
, Code
, e Message
. Nossas informações mais comuns
são as informações com o Código 1003. Quando o valor do Código é 1003, as informações exibidas no campo Mensagem são semelhantes à instrução após reescrever查询优化器
nossa instrução de consulta . Por exemplo, nossa consulta acima era originalmente uma consulta de junção esquerda (externa), mas há uma condição de s2.common_field IS NOT NULL, que fará com que o otimizador de consulta otimize a consulta de junção esquerda (externa) em uma consulta de junção interna, do campo Também pode ser visto que o LEFT JOIN original se tornou um JOIN (inner join).SHOW WARNINGS
Message
8. Analise o plano de execução do otimizador: rastreamento
OPTIMIZER_TRACE
É um recurso de rastreamento introduzido no MySQL 5.6. Ele pode rastrear várias decisões tomadas pelo otimizador (como métodos de acesso a tabelas, vários cálculos de custos, várias transformações, etc.) e registrar os resultados do rastreamento na tabela INFORMATION_SCHEMA.OPTIMIZER_TRACE
.
Este recurso está desativado por padrão. Ative o rastreio, defina o formato como JSON e defina o tamanho máximo de memória que o rastreio pode usar para evitar a exibição completa durante o processo de análise devido à memória padrão ser muito pequena.
SET optimizer_trace="enabled=on",end_markers_in_json=on;
set optimizer_trace_max_mem_size=1000000;
Após ligá-lo, as seguintes afirmações podem ser analisadas:
- SELECIONAR
- INSERIR
- SUBSTITUIR
- ATUALIZAR
- EXCLUIR
- EXPLICAR
- DEFINIR
- DECLARAR
- CASO
- SE
- RETORNAR
- CHAMAR
Teste: execute a seguinte instrução SQL
select * from student where id < 10;
Por fim, consulte information_schema.optimizer_trace para saber como o MySQL executa o SQL:
select * from information_schema.optimizer_trace\G
mysql> select * from information_schema.optimizer_trace\G
*************************** 1. row ***************************
# //第1部分:查询语句
QUERY: select * from student where id < 10
//第2部分:QUERY字段对应语句的跟踪信息
TRACE: {
"steps": [
{
"join_preparation": { /*预备工作*/
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `student`.`id` AS `id`,`student`.`stuno` AS `stuno`,`student`.`name` AS `name`,`student`.`age` AS `age`,`student`.`classId` AS `classId` from `student` where (`student`.`id` < 10)"
}
] /* steps */
} /* join_preparation */
},
{
"join_optimization": {
/*进行优化*/
"select#": 1,
"steps": [
{
"condition_processing": {
/*条件处理*/
"condition": "WHERE",
"original_condition": "(`student`.`id` < 10)",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "(`student`.`id` < 10)"
},
{
"transformation": "constant_propagation",
"resulting_condition": "(`student`.`id` < 10)"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "(`student`.`id` < 10)"
}
] /* steps */
} /* condition_processing */
},
{
"substitute_generated_columns": {
/*替换生成的列*/
} /* substitute_generated_columns */
},
{
"table_dependencies": [ /* 表的依赖关系*/
{
"table": "`student`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
] /* depends_on_map_bits */
}
] /* table_dependencies */
},
{
"ref_optimizer_key_uses": [ /* 使用键*/
] /* ref_optimizer_key_uses */
},
{
"rows_estimation": [ /*行判断*/
{
"table": "`student`",
"range_analysis": {
"table_scan": {
"rows": 3945207,
"cost": 404306
} /* table_scan */,/*表扫描*/
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": true,
"key_parts": [
"id"
] /* key_parts */
}
] /* potential_range_indexes */,
"setup_range_conditions": [
] /* 设置条件范围 */,
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
} /* group_index_range */,
"skip_scan_range": {
"potential_skip_scan_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "query_references_nonkey_column"
}
] /* potential_skip_scan_indexes */
} /* skip_scan_range */,
"analyzing_range_alternatives": {
/*分析范围选项*/
"range_scan_alternatives": [
{
"index": "PRIMARY",
"ranges": [
"id < 10"
] /* ranges */,
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"in_memory": 0.159895,
"rows": 9,
"cost": 1.79883,
"chosen": true
}
] /* range_scan_alternatives */,
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
} /* analyzing_roworder_intersect */
} /* analyzing_range_alternatives */,
"chosen_range_access_summary": {
/*选择范围访问摘要*/
"range_access_plan": {
"type": "range_scan",
"index": "PRIMARY",
"rows": 9,
"ranges": [
"id < 10"
] /* ranges */
} /* range_access_plan */,
"rows_for_plan": 9,
"cost_for_plan": 1.79883,
"chosen": true
} /* chosen_range_access_summary */
} /* range_analysis */
}
] /* rows_estimation */
},
{
"considered_execution_plans": [/*考虑执行计划*/
{
"plan_prefix": [
] /* plan_prefix */,
"table": "`student`",
"best_access_path": {
/*最佳访问路径*/
"considered_access_paths": [
{
"rows_to_scan": 9,
"access_type": "range",
"range_details": {
"used_index": "PRIMARY"
} /* range_details */,
"resulting_rows": 9,
"cost": 2.69883,
"chosen": true
}
] /* considered_access_paths */
} /* best_access_path */,
"condition_filtering_pct": 100, /*行过滤百分比*/
"rows_for_plan": 9,
"cost_for_plan": 2.69883,
"chosen": true
}
] /* considered_execution_plans */
},
{
"attaching_conditions_to_tables": { /*将条件附加到表上*/
"original_condition": "(`student`.`id` < 10)",
"attached_conditions_computation": [
] /* attached_conditions_computation */,
"attached_conditions_summary": [ /*附加条件概要*/
{
"table": "`student`",
"attached": "(`student`.`id` < 10)"
}
] /* attached_conditions_summary */
} /* attaching_conditions_to_tables */
},
{
"finalizing_table_conditions": [
{
"table": "`student`",
"original_table_condition": "(`student`.`id` < 10)",
"final_table_condition ": "(`student`.`id` < 10)"
}
] /* finalizing_table_conditions */
},
{
"refine_plan": [ /*精简计划*/
{
"table": "`student`"
}
] /* refine_plan */
}
] /* steps */
} /* join_optimization */
},
{
"join_execution": { /*执行*/
"select#": 1,
"steps": [
] /* steps */
} /* join_execution */
}
] /* steps */
}
/
/*第3部分:跟踪信息过长时,被截断的跟踪信息的字节数。*/
MISSING_BYTES_BEYOND_MAX_MEM_SIZE: 0 /*丢失的超出最大容量的字节*/
/*第4部分:执行跟踪语句的用户是否有查看对象的权限。当不具有权限时,该列信息为1且TRACE字段为空,一般在
调用带有SQL SECURITY DEFINER的视图或者是存储过程的情况下,会出现此问题。*/
INSUFFICIENT_PRIVILEGES: 0 /*缺失权限*/
1 row in set (0.01 sec)
9. Esquema view-sys de análise de monitoramento MySQL
9.1 Resumo da visualização do esquema Sys
1. Relacionado ao host : começando com host_summary, ele resume principalmente as informações de atraso de IO.
2. Relacionado ao Innodb : começando com o innodb, ele resume as informações do buffer do innodb e as informações sobre as transações que aguardam bloqueios do innodb.
3. Relacionado a E/S : começando com io, ele resume a espera por E/S e o uso de E/S.
4. Uso de memória : começando com memória, mostrando o uso de memória da perspectiva de host, thread, evento, etc.
5. Informações de conexão e sessão : lista de processos e visualizações relacionadas à sessão resumem as informações relacionadas à sessão.
6. Relacionado à tabela : as visualizações que começam com schema_table mostram as informações estatísticas da tabela.
7. Informações do índice : estatísticas sobre o uso do índice, incluindo índices redundantes e índices não utilizados.
8. Relacionado à instrução : começando com a instrução, contém informações da instrução para realizar uma varredura completa da tabela, usar tabelas temporárias, classificação, etc.
9. Relacionado ao usuário : a visualização que começa com o usuário conta a E/S do arquivo e as estatísticas da instrução de execução usadas pelo usuário.
10. Informações relacionadas aos eventos em espera : começando com espera, mostrando o atraso dos eventos em espera.
9.2 Cenários de uso da visualização do esquema Sys
Status do índice
#1. 查询冗余索引
select * from sys.schema_redundant_indexes;
#2. 查询未使用过的索引
select * from sys.schema_unused_indexes;
#3. 查询索引的使用情况
select index_name,rows_selected,rows_inserted,rows_updated,rows_deleted
from sys.schema_index_statistics where table_schema='dbname' ;
relacionado à tabela
# 1. 查询表的访问量
select table_schema,table_name,sum(io_read_requests+io_write_requests) as io from
sys.schema_table_statistics group by table_schema,table_name order by io desc;
# 2. 查询占用bufferpool较多的表
select object_schema,object_name,allocated,data
from sys.innodb_buffer_stats_by_table order by allocated limit 10;
# 3. 查看表的全表扫描情况
select * from sys.statements_with_full_table_scans where db='dbname';
Declaração relacionada
#1. 监控SQL执行的频率
select db,exec_count,query from sys.statement_analysis
order by exec_count desc;
#2. 监控使用了排序的SQL
select db,exec_count,first_seen,last_seen,query
from sys.statements_with_sorting limit 1;
#3. 监控使用了临时表或者磁盘临时表的SQL
select db,exec_count,tmp_tables,tmp_disk_tables,query
from sys.statement_analysis where tmp_tables>0 or tmp_disk_tables >0
order by (tmp_tables+tmp_disk_tables) desc;
Relacionado a IO
#1. 查看消耗磁盘IO的文件
select file,avg_read,avg_write,avg_read+avg_write as avg_io
from sys.io_global_by_file_by_bytes order by avg_read limit 10;
Relacionado ao Innodb
#1. 行锁阻塞情况
select * from sys.innodb_lock_waits;
aviso de risco:
Ao consultar a biblioteca sys, o MySQL consumirá muitos recursos para coletar informações relevantes. Em casos graves, as solicitações de negócios podem ser bloqueadas, causando falhas. Recomenda-se que aqueles em
不要频繁
sistemas de consulta de produção ou performance_schema e information_schema concluam monitoramento, inspeção e outras tarefas.