2010-12-05 4 views
0

J'ai ce tableau:requête MySQL lent lors de la sélection VARCHAR

CREATE TABLE `search_engine_rankings` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `keyword_id` int(11) DEFAULT NULL, 
    `search_engine_id` int(11) DEFAULT NULL, 
    `total_results` int(11) DEFAULT NULL, 
    `rank` int(11) DEFAULT NULL, 
    `url` varchar(255) DEFAULT NULL, 
    `created_at` datetime DEFAULT NULL, 
    `updated_at` datetime DEFAULT NULL, 
    `indexed_at` date DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`), 
    KEY `search_engine_rankings_search_engine_id_fk` (`search_engine_id`), 
    CONSTRAINT `search_engine_rankings_keyword_id_fk` FOREIGN KEY (`keyword_id`) REFERENCES `keywords` (`id`) ON DELETE CASCADE, 
    CONSTRAINT `search_engine_rankings_search_engine_id_fk` FOREIGN KEY (`search_engine_id`) REFERENCES `search_engines` (`id`) ON DELETE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=244454637 DEFAULT CHARSET=utf8 

Il a environ 250 millions de lignes de production.

Quand je fais:

select id, 
     rank 
    from search_engine_rankings 
where keyword_id = 19000 
    and search_engine_id = 11 
    and indexed_at = "2010-12-03"; 

... il fonctionne très rapidement.

Quand j'ajoute la colonne url (VARCHAR):

select id, 
     rank, 
     url 
    from search_engine_rankings 
where keyword_id = 19 
    and search_engine_id = 11 
    and indexed_at = "2010-12-03"; 

... il fonctionne très lentement.

Des idées?

+0

dépend des données si ce n'est pas une faute de frappe sur le keyword_id (19000 vs 19) –

+0

Oui, c'est une faute de frappe. – Scott

Répondre

1

La première requête peut être satisfaite par l'index seul - pas besoin de lire la table de base pour obtenir les valeurs dans la clause Select. La deuxième instruction nécessite des lectures de la table de base car la colonne URL ne fait pas partie de l'index.

UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`), 

Les lignes du tableau de base de TBE ne sont pas dans le même ordre physique que les lignes dans l'index, et donc la lecture de la table de base peut impliquer disque raclée considérable.

Vous pouvez considérer cela comme une sorte de preuve d'optimisation - sur la première requête, le disque est battu évité parce que le moteur est assez intelligent pour consulter l'index pour les valeurs demandées dans la clause select; il aura déjà lu cet index dans la RAM pour la clause where, donc il en profite.

+0

... conduisant à une solution possible consistant à ajouter l'URL à la clé unique_ranking, ou à créer un index séparé avec les parties concernées. Cela entraînera cependant des insertions/mises à jour/suppressions plus lentes. –

+0

Peut-être pas. L'ajout de la colonne URL pourrait gonfler l'index, et vous pourriez vous retrouver avec une perte de performance globale, surtout si vous prenez en compte le délai de mise à jour. Je vois que vous abordez ce point. Peut-être que nous tapons en même temps. – Tim

+0

@Scott - pourrait être, ou il pourrait vous causer des problèmes. Les observations d'ontrack ci-dessous ont également un sens. –

1

En plus de la réponse de Tim. Un index dans Mysql ne peut être utilisé que de gauche à droite. Ce qui signifie qu'il peut utiliser les colonnes de votre index dans votre clause WHERE seulement jusqu'au moment où vous les utilisez. Actuellement, votre index UNIQUE est keyword_id, search_engine_id, rank, indexed_at. Ce sera en mesure de filtrer les colonnes keyword_id et search_engine_id, ont encore besoin de scanner sur les lignes restantes pour filtrer indexed_at

Mais si vous changez à: keyword_id, search_engine_id, indexed_at, rank (juste l'ordre). Ce sera en mesure de filtrer les colonnes keyword_id, search_engine_id et indexed_at

Je crois qu'il sera en mesure d'utiliser pleinement cet index pour lire la partie appropriée de votre tableau.

+0

Cela ressemble à la première chose que je devrais essayer et voir ce qui se passe. Merci! – Scott

0

Je sais que c'est un ancien article, mais je vivais la même situation et je n'ai pas trouvé de réponse. Cela arrive vraiment dans MySQL, quand vous avez des colonnes varchar cela demande beaucoup de temps. Ma requête a duré environ 20 secondes pour traiter des lignes de 1,7 million et est maintenant d'environ 1,9 seconde.

Ok d'abord, créez une vue de cette requête:

CREATE VIEW view_one AS 
    select id,rank 
    from search_engine_rankings 
    where keyword_id = 19000 
    and search_engine_id = 11 
    and indexed_at = "2010-12-03"; 

En second lieu, même requête, mais avec une jointure interne:

select v.*, s.url 
from view_one AS v 
inner join search_engine_rankings s ON s.id=v.id; 
Questions connexes