Question

Je les 3 tableaux ci-dessous dans une base de données MySQL 4.x:

  • hôtes: (300,000 enregistrements)
    • id (unsigned int) PRIMARY KEY
    • nom (VARCHAR 100)
  • chemins: (6.000.000 enregistrements)
    • id (unsigned int) PRIMARY KEY
    • nom (VARCHAR 100)
  • urls: (7.000.000 enregistrements)
    • host (unsigned int) PRIMARY KEY <--- liens vers hosts.id
    • chemin (unsigned int) PRIMARY KEY <--- liens vers paths.id

Comme vous pouvez le voir, le schéma est très simple, mais le problème est la quantité de données dans ces tableaux.

Voici la requête que je suis en cours d'exécution:

SELECT CONCAT(H.name, P.name)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id;

Cette requête fonctionne parfaitement bien, mais prend 50 minutes pour courir. Est-ce que quelqu'un a une idée sur la façon dont je pourrais accélérer cette requête?

Merci d'avance. Nicolas

Était-ce utile?

La solution

D'une part je ne voudrais pas faire la CONCAT dans la requête. Faites-le en dehors.

Mais vraiment vous s'exécute requête lentement parce que vous récupérez des millions de lignes.

Autres conseils

Peut-être que vous devriez inclure une clause WHERE? Ou avez-vous vraiment besoin de toutes les données?

Cela me semble comme un cas où trop zélés utilisation des clés de substitution est de vous ralentir. Si les tables étaient:

  • hôtes:

    • nom (VARCHAR 100) PRIMARY KEY
  • chemins:

    • nom (VARCHAR 100) PRIMARY KEY
  • urls:

    • host (VARCHAR 100) clé primaire <--- liens vers hosts.name
    • chemin (VARCHAR 100) clé primaire <--- liens vers paths.name

Ensuite, votre requête nécessiterait pas du tout joint:

SELECT CONCAT(U.host, U.path) FROM urls U;

True, table URLS occuperaient plus d'espace disque - mais le fait que la matière

?

EDIT: A la réflexion, ce qui est le point de cette table CHEMINS de toute façon? À quelle fréquence les différents hôtes partagent les mêmes chemins?

Pourquoi ne pas:

  • hôtes:

    • nom (VARCHAR 100) PRIMARY KEY
  • urls:

    • host (VARCHAR 100) clé primaire <--- liens vers hosts.name
    • chemin (VARCHAR 100) PRIMARY KEY <--- aucun lien nulle part

EDIT2: Ou si vous avez vraiment besoin la clé de substitution pour les hôtes:

  • hôtes:

    • id integer PRIMARY KEY
    • nom (VARCHAR 100)
  • urls:

    • entier hôte PRIMARY KEY <--- liens vers hosts.name
    • chemin (VARCHAR 100) PRIMARY KEY <--- aucun lien nulle part

    SELECT CONCAT (H.name, U.path) à partir d'URL U JOIN hôtes H ON H.id = U.host;

Dans l'ensemble, le meilleur conseil est de tracer et profil pour voir ce qui est vraiment prendre le temps. Mais voici mes pensées sur des choses spécifiques à regarder.

(1) Je dirais que vous voulez vous assurer que les index sont pas utilisés dans l'exécution de cette requête. Puisque vous n'avez pas des conditions de filtrage, il devrait être plus efficace pour le balayage complet de toutes les tables et les réunir avec une sorte de fusion ou une opération de hachage.

(2) La concaténation de chaîne prend sûrement un certain temps, mais je ne comprends pas pourquoi les gens recommandent de le retirer. Vous auriez besoin sans doute alors de faire la concaténation dans un autre morceau de code, où il faudrait encore environ la même quantité de temps (sauf si la concaténation de chaînes de MySQL est particulièrement lent pour une raison quelconque).

(3) Le transferral de données du serveur au client prend probablement beaucoup de temps, très probablement plus que le temps le serveur doit récupérer les données. Si vous avez des outils pour tracer ce genre de chose, les utiliser. Si vous pouvez augmenter la taille du tableau chercher dans votre client, d'expérimenter avec différentes tailles (par exemple dans l'utilisation JDBC Statement.setFetchSize ()). Cela peut être important même si le client et le serveur sont sur le même hôte.

Je vais essayer de créer une nouvelle table avec les données que vous voulez obtenir. Faire cela signifie que vous perdez des données réelles, mais vous gagnez en rapidité. Cette idée pourrait être similaire à OLAP ou quelque chose comme ça?

Bien sûr, vous devez faire une mise à jour (jour ou autre) de ce tableau.

Je ne suis pas un expert MySQL, mais il semble que les clés primaires MySQL sont regroupés - vous voulez vous assurer que ce soit le cas avec vos clés primaires; index clusterisés aidera certainement à accélérer les choses.

Une chose, cependant - je ne crois pas que vous pouvez avoir deux clés « primaires » sur une table; votre table urls me semble suspect plutôt pour cette raison. Par-dessus tout, vous devez absolument faire en sorte que ces deux colonnes dans la table urls sont indexés sur la garde - un index numérique sur chacun doit être bien - parce que vous vous joignez à eux, de sorte que le SGBD a besoin de savoir comment les trouver rapidement; qui pourrait être ce qui se passe dans votre cas. Si vous êtes table pleine numérisation de lignes, alors oui, vous pourriez être assis là pendant un certain temps alors que le serveur essaie de trouver tout ce que vous avez demandé.

Je vous suggère également la suppression de cette fonction CONCAT de l'instruction select, et de voir comment cela affecte vos résultats. Je serais étonné si cela était pas un facteur contributif en quelque sorte. Il suffit de récupérer les deux colonnes et gérer l'enchaînement après, et voir comment ça se passe.

Enfin, avez-vous compris où le goulot d'étranglement est? Il suffit de se joindre à trois tables de plusieurs millions de ligne ne devrait pas prendre beaucoup de temps (je pense peut-être une seconde ou, juste eyeballing vos tables et requêtes), à condition que les tables sont correctement indexés. Mais si vous pousser ces lignes sur une carte réseau lent ou déjà arrimé à un serveur d'application affamée mémoire, etc., la lenteur pourrait avoir rien à voir avec votre requête du tout, mais avec ce qui se passe après la requête. Sept millions de lignes est un peu de données à l'assemblage et se déplacer, peu importe combien de temps la conclusion de ces lignes arrive à prendre. Essayez de choisir une seule ligne au lieu, plutôt que tous les sept millions, et de voir à quoi ça ressemble en revanche. Si c'est rapide, le problème est pas la requête, il est le jeu de résultats.

En tant que votre résultat des rendements ensemble toutes les données, il y a très peu d'optimisation qui peut être fait du tout. Vous numérisez toute la table, puis se joindre à d'autres tables qui ont des index.

sont les PrimaryKeys Clusterises? Cela garantit que les données sont stockées sur le disque dans l'ordre d'index, évitant ainsi rebondir parties différentes autour du disque.

En outre, vous pouvez avoir la diffusion de données sur plusieurs disques. Si vous avez des URL sur primaire et / CHEMINS HOSTS sur SECONDAIRE alors vous obtiendrez un meilleur débit des lecteurs.

Vous devez regarder la configuration de votre serveur. Les paramètres de la mémoire par défaut pour MySQL paralysera performances sur une table que la taille. Si vous utilisez les paramètres par défaut, vous devez lever au moins et key_buffer_size par au moins join_buffer_size un facteur de 4, peut-être beaucoup plus. Regardez dans la documentation; il y a d'autres paramètres de mémoire que vous pouvez modifier.

MySQL a une performance drôle bizarrerie où si vos tables vont sur une certaine taille avec des requêtes qui renverront la plupart des données, la performance va dans les toilettes. Malheureusement, il n'a aucun moyen de vous dire quand ce seuil est atteint. Il me semble que vous avez, cependant.

Essayez d'optimiser vos tables avant d'exécuter la requête:

optimize table hosts, paths, urls;

Il peut vous faire économiser du temps, surtout si les lignes ont été supprimées des tables. (Voir pour plus d'informations sur OPTIMIZE)

Avez-vous déjà déclaré quelques indices sur les join-attributs?

PS: Voir [lien mort] pour les index sur MySQL 4.x

Le concat est certainement vous ralentit. Peut-on voir les résultats d'un mysql expliquer à ce sujet? Documentation lien

La plus grande chose à faire est d'essayer de tirer uniquement les données dont vous avez besoin cependant. Si vous pouvez tirer moins d'enregistrements qui vous accélérer autant que quoi que ce soit. Mais mysql expliquer devrait nous aider à voir si des index serait utile.

Je ne peux pas dire pour sûr mySQL mais je sais que dans SQL Server que les clés primaires créer un index automatiquement, mais les clés étrangères ne le font pas. Assurez-vous de vérifier qu'il ya un index sur vos champs clés étrangers.

Depuis que je ne suis pas un grand fan de MySQL, je demande si vous avez essayé PostgreSQL. Dans ce DB, vous voulez vous assurer que votre réglage work_mem était assez élevé, mais vous pouvez le régler par connexion DB avec SET work_mem = 64Mo, par exemple.

Une autre suggestion est de regarder dans les entrées de chemin en double. Il sont nombreux URL chemins de partage.

Une autre chose qui pourrait ou ne pourrait pas aider est d'utiliser des champs de texte de longueur fixe au lieu de varchars. Il sert à faire une différence de vitesse mais je ne suis pas sûr sur les moteurs actuels DB.

Si vous utilisez PostgreSQL il vous permettra d'utiliser join En utilisant, mais même sur MySQL Je l'aime plus: le nom de votre champ id le même dans toutes les tables. Au lieu d'id dans les hôtes et hôte urls, nommez-host_id les deux endroits.

Maintenant, un peu plus de commentaires. :) Cette disposition de données que vous avez ici est très utile lorsque vous sélectionnez un petit ensemble de lignes, peut-être toutes les URL du même domaine. Il peut aussi aider à beaucoup si vos requêtes ont souvent besoin de faire des analyses séquentielles de la table urls pour d'autres données qui y sont stockées, parce que l'analyse peut ignorer les grands champs de texte (à moins qu'il n'a pas d'importance parce que votre texte stocke DB via des pointeurs vers une table liée de toute façon).

Cependant, si vous sélectionnez presque toujours toutes les données de domaine et le chemin, alors il est plus judicieux de le stocker dans une table.

scroll top