Estou trabalhando em um esquema para um sistema de análise que rastreia os tempos de uso e é necessário ver o tempo total de uso em um determinado período.
Para dar um exemplo simples, esse tipo de consulta seria executado com frequência:
select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
Essa consulta normalmente leva cerca de 7 segundos em uma tabela que é muito preenchida. Possui ~ 35 milhões de linhas, o MyISAM no MySQL é executado no Amazon RDS (db.m3.xlarge).
A eliminação da cláusula WHERE faz com que a consulta demore apenas 4 segundos e a adição de uma segunda cláusula (time_off> XXX) adiciona mais 1,5 segundos, elevando o tempo da consulta para 8,5 segundos.
Como eu sei que esses tipos de consultas serão normalmente feitos, eu gostaria de otimizar as coisas para que sejam mais rápidas, de preferência abaixo de 5 segundos.
Comecei adicionando um índice em time_on e, apesar de acelerar drasticamente uma consulta WHERE "=", ela não teve efeito na consulta ">". Existe uma maneira de criar um índice que acelere as consultas WHERE ">" ou "<"?
Ou, se houver outras sugestões sobre o desempenho desse tipo de consulta, entre em contato.
Nota: Estou usando o campo "diff_ms" como uma etapa de desnormalização (igual a time_off - time_on) que melhora o desempenho da agregação em cerca de 30% a 40%.
Estou criando o índice com este comando:
ALTER TABLE writetest_table ADD INDEX time_on (time_on) USING BTREE;
A execução de "explicação" na consulta original (com "time_on>") indica que time_on é uma "chave possível" e o tipo de seleção é "SIMPLES". A coluna "extra" diz "Usando onde" e "tipo" é "TUDO". Depois que o índice foi adicionado, a tabela diz que "time_on" é o tipo de chave "MUL", que parece correto, pois o mesmo tempo pode estar presente duas vezes.
Aqui está o esquema da tabela:
CREATE TABLE `writetest_table` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`sessionID` int(11) DEFAULT NULL,
`time_on` timestamp NULL DEFAULT NULL,
`time_off` timestamp NULL DEFAULT NULL,
`diff_ms` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `time_on` (`time_on`)
) ENGINE=MyISAM AUTO_INCREMENT=50410902 DEFAULT CHARSET=latin1;
UPDATE: Criei o seguinte índice com base na resposta do ypercube, mas isso aumenta o tempo de consulta da primeira consulta para cerca de 17 segundos!
ALTER TABLE writetest_table ADD INDEX time_on__diff_ms__ix (time_on, diff_ms) ;
ATUALIZAÇÃO 2: saída EXPLAIN
mysql> explain select sum(diff_ms) from writetest_table where time_on > '2015-07-13 15:11:56';
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
| 1 | SIMPLE | writetest_table_old | index | time_on__diff_ms__ix | time_on__diff_ms__ix | 10 | NULL | 35831102 | Using where; Using index |
+----+-------------+---------------------+-------+----------------------+----------------------+---------+------+----------+--------------------------+
1 row in set (0.00 sec)
Atualização 3: resultado da consulta solicitada
mysql> SELECT time_on FROM writetest_table ORDER BY time_on LIMIT 1;
+---------------------+
| time_on |
+---------------------+
| 2015-07-13 15:11:56 |
+---------------------+
1 row in set (0.01 sec)
time_on
ediff_ms
)? O que acontece se você adicionar na consultaWHERE ... AND diff_ms IS NOT NULL
?SELECT COUNT(*), COUNT(diff_ms) FROM writetest_table;
writetest_table_old
" enquanto a consulta o possuifrom writetest_table
. Isso é um erro de digitação ou você executa a consulta em uma tabela diferente?Respostas:
Eu acho que estou começando a entender.
Quando eu pedi para você correr
Você disse que era isso
2015-07-13 15:11:56
que você tem na suaWHERE
cláusulaQuando você fez a consulta
Ele executou uma verificação de tabela completa de 35,8 milhões de linhas.
Quando você fez a consulta
Ele executou uma verificação de índice completa de 35,8 milhões de linhas.
Faz totalmente sentido que a consulta sem a cláusula WHERE seja mais rápida. Por quê ?
A varredura da tabela exibirá 35,8 milhões de linhas em uma passagem linear.
O EXPLAIN na consulta com o WHERE também aumentou 35,8 milhões de linhas. Uma varredura de índice se comportaria um pouco diferente. Enquanto o BTREE mantém a ordem das teclas, é horrível fazer varreduras de alcance. No seu caso particular, você está executando a pior varredura de intervalo possível, com o mesmo número de entradas BTREE que existem linhas na tabela. O MySQL precisa percorrer as páginas do BTREE (pelo menos entre os nós da folha) para ler os valores. Além disso, a
time_on
coluna deve ser comparada ao longo do caminho na ordem ditada pelo índice. Portanto, os nós BTREE que não são folhas também devem ser atravessados.Por favor, veja minhas postagens no BTREEs
Aug 06, 2013
: No MySQL, se a coluna X tiver valores únicos, qual é a diferença entre o índice UNIQUE e o índice B-TreeJun 28, 2012
: Benefícios do BTREE no MySQLSe a consulta fosse hoje à meia-noite de hoje
ou mesmo meio dia hoje
isso deve levar menos tempo.
MORAL DA HISTÓRIA: Não use uma cláusula WHERE que faça uma varredura de intervalo ordenada igual ao número de linhas na tabela de destino.
fonte
Para a consulta específica:
um índice em
(time_on, diff_ms)
seria a melhor opção. Portanto, se a consulta for executada com bastante frequência ou se sua eficiência for crucial para o seu aplicativo, adicione este índice:(Não está relacionado à pergunta)
E, realmente, mude o mecanismo da tabela para InnoDB. É 2015 e o funeral do MyISAM foi há alguns anos atrás.
(/ rant)
fonte
ALTER TABLE writetest_table DROP INDEX time_on;
, 2) executarANALYZE TABLE writetest_table;
e 3) executar novamente a consulta. O tempo volta para 7 segundos?EXPLAIN select sum(diff_ms) from writetest_table where time_on > ("2015-07-13 15:11:56");
. O novo índice está sendo usado? Se não estiver sendo usado, eu diria que é a sua população-chave, principalmente se o seu tempo inicial for apenas alguns dias atrás. Como o número de linhas aumenta com dias mais distintos, a distribuição de chaves deve se estabilizar e o EXPLAIN deve ser melhor .