web-dev-qa-db-fra.com

Problème de performances MySQL à l'aide de la colonne datetime indexée

J'ai essayé de résoudre le problème suivant pendant environ une heure maintenant et je ne suis toujours pas allé plus loin.

D'accord, j'ai une table (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 |                |
+---------+-------------+------+-----+-------------------+----------------+

S'il vous plaît, ne vous occupez pas des index, j'ai essayé de trouver une solution. Maintenant, voici ma requête.

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;

le tableau stocke des informations sur les requêtes Web entrantes, c'est donc une base de données assez volumineuse.

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

notez qu'il n'y a pas de meilleur moyen de définir une clé primaire car la colonne id sera le seul identifiant unique que j'ai. La requête mentionnée ci-dessus prend environ 0,6 à 1,6 seconde pour s'exécuter.

Quel indice serait intelligent? J'ai pensé que l'indexation de la date me donnerait une "mauvaise" cardinalité et donc MySQL ne l'utiliserait pas. http est également un mauvais choix car il n'y a qu'environ 20 valeurs différentes possibles.

Merci pour ton aide!

pdate 1 J'ai ajouté un index sur (http, date) comme suggéré par ypercube:

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

et utilisé sa requête, mais il a tout aussi mauvais. L'index ajouté:

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

et les [~ # ~] expliquent [~ # ~]

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

Version du serveur 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)
15
Robin Heller

J'ai trois suggestions

SUGGESTION # 1: Réécrivez la requête

Vous devez réécrire la requête comme suit

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;

Le WHERE ne doit pas avoir de fonction des deux côtés du signe égal. Le fait d'avoir la date sur le côté gauche du signe égal permet à l'Optimiseur de requête d'utiliser plus facilement un index.

SUGGESTION # 2: Index justificatif

Je suggérerais également un index différent

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

Je suggère cet ordre de colonnes car les entrées date seraient toutes contiguës dans l'index. Ensuite, la requête collecte simplement les valeurs de http sans ignorer les lacunes dans http.

SUGGESTION # 3: Tampon de clé plus grand (facultatif)

MyISAM utilise uniquement la mise en cache d'index. Étant donné que la requête ne doit pas toucher le .MYD, vous devez utiliser un tampon de clé MyISAM légèrement plus grand.

Pour le régler sur 256M

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

Ensuite, définissez-le dans my.cnf

[mysqld]
key_buffer_size = 256M

Redémarrage de MySQL non requis

Essaie !!!

10
RolandoMySQLDBA