Qu'est-ce qui est plus rapide, une grosse requête ou plusieurs petites requêtes?


68

J'ai travaillé pour différentes entreprises et j'ai remarqué que certaines d'entre elles préféraient avoir un point de vue qui rejoindrait une table avec tous ses "proches". Mais quelques fois sur l'application, nous n'avons besoin que d'une colonne.

Alors, serait-il plus rapide de simplement faire des sélections simples, puis de les "rejoindre" dans le code système?

Le système pourrait être php, java, asp, n'importe quelle langue qui se connecte à la base de données.

La question est donc de savoir ce qui est plus rapide pour passer du côté serveur (php, java, asp, ruby, python ...) à la base de données, exécuter une requête qui obtienne tout ce dont nous avons besoin ou allant du côté serveur à la base de données et exécuter un requête qui obtient seulement les colonnes d'une table à la fois?


2
Quelle implémentation de 'SQL' utilisez-vous? MySQL, Microsoft SQL Server, Oracle, Postgresql, etc.? Veuillez mettre à jour votre tag.
RLF

1
Mysql et Postgresql
sudo.ie

6
D'après mon expérience, MySQL n'aime pas les requêtes compliquées et est généralement plus rapide avec des requêtes très simples (mais plus). L'optimiseur de requêtes de Postgres est bien meilleur et il est généralement plus efficace d'exécuter une seule requête volumineuse.
a_horse_with_no_name

3
@a_horse_with_no_name C'est une généralisation très large, spécialement dans le contexte de cette question. L’optimiseur MySQL est en effet très simple par conception et peut causer des problèmes de jointures et de sous-requêtes - en particulier sur les anciennes versions de MySQL - qui ont autrement produit des plans plus rapides dans PostgreSQL, tandis que MySQL peut être très rapide pour des charges OLTP pures. Toutefois, dans le contexte de la question, une seule requête volumineuse sera plus rapide que, disons, dans le pire des cas, un SELECT dans une boucle de programmation (quel que soit le SGBDR utilisé).
Jynus

2
@jynus: eh bien, la question est très large (plus: j'ai dit "selon mon expérience" - d'autres personnes pourraient avoir des expériences différentes). Une requête dans une boucle n'est jamais une bonne idée et résulte presque toujours d'une conception médiocre ou du manque de compréhension du fonctionnement d'une base de données relationnelle.
a_horse_with_no_name

Réponses:


69

Ce qui répondrait à votre question est le sujet JOIN DECOMPOSITION.

Selon la page 209 du livre

MySQL haute performance

Vous pouvez décomposer une jointure en exécutant plusieurs requêtes à table unique au lieu d'une jointure multitable, puis en effectuant la jointure dans l'application. Par exemple, au lieu de cette requête unique:

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

Vous pourriez exécuter ces requêtes:

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

Pourquoi diable ferais-tu cela? Cela semble inutile à première vue, car vous avez augmenté le nombre de requêtes sans rien obtenir en retour. Cependant, une telle restructuration peut en réalité offrir des avantages significatifs en termes de performances:

  • La mise en cache peut être plus efficace. De nombreuses applications mettent en cache des "objets" mappés directement sur des tables. Dans cet exemple, si l'objet avec la balise mysqlest déjà mis en cache, l'application ignorera la première requête. Si vous trouvez des publications avec un identifiant de 123, 567 ou 908 dans le cache, vous pouvez les supprimer de la IN()liste. Le cache de requêtes pourrait également bénéficier de cette stratégie. Si une seule des tables change fréquemment, la décomposition d'une jointure peut réduire le nombre d'invalidations du cache.
  • L'exécution individuelle des requêtes peut parfois réduire les conflits de verrous
  • Les jointures dans l'application facilitent la mise à l'échelle de la base de données en plaçant des tables sur différents serveurs.
  • Les requêtes elles-mêmes peuvent être plus efficaces. Dans cet exemple, utiliser une IN()liste au lieu d'une jointure permet à MySQL de trier les ID de ligne et de récupérer les lignes de manière plus optimale qu'avec une jointure.
  • Vous pouvez réduire les accès redondants aux lignes. Faire une jointure dans l'application signifie extraire chaque ligne une seule fois, alors qu'une jointure dans la requête est essentiellement une dénormalisation qui peut accéder de manière répétée aux mêmes données. Pour la même raison, une telle restructuration pourrait également réduire le trafic total sur le réseau et l'utilisation de la mémoire.
  • Dans une certaine mesure, vous pouvez voir cette technique comme implémentant manuellement une jointure de hachage au lieu de l'algorithme de boucles imbriquées utilisé par MySQL pour exécuter une jointure. Une jointure de hachage pourrait être plus efficace.

En conséquence, les jointures des actions dans l'application peuvent être plus efficaces lorsque vous mettez en cache et réutilisez une grande quantité de données de requêtes précédentes, que vous répartissez les données sur plusieurs serveurs, que vous remplacez les jointures par des IN()listes ou que la jointure fait référence à la même table plusieurs fois.

OBSERVATION

J'aime le premier point parce qu'InnoDB est un peu lourd lorsqu'il vérifie le cache de requêtes.

En ce qui concerne le dernier point, j'ai écrit un article le 11 mars 2013 ( Existe-t-il une différence d'exécution entre une condition JOIN et une condition WHERE? ) Décrivant l'algorithme de la boucle imbriquée. Après l'avoir lu, vous verrez à quel point la décomposition des jointures est efficace.

Comme pour tous les autres points du livre , les développeurs recherchent vraiment la performance comme résultat. Certaines s'appuient sur des moyens externes (en dehors de l'application) pour améliorer les performances, telles que l'utilisation d'un disque rapide, l'obtention de davantage de processeurs / cœurs, le réglage du moteur de stockage et le fichier de configuration. D'autres vont s'attacher et écrire un meilleur code. Certains peuvent recourir à la codification de toute l'intelligence d'affaires dans les procédures stockées, sans toujours appliquer la décomposition de jointure (voir Quels sont les arguments contre ou pour placer la logique d'application dans la couche base de données? Avec les autres publications). Tout dépend de la culture et de la tolérance de chaque développeur.

Certains peuvent être satisfaits des performances et ne plus toucher au code. D’autres ne réalisent tout simplement pas qu’il ya de grands avantages à tirer s’ils essaient de joindre la composition.

Pour les développeurs qui veulent ...

ESSAIE !!!


3
En ce qui concerne ce lien sur le passage à 3 requêtes ... Je connais et respecte Baron, Vadim et Peter, mais je ne suis pas d’accord avec cette suggestion trompeuse. La plupart des arguments en faveur de la scission sont si rares qu'ils ne méritent pas d'être mentionnés. Tenez-vous en à une seule requête avec JOIN, puis travaillons à l’améliorer.
Rick James

2
@ RickJames Je suis d'accord avec l'esprit de votre commentaire. Au fil des ans, j’ai vu des travaux de décomposition en commun pour certains et des échecs pour d’autres. Même avec les compétences SQL appropriées, cela pourrait vous être préjudiciable si la décomposition de la jointure n’est pas effectuée correctement. Chez mon employeur actuel, de nombreux départements aiment passer à la vitesse supérieure, en particulier lorsque des codes hérités sont impliqués et que des poches profondes sont disponibles. Pour ceux qui ont un goût de caviar mais des budgets de salade aux œufs, la décomposition jointe pourrait valoir le risque mais doit être faite correctement.
RolandoMySQLDBA

J'aimerais voir comment cela fonctionne dans un environnement Oracle si j'avais les droits et le temps.
Rick Henderson

Une autre façon de procéder peut être plus rapide: si vous commandez, les calculs seront généralement moins nombreux pour ordonner des listes plus petites que pour une liste volumineuse.
Evan Siroky

24

Dans Postgres (et probablement dans n'importe quel SGBDR, MySQL dans une moindre mesure), moins de requêtes sont presque toujours beaucoup plus rapides.

La surcharge liée à l'analyse et à la planification de plusieurs requêtes représente déjà un avantage non négligeable dans la plupart des cas.

Sans parler du travail supplémentaire à effectuer chez le client, combinant les résultats, ce qui est généralement beaucoup plus lent. Un SGBDR est spécialisé dans ce type de tâche et les opérations sont basées sur les types de données d'origine. Aucune conversion vers textet en arrière pour des résultats intermédiaires ou une conversion en types natifs du client, ce qui peut même conduire à des résultats moins corrects (ou incorrects!). Pensez aux nombres à virgule flottante ...

Vous transférez également davantage de données entre le serveur de base de données et le client. Cela peut être négligeable pour une main pleine de valeurs ou faire une énorme différence.

Si plusieurs requêtes signifient plusieurs allers et retours vers le serveur de base de données, vous collectez également plusieurs fois la latence du réseau et la surcharge de la transaction, voire la connexion. Grosse, grosse perte.

Selon votre configuration, la latence du réseau à elle seule peut prendre plus longtemps que tous les autres, par ordre de grandeur.

Question connexe sur SO:

Il peut y avoir un tournant pour les requêtes très volumineuses et longues, car les transactions collectent des verrous sur les lignes de base de données en chemin. Les très grandes requêtes peuvent conserver de nombreux verrous pendant une période prolongée, ce qui peut entraîner des frictions avec des requêtes simultanées .


Juste par curiosité, que considérez-vous comme très gros ?
Sablefoste

@Sablefoste: Cela dépend beaucoup de vos habitudes d'accès. Un point critique est le moment où les transactions simultanées commencent à se mettre en file d'attente, en attendant que les verrous soient libérés. Ou si vous accumulez suffisamment de verrous pour consommer une partie substantielle de vos ressources. Ou si vos requêtes durent assez longtemps pour interférer avec l'autovacuum ...
Erwin Brandstetter

Mais si nous prenons une situation quelque peu typique - une requête qui utilise une jointure externe et renvoie beaucoup de données redondantes pour la table "parent", qui doit ensuite être analysée et triée par l'application (très probablement, une bibliothèque ORM) par rapport à une petite sélection qui récupère d'abord tous les ID requis, puis une autre plus petite sélection avec IN () au lieu d'une jointure externe? La seconde approche ne sera-t-elle pas plus efficace (en considérant à la fois la CPU et la bande passante consommée par les DB et les applications)?
JustAMartin

1
@JustAMartin: Cela ressemble au genre de requête qui est certainement certainement plus rapide lorsque gérée par le planificateur de requêtes du SGBDR - en supposant que les requêtes soient correctes. Concernant returns lots of redundant data for "parent" table: Pourquoi renverriez-vous des données redondantes? Renvoyez uniquement les données dont vous avez besoin.
Erwin Brandstetter

1
Avec une jointure externe, le SGBDR renvoie les données de la table parent dupliquées pour chaque enfant joint, ce qui signifie une surcharge du réseau et de la mémoire, puis une analyse complémentaire dans l'outil ORM afin d'éliminer les valeurs parent dupliquées et de ne conserver qu'un seul parent avec n enfants. Ainsi, avec une seule requête, nous économisons sur le travail efficace du planificateur de requêtes RDBMS, moins de demandes de réseau (ou de canal local), mais nous perdons de la charge utile inutile et du transfert de données dans la bibliothèque ORM. Je suppose que c'est comme toujours - mesurer avant d'optimiser.
JustAMartin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.