Noções básicas de MySQL (27) Uso de ferramentas de análise de desempenho

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).

Insira a descrição da imagem aqui
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_timedefinir 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 EXPLAINvisualizar o plano de execução da instrução SQL correspondente de maneira direcionada ou visualizar show profileo 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: 慢查询、 EXPLAINe SHOWPROFILING.

resumo:
Insira a descrição da imagem aqui

2. Visualize os parâmetros de desempenho do sistema

No MySQL, você pode usar instruções SHOW STATUSpara 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_costSe 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:

  1. 位置决定效率. 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.

  2. 批量决定效率. 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_timeo 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:
Insira a descrição da imagem aqui
Você pode ver que a análise de consulta lenta foi ativada e o arquivo foi salvo em /var/lib/mysql/atguigu02-slow.logFile
.

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%';

Insira a descrição da imagem aqui

# 测试发现:设置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%';  

Insira a descrição da imagem aqui
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.cnfo arquivo, adicione [mysqld]下ou modifique parâmetros long_query_timee reinicie o servidor MySQL. |slow_query_logslow_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-logspara 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 2os 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:

  1. converting HEAP to MyISAM: o resultado da consulta é muito grande, a memória não é suficiente e os dados foram movidos para o disco.
  2. 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.
  3. Copying to tmp table on disk:Copie a tabela temporária da memória para o disco, tome cuidado!
  4. 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 EXPLAINinstruções para nos ajudar a visualizar o plano de execução específico de uma determinada instrução de consulta. Compreender os EXPLAINvá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
Insira a descrição da imagem aqui
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.UPDATEDELETE
  • Nas versões anteriores à 5.7, você partitionsprecisa usar explain partitionso comando se quiser exibi-lo; você precisa filteredusar explain extendedo 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.

Insira a descrição da imagem aqui

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;

Insira a descrição da imagem aqui
EXPLAINAs 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 EXPLAINa 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;

Insira a descrição da imagem aqui

#s1:驱动表  s2:被驱动表
EXPLAIN SELECT * FROM s1 INNER JOIN s2;
# 驱动表和被驱动表是 优化器决定的,他认为哪个比较好久用哪个

Insira a descrição da imagem aqui

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';

Insira a descrição da imagem aqui

mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;

Insira a descrição da imagem aqui

mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';

Insira a descrição da imagem aqui
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.

Insira a descrição da imagem aqui

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;

Insira a descrição da imagem aqui

# union all 不去重  所以不需要放在临时表里面
mysql> EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;

Insira a descrição da imagem aqui
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_typequais 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;
    
  • PRIMARYe UNIONcomUNION RESULT

    • UNION RESULT

      O MySQL opta por usar uma tabela temporária para completar UNIONa desduplicação das consultas. A consulta para esta tabela temporária select_typeé UNION RESULTmostrada 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;	
    

Insira a descrição da imagem aqui

EXPLAIN SELECT * FROM s1 UNION ALL SELECT * FROM s2;

Insira a descrição da imagem aqui
T 关键字代表的那个查询的select_type 就是SUBQUERY` EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';

Insira a descrição da imagem aqui
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`的查询可能会被执行多次。

Insira a descrição da imagem aqui
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

Insira a descrição da imagem aqui
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;

Insira a descrição da imagem aqui
MATERIALIZED

materializado: Inglês [məˈtɪəri:əˌlaɪzd] materializado

#当查询优化器在执行包含子查询的语句时,选择将子查询物化之后与外层查询进行连接查询时,
#该子查询对应的`select_type`属性就是`MATERIALIZED`
EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2); #子查询被转为了物化表 

Insira a descrição da imagem aqui

  • 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.

Insira a descrição da imagem aqui

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 typeindicam qual é o método de acesso, que é um indicador mais importante. Por exemplo, ver typeum valor de coluna ref, indica que o MySQL está prestes a usar refo método de acesso para executar s1uma 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 nova MyISAMtabela 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 acessado eq_refao 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)
    
    

    typePode-se observar pelo valor da coluna no plano de execução index_mergeque o MySQL pretende usar a fusão de índices para executar consultas
    na s1tabela.

  • 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 composto

    key_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 Alleste método de acesso, todos os outros métodos de acesso podem usar índices. Exceto index_mergeo 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_keysem 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 keyrepresentados 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_key3idx_key3idx_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';

Insira a descrição da imagem aqui

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';

Insira a descrição da imagem aqui

11. Extra ☆

Como o nome sugere, Extraas 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;				

Insira a descrição da imagem aqui

mysql> EXPLAIN SELECT * FROM s1 WHERE 1 != 1;

Insira a descrição da imagem aqui

  • Usando onde

    Quando usamos uma varredura completa da tabela para executar uma consulta em uma determinada tabela, e WHEREhá condições de pesquisa para a tabela nas cláusulas da instrução, Extraas informações adicionais acima serão solicitadas na coluna.

EXPLAIN SELECT * FROM s1 WHERE common_field = 'a';

Insira a descrição da imagem aqui
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';

Insira a descrição da imagem aqui

  • Nenhuma linha mínima/máxima correspondente`

Quando houver uma função agregada na lista de consulta MIN, MAXmas não houver WHEREregistros 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';

Insira a descrição da imagem aqui

 # 数据库存在 QLjKYO
 EXPLAIN SELECT MIN(key1) FROM s1 WHERE key1 = 'QLjKYO';

Insira a descrição da imagem aqui

  • 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, Extraserão solicitadas informações adicionais para a coluna.

Por exemplo, a consulta a seguir só precisa ser usada idx_key1e não requer operações de retorno de tabela:

EXPLAIN SELECT key1 FROM s1 WHERE key1 = 'a';

Insira a descrição da imagem aqui
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';

Insira a descrição da imagem aqui

  • 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, mas key1 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_key1obtenha 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 participar rangeda execução do método de acesso, esta condição envolve apenas key1colunas, então o MySQL melhorou as etapas acima:

    • Primeiro, com base key1 > 'z'nesta condição, localize idx_key1o 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

  • 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 bufferbloco 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;
    

Insira a descrição da imagem aqui

  • Not exists

    Quando usamos uma junção esquerda (externa), se a cláusula contém uma condição de pesquisa WHEREque 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 adicionaisNULLNULLNot exists

    EXPLAIN SELECT * FROM s1 LEFT JOIN s2 ON s1.key1 = s2.key1 WHERE s2.id IS NULL;
    # 都表关联了,,关联字段怎么会等于 is null
    

    Insira a descrição da imagem aqui
    Using intersect(...) 、 Using union(...) 和 Using sort_union(...)

  • ExtraSe um prompt aparecer na coluna do plano de execução Using intersect(...), significa que você está pronto para usar Intersecto í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 usar Uniona mesclagem de índices para executar a consulta;

  • Um prompt aparece Using sort_union(...)indicando que Sort-Uniona consulta deve ser executada usando a mesclagem de índices.

     EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' OR key3 = 'a';
    

Insira a descrição da imagem aqui

  • 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_key1o í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 filesortSe 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 DISTINCTa GROUP BYconclusão UNIONSe uma tabela temporária interna for usada na consulta, um prompt Extraserá exibido na coluna do plano de execução.Using temporary

     EXPLAIN SELECT DISTINCT common_field FROM s1;
     
    

Insira a descrição da imagem aqui

 #执行计划中出现`Using temporary`并不是一个好的征兆,因为建立与维护临时表要付出很大成本的,所以
 #我们`最好能使用索引来替代掉使用临时表`。比如:扫描指定的索引idx_key1即可
 EXPLAIN SELECT key1, COUNT(*) AS amount FROM s1 GROUP BY key1;

Insira a descrição da imagem aqui

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 EXPLAINcarece 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)

Insira a descrição da imagem aqui
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)

Insira a descrição da imagem aqui
Usamos #o formulário seguido de comentários para explicar EXPLAIN FORMAT=JSONo conteúdo do extrato, mas você pode
ter dúvidas. cost_infoOs custos em " " parecem estranhos. Como são calculados? Vejamos primeiro a parte “ ” s1da 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 é calculado

    O custo de detecção rows × filterde registros.

  • prefix_costEsse é o custo de consultar apenas a tabela s1, ou seja:

    read_cost + eval_cost

  • data_read_per_joinIndica 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 s2a tabela é uma tabela controlada, ela pode ser lida várias vezes. read_costE aqui eval_costestão os valores acumulados após acessar s2a tabela várias vezes
. Todos estão preocupados principalmente com o valor interno prefix_costque 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.

Insira a descrição da imagem aqui
Insira a descrição da imagem aqui
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 WARNINGSas 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 WARNINGSMessage

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.

Acho que você gosta

Origin blog.csdn.net/zhufei463738313/article/details/130624101
Recomendado
Clasificación