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

15

J'ai essayé de résoudre le problème suivant pendant environ une heure maintenant et je n'ai toujours pas pu aller 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 la date d' indexation 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 possibles différentes.

Merci pour ton aide!

Mise à jour 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 mal fonctionné. 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 l' EXPLIQUER

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| 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)
Robin Heller
la source
Pouvez-vous également ajouter la version mysql et quel est le moteur de la table? (myisam ou innodb)
ypercubeᵀᴹ
MyISAM et 5.1.73 - tous les détails maintenant dans le post.
Robin Heller
J'ai peur que cela ait à voir avec la httpcolonne pouvant être annulée. Je vais enquêter demain, si je trouve du temps.
ypercubeᵀᴹ
J'ai peur que cela ait à voir avec la colonne http pouvant être annulée. Je vais enquêter demain, si je trouve du temps. Vous pouvez tester en créant une table identique (sauf avec http NOT NULL) et en y copiant toutes les données (sauf les lignes avec http NULL bien sûr.)
ypercubeᵀᴹ
Le changer en NOT NULL (ce qui est tout à fait possible, cela ne me dérangeait pas beaucoup lors de la création de la table) a augmenté les performances à environ ~ 1s - 1,6s pour la requête (ma requête). Merci pour vos efforts jusqu'à présent.
Robin Heller

Réponses:

10

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 dateentrées seraient toutes contiguës dans l'index. Ensuite, la requête collecte simplement les httpvaleurs sans ignorer les lacunes 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 .MYDfichier, 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, placez-le dans my.cnf

[mysqld]
key_buffer_size = 256M

Redémarrage de MySQL non requis

Essaie !!!

RolandoMySQLDBA
la source
J'ai essayé les requêtes que vous m'avez données. # 1 s'est comporté à peu près aussi bien que l'autre suggestion ou la mienne, la seconde a en fait moins bien fonctionné. Même chose pour l'indice de support: faites chuter les performances d'environ 75%. Je vais essayer le plus gros tampon de clé maintenant, merci quand même!
Robin Heller
J'ai accepté votre réponse bien qu'elle n'ait pas résolu le problème, avec un tampon de clé plus gros, mais elle s'est comportée un peu mieux. Fermer cela car c'est la meilleure soluion de toutes les données. Je vous remercie!
Robin Heller
Pour que la suggestion # 2 fonctionne, il peut être nécessaire d'ajouter "USE INDEX" ou "FORCE INDEX" dans la requête, du moins c'est ce que je devais faire pour accélérer ma requête après avoir créé un index comme celui-ci.
Johano Fierra
-2

Remplacez votre type de colonne de date par un entier. Stockez la date en tant que date Unix dans un entier. L'horodatage est beaucoup plus grand qu'un int. Vous en tireriez un coup.

apachebeard
la source
2
Est-ce que vous plaisantez? Les deux INTet ont TIMESTAMPbesoin de 4 octets.
ypercubeᵀᴹ
2
Sans oublier que vous perdez toutes les fonctions datetime lorsque vous stockez des dates ou des horodatages sous forme d'entiers.
ypercubeᵀᴹ