Problema de rendimiento de MySQL con la columna de fecha y hora indexada

14

Traté de resolver el siguiente problema durante aproximadamente una hora ahora y todavía no conseguí más.

Bien, tengo una mesa (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 |                |
+---------+-------------+------+-----+-------------------+----------------+

No te preocupes por los índices, he estado jugando tratando de encontrar una solución. Ahora, aquí está mi 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;

la tabla almacena información sobre solicitudes web entrantes, por lo que es una base de datos bastante grande.

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

tenga en cuenta que no hay una mejor manera de configurar una clave principal, ya que la columna de identificación será el único identificador único que tengo. La consulta mencionada anteriormente tarda aproximadamente 0.6-1.6 segundos en ejecutarse.

¿Qué índice sería inteligente? Pensé que la fecha de indexación me daría una cardinalidad "mala" y, por lo tanto, MySQL no la usará. http también es una mala elección, ya que solo hay unos 20 valores diferentes posibles.

¡Gracias por tu ayuda!

Actualización 1 He agregado un índice en (http, fecha) como sugirió ypercube:

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

y usó su consulta, pero funcionó igualmente mal. El índice agregado:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 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      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

y el EXPLICAR

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| 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                                  |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+

Versión del 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
fuente
¿Puedes agregar también la versión mysql y cuál es el motor de la tabla? (myisam o innodb)
ypercubeᵀᴹ
MyISAM y 5.1.73: todos los detalles ahora en la publicación.
Robin Heller
Me temo que puede tener que ver con que la httpcolumna sea anulable. Investigaré mañana, si encuentro tiempo.
ypercubeᵀᴹ
Me temo que puede tener que ver con que la columna http sea anulable. Investigaré mañana, si encuentro tiempo. Usted puede probar mediante la creación de una mesa de idéntico (salvo http NOT NULL) y la copia de todos los datos en él (excepto las filas con NULL http por supuesto.)
ypercubeᵀᴹ
Cambiarlo a NOT NULL (que es completamente posible, no me importó mucho al crear la tabla) aumentó el rendimiento a aproximadamente ~ 1s - 1.6s para la consulta (mi consulta). Gracias por tu esfuerzo hasta ahora.
Robin Heller

Respuestas:

10

Tengo tres sugerencias

SUGERENCIA # 1: Reescribe la consulta

Debería reescribir la consulta de la siguiente manera

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

o

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;

WHERE no debe tener una función en ambos lados del signo igual. Tener la fecha en el lado izquierdo del signo igual hace que sea más fácil para el Optimizador de consultas usar un índice en su contra.

SUGERENCIA # 2: Índice de apoyo

También sugeriría un índice diferente

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

Sugiero este orden de columnas porque todas las dateentradas serían contiguas en el índice. Luego, la consulta simplemente recopila httpvalores sin omitir huecos http.

SUGERENCIA # 3: Búfer de clave más grande (opcional)

MyISAM solo usa el almacenamiento en caché de índice. Como la consulta no debe tocar el .MYDarchivo, debe usar un búfer de clave MyISAM un poco más grande.

Para configurarlo a 256M

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

Luego, configúralo my.cnf

[mysqld]
key_buffer_size = 256M

No se requiere reiniciar MySQL

Darle una oportunidad !!!

RolandoMySQLDBA
fuente
Intenté las consultas que me diste. El n. ° 1 funcionó tan bien como la otra sugerencia o la mía, la segunda en realidad tuvo un rendimiento peor. Lo mismo para el Índice de soporte: haga que el rendimiento caiga aproximadamente un 75 por ciento. Voy a probar el búfer de teclas más grande ahora, ¡gracias de todos modos!
Robin Heller
Acepté su respuesta, aunque no solucionó el problema, con un búfer de clave más grande, sin embargo, funcionó un poco mejor. Cerrando esto, ya que es la mejor solución de todas. ¡Gracias!
Robin Heller
Para que la Sugerencia # 2 funcione, puede ser necesario agregar "USE INDEX" o "FORCE INDEX" en la consulta, al menos eso es lo que tuve que hacer para acelerar mi consulta después de crear un índice como ese.
Johano Fierra el
-2

Cambie su tipo de columna de fecha a un entero. Almacene la fecha como una fecha Unix en entero. La marca de tiempo es mucho más grande que una int. Conseguirías algo de eso.

apachebeard
fuente
2
¿Estás bromeando? Ambos INTy TIMESTAMPnecesitan 4 bytes.
ypercubeᵀᴹ
2
Sin mencionar que pierde todas las funciones de fecha y hora cuando almacena fechas o marcas de tiempo como enteros.
ypercubeᵀᴹ