Problema de desempenho do MySQL usando a coluna datetime indexada

14

Tentei resolver o seguinte problema por cerca de uma hora agora e ainda não consegui avançar mais.

Ok, eu tenho uma tabela (MyISAM):

+---------+-------------+------+-----+-------------------+----------------+
| Field   | Type        | Null | Key | Default           | Extra          |
+---------+-------------+------+-----+-------------------+----------------+
| id      | int(11)     | NO   | PRI | NULL              | auto_increment |
| http    | smallint(3) | YES  | MUL | 200               |                |
| elapsed | float(6,3)  | NO   |     | NULL              |                |
| cached  | tinyint(1)  | YES  |     | NULL              |                |
| ip      | int(11)     | NO   |     | NULL              |                |
| date    | timestamp   | NO   | MUL | CURRENT_TIMESTAMP |                |
+---------+-------------+------+-----+-------------------+----------------+

Por favor, não se importe com os índices, estou brincando tentando encontrar uma solução. Agora, aqui está a minha consulta.

SELECT http,
COUNT( http )  AS count 
FROM reqs
WHERE DATE(date) >= cast(date_sub(date(NOW()),interval 24 hour) as datetime)
GROUP BY http
ORDER BY count;

a tabela está armazenando informações sobre solicitações da web recebidas, portanto é um banco de dados bastante grande.

+-----------+
| count(id) |
+-----------+
|    782412 |
+-----------+

observe que não há melhor maneira de definir uma chave primária, pois a coluna id será o único identificador exclusivo que tenho. A consulta acima mencionada leva cerca de 0,6-1,6 segundos para executar.

Qual índice seria inteligente? Imaginei que a data de indexação me daria cardinalidade "ruim" e, portanto, o MySQL não a usaria. http também é uma má escolha, pois existem apenas cerca de 20 valores possíveis.

Obrigado pela ajuda!

Atualização 1 Adicionei um índice em (http, data) conforme sugerido pelo ypercube:

mysql> CREATE INDEX httpDate ON reqs (http, date);

e usou sua consulta, mas teve um desempenho igualmente ruim. O índice adicionado:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| reqs  |          0 | PRIMARY  |            1 | id          | A         |      798869 |     NULL | NULL   |      | BTREE      |         |
| reqs  |          1 | httpDate |            1 | http        | A         |          19 |     NULL | NULL   | YES  | BTREE      |         |
| reqs  |          1 | httpDate |            2 | date        | A         |       99858 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

e o EXPLAIN

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| id | select_type        | table | type  | possible_keys | key      | key_len | ref  | rows  | Extra                                                     |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
|  1 | PRIMARY            | r     | range | NULL          | httpDate | 3       | NULL |    20 | Using index for group-by; Using temporary; Using filesort |
|  2 | DEPENDENT SUBQUERY | ri    | ref   | httpDate      | httpDate | 3       | func | 41768 | Using where; Using index                                  |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+

Versão do servidor MySQL:

mysql> SHOW VARIABLES LIKE "%version%";
+-------------------------+---------------------+
| Variable_name           | Value               |
+-------------------------+---------------------+
| protocol_version        | 10                  |
| version                 | 5.1.73              |
| version_comment         | Source distribution |
| version_compile_machine | x86_64              |
| version_compile_os      | redhat-linux-gnu    |
+-------------------------+---------------------+
5 rows in set (0.00 sec)
Robin Heller
fonte
Você também pode adicionar a versão mysql e qual é o mecanismo da tabela? (myisam ou innodb)
ypercube #
MyISAM e 5.1.73 - todos os detalhes agora no post.
Robin Heller
Receio que possa ter a ver com a httpcoluna ser anulável. Investigarei amanhã, se encontrar tempo.
ypercubeᵀᴹ
Receio que possa ter a ver com a coluna http ser anulável. Investigarei amanhã, se encontrar tempo. Você pode testar criando uma tabela idêntica (exceto com http NOT NULL) e copiando todos os dados para ela (exceto as linhas com http NULL, é claro.)
ypercubeᵀᴹ
Mudá-lo para NOT NULL (o que é inteiramente possível, não me importei muito ao criar a tabela) aumentou o desempenho para cerca de ~ 1s - 1,6s para a consulta (minha consulta). Obrigado pelo seu esforço até agora.
Robin Heller

Respostas:

10

Eu tenho três sugestões

SUGESTÃO # 1: reescreva a consulta

Você deve reescrever a consulta da seguinte maneira

SELECT http,
COUNT( http )  AS count 
FROM reqs
WHERE date >= ( DATE(NOW() - INTERVAL 1 DAY) + INTERVAL 0 SECOND )
GROUP BY http
ORDER BY count;

ou

SELECT * FROM
(
    SELECT http,
    COUNT( http )  AS count 
    FROM reqs
    WHERE date >= ( DATE(NOW() - INTERVAL 1 DAY) + INTERVAL 0 SECOND )
    GROUP BY http
) A ORDER BY count;

O WHERE não deve ter uma função nos dois lados do sinal de igual. A data no lado esquerdo do sinal de igual facilita o uso do Query Optimizer por um índice.

SUGESTÃO # 2: Índice de Suporte

Eu também sugeriria um índice diferente

ALTER TABLE reqs ADD INDEX date_http_ndx (date,http); -- not (http,date) 

Eu sugiro essa ordem de colunas porque o date entradas seriam contíguas no índice. Em seguida, a consulta simplesmente coleta httpvalores sem ignorar as lacunas http.

SUGESTÃO # 3: Buffer de chave maior (opcional)

O MyISAM usa apenas o cache de índice. Como a consulta não deve tocar no.MYD arquivo, você deve usar um buffer de chave MyISAM um pouco maior.

Para configurá-lo para 256M

SET @newsize = 1024 * 1024 * 256;
SET GLOBAL key_buffer_size = @newsize;

Em seguida, defina-o my.cnf

[mysqld]
key_buffer_size = 256M

Não é necessário reiniciar o MySQL

De uma chance !!!

RolandoMySQLDBA
fonte
Eu tentei as perguntas que você me deu. O número 1 foi tão bom quanto a outra sugestão ou a minha, a segunda realmente foi pior. O mesmo vale para o Índice de suporte - faça com que o desempenho caia cerca de 75%. Vou tentar o buffer de chaves maior agora, obrigado mesmo assim!
22714 Robin Onassou em
Aceitei sua resposta, embora não tenha resolvido o problema, com um buffer de chave maior, mas ele teve um desempenho um pouco melhor. Fechando isso, já que é a melhor solução de todas. Obrigado!
Robin Heller
Para que a sugestão 2 funcione, pode ser necessário adicionar "USE INDEX" ou "FORCE INDEX" na consulta, pelo menos é o que eu precisava fazer para acelerar minha consulta após criar um índice como esse.
você precisa saber é o seguinte
-2

Altere seu tipo de coluna de data para um número inteiro. Armazene a data como uma data Unix em número inteiro. O registro de data e hora é muito maior que um int. Você conseguiria um pouco disso.

apachebeard
fonte
2
Você está de brincadeira? Ambos INTe TIMESTAMPprecisam de 4 bytes.
precisa saber é o seguinte
2
Não mencione que você perde todas as funções de data e hora ao armazenar datas ou carimbos de data e hora como números inteiros.
precisa saber é o seguinte