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)
J'ai trois suggestions
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.
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
.
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