2010-11-20 7 views
1

Je suis à court de ressources. J'ai une table avec environ 100K lignes. Lors de l'interrogation de cette table, les résultats sont généralement vifs, environ 2 ms ou plus. Mais chaque fois que j'utilise un ORDER BY, la performance chute comme une pierre à environ 120 ms. J'ai lu la page MySQL ORDER BY Optimization mais je ne peux pas dire que je comprends tout. Surtout les index ne sont pas clairs pour moi.Optimiser une requête ORDER BY

En fin de compte, je voudrais lancer la requête suivante:

SELECT * 
    FROM `affiliate_new_contracts` 
WHERE phone_brand IN ('Apple','Blackberry','HTC','LG','Motorola','Nokia', 
         'Samsung','Sony Ericsson') 
    AND contract_length IN ('12','24') 
    AND (addon IS NULL OR addon IN('Telfort Sms 300','Surf & Mail')) 
    AND (plan_name = 'Telfort 100' 
     AND 
     credible_shop = 1 
     ) 
    ORDER BY average_price_per_month ASC, phone_price_guestimate DESC, 
      contract_length ASC; 

Mais je serais heureux si je compris les principes sous-jacents.
La suppression de la clause ORDER BY dans la requête précédente la fait fonctionner en 20ms au lieu de 120ms. J'ai un index sur le champ average_price_per_month mais la simplification de la clause ORDER BY à ORDER BY average_price_per_month n'a donné aucune augmentation de performance. Que je ne comprends pas. Je suis également dans le noir à propos de ce que l'on appelle les index multi-colonnes qui devraient être en mesure de m'aider avec la requête ultime.

Toute aide serait appréciée. Comment puis-je faire ce mauvais garçon? Ou est-ce utopique?

La syntaxe CREATE TABLE est la suivante:

$ show create table affiliate_new_contracts; 
CREATE TABLE `affiliate_new_contracts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `contract_length` int(11) DEFAULT NULL, 
    `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `price` float DEFAULT NULL, 
    `average_price_per_month` float DEFAULT NULL, 
    `phone_price_guestimate` float DEFAULT NULL, 
    `credible_shop` tinyint(1) DEFAULT '0', 
    `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `addon_price` float DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`), 
    KEY `index_affiliate_new_contracts_on_average_price_per_month` (`average_price_per_month`), 
    KEY `index_affiliate_new_contracts_on_price` (`price`) 
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

BTW Cette table est recréée par semaine et est pas mis à jour entre-temps.

+1

J'ai reformaté la requête pour éviter les barres de défilement horizontales. Les deux derniers termes de requête (sur le nom du plan et le magasin crédible) sont écrits de manière incohérente avec le reste de la requête (les autres termes n'utilisent pas le nom de la table) et n'ont vraiment pas besoin de parenthèses. J'ai débattu de la possibilité de le réparer sans commenter ... et j'ai décidé de ne pas le faire. Si vous décidez de rendre ces termes cohérents, je vais supprimer ce commentaire. –

+0

Excellent commentaire. Une partie de la requête est/a été générée (plus de preuves que je suis mal adapté aux requêtes). J'ai enlevé le nom de la table superflue. – harm

Répondre

3

Il existe une limite à l'optimisation que vous pouvez effectuer sur les clauses ORDER BY. Le principal qui aide parfois est d'avoir un index sur le bon ensemble de colonnes dans le bon ordre. Ainsi, par votre exemple, un index (simple, composite) sur:

average_price_per_month ASC, phone_price_guestimate DESC, contract_length ASC 

pourrait aider, mais l'optimiseur peut encore décider qu'il est préférable d'utiliser un autre indice pour faire face aux conditions de filtre dans la requête et Ensuite, il triera les données ainsi sélectionnées. Notez qu'à moins que l'index fournisse les données dans l'ordre trié correct et que l'utilisation de l'index accélère globalement la requête, l'optimiseur ne l'utilisera pas. Un index sur une seule des colonnes à trier est un bénéfice limité pour l'optimiseur, et normalement il n'utilisera pas un tel index.

Une question à considérer:

  • Comment rapidement la requête sans effectuer la clause ORDER BY.

Cela vous donne une mesure très directe du coût du tri. Vous parlez 20 ms sans commander et 120 ms avec la commande, donc l'ORDER BY est modérément cher. La question suivante pourrait être "Pouvez-vous surpasser son genre dans votre application?". Vous pourriez être capable de faire cela, mais le paquet de tri dans un SGBD est généralement assez bien optimisé et vous devrez probablement travailler dur pour le battre.

0

Je suppose que votre index ne vous sert à rien parce que ce n'est pas la clé primaire et que votre logique de sélection de requête (where clause) ne l'utilise pas. Comme vous n'utilisez pas l'index pour choisir les lignes, vous devez trier les résultats après la sélection. Le fait que ce ne soit pas votre clé primaire signifie que les résultats ne sont pas déjà commandés par le prix moyen mensuel, ce qui réduirait ou éliminerait le temps de tri puisqu'ils seraient déjà commandés.

Une solution consisterait à utiliser un index composé comprenant la colonne la plus sélective (nom du plan) et la colonne de classement (average_price_per_month).Il aura toujours besoin de faire le tri après la sélection, mais les résultats seront déjà commandés par la colonne de classement primaire, ce qui réduira le temps passé.

CREATE TABLE `affiliate_new_contracts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `contract_length` int(11) DEFAULT NULL, 
    `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `price` float DEFAULT NULL, 
    `average_price_per_month` float DEFAULT NULL, 
    `phone_price_guestimate` float DEFAULT NULL, 
    `credible_shop` tinyint(1) DEFAULT '0', 
    `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `addon_price` float DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`,`average_price_per_month`), 
    KEY `index_affiliate_new_contracts_on_price` (`price`) 
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

Vous pouvez également utiliser EXPLAIN pour comprendre comment la requête est en cours d'exécution (si mon intuition est pas correct) et ajuster les indices en conséquence.