2012-10-16 6 views
2

J'ai la requête MySQL simple suivante:requête MySQL simple avec problèmes de performances

SELECT SQL_NO_CACHE mainID 
FROM tableName 
WHERE otherID3=19 
AND dateStartCol >= '2012-08-01' 
AND dateStartCol <= '2012-08-31'; 

Quand je lance ce qu'il faut 0,29 secondes pour ramener 36074 résultats. Quand j'augmente ma période de date pour ramener plus de résultats (65703), elle fonctionne en 0.56. Lorsque j'exécute d'autres requêtes SQL similaires sur le même serveur mais sur des tables différentes (certaines tables sont plus grandes), les résultats reviennent dans environ 0,01 seconde.

Bien que 0.29 ne soit pas lent, il s'agit d'une partie de base pour une requête complexe et cette temporisation signifie qu'elle n'est pas évolutive.

Voir ci-dessous pour la définition de la table et les index.

Je sais que ce n'est pas la charge du serveur car j'ai le même problème sur un serveur de développement qui a très peu d'utilisation.

+---------------------------+--------------+------+-----+---------+----------------+ 
| Field      | Type   | Null | Key | Default | Extra   | 
+---------------------------+--------------+------+-----+---------+----------------+ 
| mainID     | int(11)  | NO | PRI | NULL | auto_increment | 
| otherID1     | int(11)  | NO | MUL | NULL |    | 
| otherID2     | int(11)  | NO | MUL | NULL |    | 
| otherID3     | int(11)  | NO | MUL | NULL |    | 
| keyword     | varchar(200) | NO | MUL | NULL |    | 
| dateStartCol    | date   | NO | MUL | NULL |    | 
| timeStartCol    | time   | NO | MUL | NULL |    | 
| dateEndCol    | date   | NO | MUL | NULL |    | 
| timeEndCol    | time   | NO | MUL | NULL |    | 
| statusCode    | int(1)  | NO | MUL | NULL |    | 
| uRL      | text   | NO |  | NULL |    | 
| hostname     | varchar(200) | YES | MUL | NULL |    | 
| IPAddress     | varchar(25) | YES |  | NULL |    | 
| cookieVal     | varchar(100) | NO |  | NULL |    | 
| keywordVal    | varchar(60) | NO |  | NULL |    | 
| dateTimeCol    | datetime  | NO | MUL | NULL |    | 
+---------------------------+--------------+------+-----+---------+----------------+ 


+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table    | Non_unique | Key_name      | Seq_in_index | Column_name    | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+ 
| tableName   |   0 | PRIMARY      |   1 | mainID     | A   |  661990 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_otherID1     |   1 | otherID1     | A   |  330995 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_otherID2     |   1 | otherID2     | A   |   25 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_otherID3     |   1 | otherID3     | A   |   48 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_dateStartCol    |   1 | dateStartCol    | A   |   187 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_timeStartCol    |   1 | timeStartCol    | A   |  73554 |  NULL | NULL |  | BTREE  |   | 
|tableName   |   1 | idx_dateEndCol     |   1 | dateEndCol     | A   |   188 |  NULL | NULL |  | BTREE  |   | 
|tableName   |   1 | idx_timeEndCol     |   1 | timeEndCol     | A   |  73554 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_keyword     |   1 | keyword     | A   |  82748 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_hostname     |   1 | hostname     | A   |  2955 |  NULL | NULL | YES | BTREE  |   | 
| tableName   |   1 | idx_dateTimeCol    |   1 | dateTimeCol    | A   |  220663 |  NULL | NULL |  | BTREE  |   | 
| tableName   |   1 | idx_statusCode    |   1 | statusCode     | A   |   2 |  NULL | NULL |  | BTREE  |   | 
+--------------------+------------+-------------------------------+--------------+---------------------------+-----------+-------------+----------+--------+------+------------+---------+ 

Expliquer Sortie:

+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+ 
| id | select_type | table  | type | possible_keys     | key    | key_len | ref | rows | filtered | Extra  | 
+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+ 
| 1 | SIMPLE  | tableName | range | idx_otherID3,idx_dateStartCol | idx_dateStartCol | 3  | NULL | 66875 | 75.00 | Using where | 
+----+-------------+-----------+-------+----------------------------------+-------------------+---------+------+-------+----------+-------------+ 
+0

Ajoutez le résultat de l'explication. –

+0

Avez-vous essayé 'AND dateStartCol ENTRE '2012-08-01' ET '2012-08-31''. Je ne sais pas si ça va aider, mais ça vaut le coup d'essayer. – Bojangles

+0

J'ai essayé le BETWEEN et il n'y a pas de différence significative dans les temps (va de 0.29 à 0.28). -Merci pour la suggestion cependant. – mh1

Répondre

1

Si tel est vraiment votre requête (et non une version simplifiée de même), cela devrait obtenir de meilleurs résultats:

CREATE INDEX table_ndx on tableName(otherID3, dateStartCol, mainID); 

Le premier indice entrée signifie que le premier match dans le WHERE est très rapide; la même chose s'applique également avec dateStartCol. Le troisième champ est très petit et ne ralentit pas sensiblement l'index, mais permet de trouver immédiatement la donnée dont vous avez besoin dans l'index sans aucun accès à la table.

Il est important que les clés soient dans le même index. Dans le EXPLAIN que vous avez posté, chaque clé est dans un index qui lui est propre, donc même si MySQL choisit le meilleur index, les performances ne seront pas optimales. J'essaierais d'utiliser moins d'index, car ils ont aussi un coût (bouchon éhonté: Can Indices actually decrease SELECT performance?).

0

Si cela est une récurrente ou une requête importante puis créez un index à plusieurs colonnes:

CREATE INDEX index_name ON tableName (otherID3, dateStartCol) 

Supprimer les index non utilisés comme ils font table change plus coûteux.

BTW vous n'avez pas besoin de deux colonnes séparées pour la date et l'heure. Vous pouvez combiner ensuite dans un type datetime ou timestamp. Une colonne de moins et un indice de moins.

La explain sortie montre qu'il a choisi l'indice dateStartCol pour que vous puissiez essayer le contraire je l'ai suggéré ci-dessus:

CREATE INDEX index_name ON tableName (dateStartCol, otherID3) 

Notez que la condition de dateStartCol de requête toujours obtenir 75% des lignes afin de ne pas beaucoup d'amélioration, si tout, en utilisant cet index unique.

Quelle est l'unique otherID3? S'il n'y a pas beaucoup de otherID3 répété, vous pouvez hint le moteur pour l'utiliser.

+0

J'ai essayé de le faire mais cela prend le temps de requête de 0.29 à 0.25. Tous les index sont également utilisés - pas nécessairement dans ce SQL mais dans d'autres rapports, ils ne peuvent donc pas être supprimés. – mh1

+0

@ mh1 Modifier basé sur 'explain' –

+0

En effet, cela devrait le faire. Cependant, je suis assez sûr que l'ordre des champs dans cet index ne devrait pas importer, parce que dans une requête AND MySQL peut décider de les réorganiser de toute façon. Si cela fait une différence, ça pourrait même être un bug. –

0

Essayez d'abord d'ajouter la bonne clé. Il semble que dateStartCol est plus sélectif que otherID3

ALTER TABLE tableName ADD KEY idx_dates(dateStartCol, dateStartCol) 

deuxième - s'il vous plaît assurez-vous de sélectionner uniquement les lignes dont vous avez besoin en ajoutant la clause de limite au SELECT. Cela devrait remonter la requête. Essayez comme ceci:

SELECT SQL_NO_CACHE mainID DE tableName OÙ otherID3 = 19 ET dateStartCol> = '2012-08-01' ET dateStartCol < = '2012-08-31' LIMITE 10;

Veuillez également vous assurer que votre MySQL a été correctement réglé.Vous pouvez vérifier key_buffer_size et innodb_buffer_pool_size comme décrit dans http://astellar.com/2011/12/why-is-stock-mysql-slow/

+0

LIMIT ne fera pas beaucoup de différence, car il fera d'abord la même requête et ne retournera qu'une partie des résultats. Comme il s'agit seulement d'une requête pour quelques identifiants, la transmission des données ne sera pas la partie la plus coûteuse. –

+0

Vous avez raison concernant la transmission de données, mais dans certains cas, MySQL arrêtera de lire les données lorsque la limite est atteinte. Il semble que IO impliqué fortement dans ce cas pourrait aider. Mais de toute façon 300ms est beaucoup trop long pour ce type de requête, indépendamment de LIMIT – vfedorkov