Question

J'ai une structure de table qui peut être résumé comme suit:

pagegroup
* pagegroupid
* name

A 3600 rangées

page
* pageid
* pagegroupid
* data

Références PageGroup; a 10000 rangées; peut avoir n'importe quoi entre 1 à 700 lignes par pagegroup; La colonne de données est de type MediumText et la colonne contient des données de 100 000 à 200 kytes par ligne

userdata
* userdataid
* pageid
* column1
* column2
* column9

Page de références; a environ 300 000 lignes; peut avoir environ 1 à 50 lignes par page

La structure ci-dessus est assez droite pour Forwad, le problème est qu'une jointure de UserData au groupe de pages est terriblement, terriblement lente même si j'ai indexé toutes les colonnes qui devraient être indexées. Le temps nécessaire pour exécuter une requête pour un tel jointure (userdata inner_join page inner_join pagegroup) dépasse 3 minutes. C'est terriblement lent compte tenu du fait que je ne sélectionne pas du tout la colonne de données. Exemple de la requête qui prend trop de temps:

SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

Veuillez aider en expliquant pourquoi cela prend-il si longtemps et que puis-je faire pour le rendre plus rapide.

EDIT # 1

Expliquez les retours après le charabia:

id  select_type  table      type    possible_keys        key      key_len  ref                         rows    Extra
1   SIMPLE       userdata   ALL     pageid                                                             372420
1   SIMPLE       page       eq_ref  PRIMARY,pagegroupid  PRIMARY  4        topsecret.userdata.pageid   1
1   SIMPLE       pagegroup  eq_ref  PRIMARY              PRIMARY  4        topsecret.page.pagegroupid  1

EDIT # 2

SELECT
u.field2, p.pageid
FROM
userdata u
INNER JOIN page p ON u.pageid = p.pageid;
/*
0.07 sec execution, 6.05 sec fecth
*/

id  select_type  table  type    possible_keys  key      key_len  ref                rows     Extra
1   SIMPLE       u      ALL     pageid                                              372420
1   SIMPLE       p      eq_ref  PRIMARY        PRIMARY  4        topsecret.u.pageid 1        Using index

SELECT
p.pageid, g.pagegroupid
FROM
page p
INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid;
/*
9.37 sec execution, 60.0 sec fetch
*/

id  select_type  table  type   possible_keys  key          key_len  ref                      rows  Extra
1   SIMPLE       g      index  PRIMARY        PRIMARY      4                                 3646  Using index
1   SIMPLE       p      ref    pagegroupid    pagegroupid  5        topsecret.g.pagegroupid  3     Using where

Morale de l'histoire

Gardez les colonnes de texte moyen / long dans une table séparée si vous rencontrez des problèmes de performances tels que celui-ci.

Était-ce utile?

La solution

Quel est le type de données et le but de ColumnX dans la table UserData? Il convient de noter que tout type de données de texte (c'est-à-dire excluant CHAR, VARCHAR) force toutes les tables temporaires à créer sur le disque. Maintenant, comme vous faites une jointure directe sans conditions, regroupant ou commande, il n'aura probablement pas besoin de tables temporaires, sauf pour agréger le résultat final.

Je pense qu'il serait également très utile si vous nous montrez comment vos index sont créés. Une chose à retenir est que, bien qu'InNODB concaténe la clé principale du tableau à chaque index, Myisam ne le fait pas. Cela signifie que si vous indexez la colonne Nom et la recherche avec comme, mais je veux toujours obtenir le identifiant du groupe de pages; Ensuite, la requête devrait encore visiter la table pour obtenir le identifiant Au lieu de pouvoir le récupérer de l'index.

Ce que cela signifie, dans votre cas, si je comprends votre commentaire apphacker Correctement, c'est d'obtenir le nom de chaque utilisateur PageGroups. L'optimiseur de requête voudrait utiliser l'index pour la jointure, mais pour chaque résultat, il devrait également visiter le tableau pour récupérer le nom du groupe de pages. Si votre dataType sur Nom n'est pas plus grand qu'un varchar modéré, c'est-à-dire pas de texte, vous pouvez également créer un index (id, nom) qui permettrait à la requête de récupérer le nom directement à partir de l'index.

En dernier essai, vous soulignez que toute la requête serait probablement plus rapide si le texte moyen n'était pas dans le tableau des pages.

  1. Cette colonne est exclue de la requête que vous exécutez que je présume?
  2. Vous pouvez également essayer de séparer les données de la page de la page "Configuration", c'est-à-dire à quel groupe il appartient. Vous auriez alors probablement quelque chose comme:
    • Pages
      • pageide
      • pagegroupid
    • Pagenata
      • pageide
      • Les données

Nous espérons que cela vous permettra de vous joindre plus rapidement car aucune colonne en pages ne prend beaucoup de place. Ensuite, lorsque vous deviez afficher une certaine page, vous vous joignez à la table Pagedata sur la colonne PageID pour récupérer les données nécessaires pour afficher une page particulière.

Autres conseils

Le moyen facile de comprendre ce que fait MySQL avec votre requête est de vous expliquer la requête. Exécutez ceci et jetez un œil à la sortie:

EXPLAIN SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )

MySQL vous dira dans quel ordre il traite les requêtes et les index qu'il utilise. Le fait que vous ayez créé des index ne signifie pas que MySQL les utilise réellement.

Voir également Optimiser les requêtes avec Explication

ÉDITER

La sortie de votre explication semble bien. Il fait une analyse de table complète sur la table UserData, mais c'est normal car vous souhaitez y retourner toutes les lignes. La meilleure façon d'optimiser cela est de repenser votre application. Avez-vous vraiment besoin de retourner les 372k de lignes?

Je suppose que la table UserData est très grande et ne rentre pas en mémoire. MySQL devrait lire l'intégralité du tableau de Harddisk, même s'il n'a besoin que de deux petites colonnes.

Vous pouvez essayer d'éliminer le besoin de numériser l'ensemble du tableau en définissant un index qui contient tout ce dont la requête a besoin. De cette façon, l'index n'est pas un moyen de faciliter une recherche dans le tableau principal, mais c'est une version scolarisée du tableau lui-même. MySQL n'a qu'à lire la table du raccourci du disque.

L'index pourrait ressembler à ceci:

column1, pageid

Cela doit être non groupé, ou cela ferait partie de la grande table, battant son objectif. Voir cette page Pour une idée sur la façon dont MySQL décide quel index à se regrouper. La manière la plus simple semble s'assurer que vous avez une clé principale sur PageID, qui sera regroupée, de sorte que l'index de colonnes secondaire1 + PageID sera non cluster.

Un problème possible est que MySQL utilise un seul index par requête et peut-être que vous n'avez pas un seul index avec ces colonnes - ou l'optimiseur de requête de MySQL ne le choisit pas. Qu'est-ce que EXPLAIN SELECT & c vous dire ici?

Je commencerais par briser la requête, pour déterminer s'il y a une partie lente et rapide, ou si les deux sont lents (désolé, je ne suis pas fan de la syntaxe d'utilisation, donc je vais utiliser):

SELECT 
  u.userdata, p.pageid
FROM
  userdata u
  INNER JOIN page p ON u.pageid = p.pageid

SELECT 
  p.pageid, g.pagegroupid
FROM
  page 
  INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid

Qu'est-ce que cela vous donne? Les exécuter avec EXPLAIN EXTENDED fournira des conseils supplémentaires.

On dirait que vous vous jointez sur toutes les lignes userdata puis essayer de tout sélectionner. C'est tout page dans un pagegroup avec userdata. Où est le WHERE clause? Il n'y a pas LIMIT, combien de résultats vouliez-vous? Pourquoi ne pas obtenir votre compte de ligne sur userdata rangée dans votre explain Résultat, cela devrait accélérer la requête. Il h.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top