Was ist schneller, eine große Abfrage oder viele kleine Abfragen?


68

Ich habe für verschiedene Unternehmen gearbeitet und festgestellt, dass einige von ihnen es vorziehen, Ansichten zu haben, die mit all ihren "Verwandten" an einem Tisch zusammenarbeiten. Aber dann müssen wir in der Anwendung manchmal nur eine Spalte verwenden.

Wäre es also schneller, nur einfache Auswahlen zu treffen und diese dann im Systemcode "zusammenzufügen"?

Das System kann PHP, Java, ASP oder jede andere Sprache sein, die eine Verbindung zur Datenbank herstellt.

Die Frage ist also, was ist schneller von einer Serverseite (PHP, Java, ASP, Ruby, Python ...) zur Datenbank? Abfrage, die jeweils nur die Spalten einer Tabelle abruft?


2
Welche SQL-Implementierung verwenden Sie? MySQL, Microsoft SQL Server, Oracle, Postgresql usw.? Bitte aktualisieren Sie Ihren Tag.
RLF

1
Mysql und Postgresql
sudo.ie

6
Ich habe die Erfahrung gemacht, dass MySQL keine komplizierten Abfragen mag und normalerweise mit sehr einfachen Abfragen (aber mehr) schneller ist. Der Abfrageoptimierer von Postgres ist viel besser und dort ist es normalerweise effizienter, eine einzelne große Abfrage auszuführen.
a_horse_with_no_name

3
@a_horse_with_no_name Das ist eine sehr breite Verallgemeinerung, insbesondere im Zusammenhang mit dieser Frage. Der MySQL-Optimierer ist von Natur aus sehr einfach und kann Probleme mit Verknüpfungen und Unterabfragen verursachen, insbesondere bei älteren Versionen von MySQL, die ansonsten schnellere Pläne in PostgreSQL erzeugen, während MySQL für reine OLTP-Ladevorgänge sehr schnell sein kann. Im Zusammenhang mit der Frage ist eine einzelne große Abfrage jedoch schneller als - im schlimmsten Fall - ein SELECT innerhalb einer Programmierschleife (unabhängig vom verwendeten RDBMS).
Jynus

2
@jynus: na ja, die frage ist sehr weit gefasst (plus: ich sagte "in my experience" - andere leute könnten andere erfahrungen haben). Eine Abfrage in einer Endlosschleife ist nie eine gute Idee und fast immer das Ergebnis eines schlechten Designs oder mangelnden Verständnisses für die Arbeit mit einer relationalen Datenbank.
a_horse_with_no_name

Antworten:


69

Was Ihre Frage beantworten würde, ist das Thema ZERLEGUNG BEITRETEN.

Laut Seite 209 des Buches

Hochleistungs-MySQL

Sie können einen Join zerlegen, indem Sie anstelle eines Joins mit mehreren Tabellen mehrere Abfragen mit einer Tabelle ausführen und den Join dann in der Anwendung ausführen. Zum Beispiel anstelle dieser einzelnen Abfrage:

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';

Sie könnten diese Abfragen ausführen:

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);

Warum um alles in der Welt würdest du das tun? Es sieht auf den ersten Blick verschwenderisch aus, weil Sie die Anzahl der Abfragen erhöht haben, ohne dass Sie eine Gegenleistung erhalten. Eine solche Umstrukturierung kann jedoch tatsächlich erhebliche Leistungsvorteile bringen:

  • Caching kann effizienter sein. In vielen Anwendungen werden "Objekte" zwischengespeichert, die Tabellen direkt zugeordnet sind. Wenn in diesem Beispiel das Objekt mit dem Tag mysqlbereits zwischengespeichert ist, überspringt die Anwendung die erste Abfrage. Wenn Sie Beiträge mit der ID 123, 567 oder 908 im Cache finden, können Sie sie aus der IN()Liste entfernen . Der Abfragecache kann ebenfalls von dieser Strategie profitieren. Wenn sich nur eine der Tabellen häufig ändert, kann das Zerlegen eines Joins die Anzahl der Cache-Ungültigkeitserklärungen verringern.
  • Das Ausführen der Abfragen einzeln kann manchmal Sperrenkonflikte reduzieren
  • Durch Joins in der Anwendung wird die Skalierung der Datenbank vereinfacht, indem Tabellen auf verschiedenen Servern platziert werden.
  • Die Abfragen selbst können effizienter sein. In diesem Beispiel können Sie mit einer IN()Liste anstelle eines Joins Zeilen-IDs in MySQL sortieren und Zeilen optimaler abrufen, als dies mit einem Join möglich wäre.
  • Sie können redundante Zeilenzugriffe reduzieren. Wenn Sie einen Join in der Anwendung ausführen, müssen Sie jede Zeile nur einmal abrufen. Bei einem Join in der Abfrage handelt es sich im Wesentlichen um eine Denormalisierung, die möglicherweise wiederholt auf dieselben Daten zugreift. Aus dem gleichen Grund kann eine solche Umstrukturierung auch den gesamten Netzwerkverkehr und die Speichernutzung verringern.
  • In gewissem Maße können Sie diese Technik als manuelles Implementieren eines Hash-Joins anstelle des Algorithmus für verschachtelte Schleifen betrachten, den MySQL zum Ausführen eines Joins verwendet. Ein Hash-Join ist möglicherweise effizienter.

Infolgedessen können Aufgabenverknüpfungen in der Anwendung effizienter sein, wenn Sie viele Daten aus früheren Abfragen zwischenspeichern und wiederverwenden, Daten auf mehrere Server IN()verteilen, Verknüpfungen durch Listen ersetzen oder eine Verknüpfung mehrmals auf dieselbe Tabelle verweist.

ÜBERWACHUNG

Ich mag den ersten Bulletpoint, weil InnoDB ein wenig unbeholfen ist, wenn es den Abfrage-Cache überprüft.

Was den letzten Aufzählungspunkt betrifft, habe ich am 11. März 2013 einen Beitrag geschrieben ( Gibt es einen Ausführungsunterschied zwischen einer JOIN-Bedingung und einer WHERE-Bedingung? ), Der den Nested-Loop-Algorithmus beschreibt. Nachdem Sie es gelesen haben, werden Sie sehen, wie gut die Join-Zerlegung sein kann.

Wie für alle anderen Punkte aus dem Buch , suchen die Entwickler wirklich nach Leistung als Endergebnis. Einige sind auf externe Mittel (außerhalb der Anwendung) angewiesen, um die Leistung zu verbessern, z. B. die Verwendung einer schnellen Festplatte, das Abrufen weiterer CPUs / Kerne, das Optimieren der Speicher-Engine und das Optimieren der Konfigurationsdatei. Andere werden sich anschnallen und besseren Code schreiben. Einige greifen möglicherweise auf das Codieren der gesamten Business Intelligence in gespeicherten Prozeduren zurück, wenden jedoch immer noch keine Verknüpfungszerlegung an (siehe Was sind die Argumente gegen oder zum Einfügen von Anwendungslogik in die Datenbankschicht? Zusammen mit den anderen Posts). Es hängt alles von der Kultur und Toleranz jedes Entwickler-Shops ab.

Einige sind möglicherweise mit der Leistung zufrieden und berühren den Code nicht mehr. Andere erkennen einfach nicht, dass es große Vorteile gibt, die man nutzen kann, wenn sie versuchen, sich der Komposition anzuschließen.

Für diejenigen Entwickler, die bereit sind ...

VERSUCHE ES !!!


3
Bezüglich dieses Links über das Ändern auf 3 Fragen ... Ich kenne und respektiere Baron, Vadim und Peter, aber ich bin mit diesem irreführenden Vorschlag nicht einverstanden. Die meisten Argumente für die Aufteilung sind so selten, dass sie nicht erwähnenswert sind. Bleiben Sie bei einer einzelnen Abfrage bei JOINs und arbeiten Sie an deren Verbesserung.
Rick James

2
@ RickJames Ich stimme dem Geist Ihres Kommentars zu. Im Laufe der Jahre habe ich gesehen, dass manche bei der Zersetzung mitmachen und andere scheitern. Selbst mit den richtigen SQL-Kenntnissen könnte es gegen Sie arbeiten, wenn die Join-Dekomposition nicht richtig durchgeführt wird. Bei meinem jetzigen Arbeitgeber vergrößern und verkleinern sich viele Leute gern, besonders wenn es um Legacy-Code geht und tiefe Taschen zur Verfügung stehen. Mit denen, die Kaviargeschmack, aber Eiersalatbudgets haben, könnte die Zersetzung der Verbindung das Risiko wert sein, muss aber richtig gemacht werden.
RolandoMySQLDBA

Ich würde gerne sehen, wie dies in einer Oracle-Umgebung funktioniert, wenn ich die Rechte und die Zeit hätte.
Rick Henderson

Eine andere Möglichkeit, die schneller sein kann, besteht darin, dass beim Bestellen insgesamt weniger Berechnungen erforderlich sind, um kleinere Listen als eine große Liste zu bestellen.
Evan Siroky

24

In Postgres (und wahrscheinlich in einem ähnlichen Ausmaß in jedem RDBMS, in einem geringeren Ausmaß in MySQL) sind weniger Abfragen fast immer viel schneller.

Der Aufwand für das Parsen und Planen mehrerer Abfragen ist in den meisten Fällen bereits höher als der mögliche Gewinn.

Ganz zu schweigen von der zusätzlichen Arbeit, die im Client erledigt werden muss, indem die Ergebnisse kombiniert werden, was in der Regel viel langsamer ist. Ein RDBMS ist auf diese Art von Aufgaben spezialisiert, und Vorgänge basieren auf den ursprünglichen Datentypen. Kein Casting textfür Zwischenergebnisse oder keine Umwandlung in native Client-Typen, was sogar zu weniger korrekten (oder falschen!) Ergebnissen führen kann. Denken Sie an Gleitkommazahlen ...

Sie übertragen auch mehr Daten zwischen DB-Server und Client. Dies kann für eine Hand voller Werte vernachlässigbar sein oder einen großen Unterschied machen.

Wenn mehrere Abfragen mehrere Roundtrips zum Datenbankserver bedeuten, erfassen Sie auch das Mehrfache der Netzwerklatenz und des Transaktionsaufwands, möglicherweise sogar des Verbindungsaufwands. Großer, großer Verlust.

Abhängig von Ihrer Konfiguration kann die Netzwerklatenz allein um Größenordnungen länger dauern als der Rest.

Verwandte Frage zu SO:

Es kann einen Wendepunkt für sehr große , lange laufende Abfragen geben, da Transaktionen unterwegs Sperren für DB-Zeilen sammeln. Sehr große Abfragen können über einen längeren Zeitraum viele Sperren enthalten, was zu Reibungsverlusten bei gleichzeitigen Abfragen führen kann .


Was halten Sie aus Neugier für sehr groß ?
Sablefoste

@Sablefoste: Viel hängt von Ihren Zugriffsmustern ab. Ein kritischer Punkt ist, wenn gleichzeitige Transaktionen anstehen und darauf warten, dass Sperren freigegeben werden. Oder wenn Sie genügend Sperren sammeln, um einen wesentlichen Teil Ihrer Ressourcen zu verbrauchen. Oder wenn Ihre Abfragen lange genug dauern, um das automatische Absaugen zu stören ...
Erwin Brandstetter,

Aber wenn wir eine etwas typische Situation annehmen - eine Abfrage, die einen Outer Join verwendet und eine Menge redundanter Daten für die "übergeordnete" Tabelle zurückgibt, die dann von der App (höchstwahrscheinlich eine ORM-Bibliothek) verglichen mit a analysiert und aussortiert werden muss small select ruft zuerst alle erforderlichen IDs ab und dann eine weitere kleinere Auswahl mit IN () anstelle von Outer Join? Wird der zweite Ansatz nicht effizienter sein (wenn man bedenkt, dass sowohl die Datenbank als auch die App CPU und Kommunikationsbandbreite verbrauchen)?
JustAMartin

1
@JustAMartin: Das klingt nach der Art von Abfrage, die mit ziemlicher Sicherheit schneller ausgeführt wird, wenn der Abfrageplaner des RDBMS die richtigen Abfragen voraussetzt. Betrifft returns lots of redundant data for "parent" table: Warum würden Sie redundante Daten zurückgeben? Geben Sie nur die Daten zurück, die Sie benötigen.
Erwin Brandstetter

1
Bei der äußeren Verknüpfung gibt RDBMS Daten aus der übergeordneten Tabelle zurück, die für jedes verknüpfte untergeordnete Element dupliziert wurden. Dies bedeutet einen gewissen Netzwerk- und Speicheraufwand und anschließend eine zusätzliche Syntaxanalyse im ORM-Tool, um die doppelten übergeordneten Werte zu entfernen und nur ein übergeordnetes Element mit n untergeordneten Elementen beizubehalten. Mit einer einzelnen Abfrage sparen wir uns also die effiziente Arbeit des RDBMS-Abfrageplaners, weniger Netzwerk- (oder lokale Pipe-) Anforderungen, verlieren jedoch zusätzliche nicht benötigte Nutzdaten und verschieben Daten in der ORM-Bibliothek. Ich denke, es ist wie immer - vor dem Optimieren messen.
JustAMartin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.