Otimizar MySQL Agregação consulta
-
03-07-2019 - |
Pergunta
Eu tenho uma tabela muito grande (~ 100MilhõesRMB Records) no MySQL que contém informações sobre arquivos. Uma das peças de informação é a data de modificação de cada arquivo.
Eu preciso escrever uma consulta que irá contar o número de arquivos que se encaixam em intervalos de datas especificadas. Para fazer isso eu fiz uma pequena mesa que especifica esses intervalos (todos em dias) e se parece com isto:
DateRanges
range_id range_name range_start range_end
1 0-90 0 90
2 91-180 91 180
3 181-365 181 365
4 366-1095 366 1095
5 1096+ 1096 999999999
E escreveu uma consulta parecida com esta:
SELECT r.range_name, sum(IF((DATEDIFF(CURDATE(),t.file_last_access) > r.range_start and DATEDIFF(CURDATE(),t.file_last_access) < r.range_end),1,0)) as FileCount
FROM `DateRanges` r, `HugeFileTable` t
GROUP BY r.range_name
No entanto, muito previsivelmente, esta consulta leva uma eternidade para ser executado. Eu acho que é porque eu estou pedindo MySQL para percorrer as HugeFileTable 5 vezes, cada vez que executar o cálculo DATEDIFF () em cada arquivo.
O que eu quero fazer é em vez de ir através do registro HugeFileTable pelo registro apenas uma vez, e para cada arquivo incrementar a contagem no RANGE_NAME correndo totais apropriado. Eu não consigo descobrir como fazer isso ....
Alguém pode ajudar com isso?
Graças.
Editar : MySQL Versão: 5.0.45, tabelas são MyISAM
EDIT2 : Aqui está a descibe que foi solicitado nos comentários
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE r ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
1 SIMPLE t ALL NULL NULL NULL NULL 96506321
Solução
Em primeiro lugar, criar um índice em HugeFileTable.file_last_access
.
Em seguida, tente a seguinte consulta:
SELECT r.range_name, COUNT(t.file_last_access) as FileCount
FROM `DateRanges` r
JOIN `HugeFileTable` t
ON (t.file_last_access BETWEEN
CURDATE() + INTERVAL r.range_start DAY AND
CURDATE() + INTERVAL r.range_end DAY)
GROUP BY r.range_name;
Aqui está o plano EXPLAIN
que eu tenho quando eu tentei esta pergunta em MySQL 5.0.75 (editada por brevidade):
+-------+-------+------------------+----------------------------------------------+
| table | type | key | Extra |
+-------+-------+------------------+----------------------------------------------+
| t | index | file_last_access | Using index; Using temporary; Using filesort |
| r | ALL | NULL | Using where |
+-------+-------+------------------+----------------------------------------------+
É ainda não vai funcionar muito bem. Usando GROUP BY
, a consulta incorre em uma tabela temporária, que pode ser caro. Não há muito que possamos fazer sobre isso.
Mas pelo menos essa consulta elimina o produto cartesiano que você tinha em sua consulta original.
atualização:. Aqui está outra consulta que utiliza uma subconsulta correlacionada, mas eu ter eliminado o GROUP BY
SELECT r.range_name,
(SELECT COUNT(*)
FROM `HugeFileTable` t
WHERE t.file_last_access BETWEEN
CURDATE() - INTERVAL r.range_end DAY AND
CURDATE() - INTERVAL r.range_start DAY
) as FileCount
FROM `DateRanges` r;
O EXPLAIN
plano mostra a tabela não temporário ou filesort (pelo menos com a quantidade trivial de linhas que eu tenho em minhas tabelas de teste):
+----+--------------------+-------+-------+------------------+--------------------------+
| id | select_type | table | type | key | Extra |
+----+--------------------+-------+-------+------------------+--------------------------+
| 1 | PRIMARY | r | ALL | NULL | |
| 2 | DEPENDENT SUBQUERY | t | index | file_last_access | Using where; Using index |
+----+--------------------+-------+-------+------------------+--------------------------+
Tente esta pergunta em seu conjunto de dados e ver se ele tem um desempenho melhor.
Outras dicas
Bem, começar por ter certeza que file_last_access
é um index para o HugeFileTable
mesa.
Eu não tenho certeza se isso é possível \ melhor, mas tentar calcular os limites datas primeiro (arquivos a partir da data A para data B ), então usar algum consulta com> = e <=. Será, pelo menos teoricamente, melhorar o desempenho.
A comparação seria algo como:
t.file_last_access >= StartDate AND t.file_last_access <= EndDate
Você pode obter uma pequena melhora, removendo CURDATE () e colocar uma data na consulta uma vez que irá executar essa função para cada linha duas vezes em seu SQL.