Sind mehrere Datenbankaufrufe bei einem Netzwerkaufruf für eine Web-API wirklich von Bedeutung?

16

Bei einem meiner Arbeitgeber haben wir an einer REST-API gearbeitet (dies gilt jedoch auch für SOAP). Der Client, bei dem es sich um die Benutzeroberfläche der Anwendung handelt, ruft die API über das Web (LAN in typischen Produktionsbereitstellungen) auf. Die API ruft die Datenbank auf.

Ein Thema, das sich in unseren Diskussionen wiederholt, ist die Leistung: Einige Teammitglieder sind der Ansicht, dass Sie aus Leistungsgründen nicht mehrere Datenbankaufrufe (normalerweise Lesezugriffe) von einem einzelnen API-Aufruf aus durchführen sollten. Sie sollten sie so optimieren, dass jeder API-Aufruf nur (genau) einen Datenbankaufruf hat.

Aber ist das wirklich wichtig? Bedenken Sie, dass die Benutzeroberfläche einen Netzwerkaufruf an die API durchführen muss. das ist ziemlich groß (Größenordnung von Millisekunden). Datenbanken sind optimiert, um den Arbeitsspeicher zu erhalten und Lesevorgänge sehr, sehr schnell auszuführen (z. B. SQL Server lädt und speichert alles im Arbeitsspeicher und belegt fast den gesamten freien Arbeitsspeicher, wenn dies möglich ist).

TLDR: Ist es wirklich wichtig, sich über mehrere Datenbankaufrufe Gedanken zu machen, wenn wir bereits einen Netzwerkanruf über das LAN tätigen? Wenn ja warum?

Um es klar auszudrücken, ich spreche von der Größenordnung - ich weiß, dass es von Besonderheiten abhängt (Maschinenhardware, Auswahl der API und der Datenbank usw.) Anrufe, die eine Größenordnung kleiner sind, sind eigentlich wichtig? Oder gibt es mehr zum Problem als dieses?

Edit: Für die Nachwelt halte ich es für ziemlich lächerlich, zu behaupten, dass wir die Leistung verbessern müssen, indem wir Datenbankaufrufe unter diesen Umständen kombinieren - insbesondere bei fehlender Profilerstellung. Es ist jedoch nicht meine Entscheidung, ob wir dies tun oder nicht; Ich möchte wissen, welche Gründe dafür sprechen, dass dies eine korrekte Methode zur Optimierung von Web-API-Aufrufen ist.

ashes999
quelle
Gibt es keinen weiteren Netzwerkaufruf zwischen der API-Schicht und der Datenbank?
Anmeldung
4
Was haben Ihre Timing-Tests gezeigt?
Dan Pichelman
@Sign Es besteht kein Netzwerkaufruf zwischen der API und der DB. Soweit ich weiß, befinden sie sich garantiert auf demselben Computer.
Asche999
@ DanPichelman das ist, was ich auch frage. Niemand scheint Leistung zu nehmen und zu planen; Wir müssen nur die Leistung in X verbessern, indem wir alle DB-Aufrufe zu einem einzigen Aufruf kombinieren.
Asche999

Antworten:

25

Aber ist das wirklich wichtig? Bedenken Sie, dass die Benutzeroberfläche einen Netzwerkaufruf an die API durchführen muss. das ist ziemlich groß (Größenordnung von Millisekunden). Datenbanken sind optimiert, um den Arbeitsspeicher zu erhalten und Lesevorgänge sehr, sehr schnell auszuführen (z. B. SQL Server lädt und speichert alles im Arbeitsspeicher und belegt fast den gesamten freien Arbeitsspeicher, wenn dies möglich ist).

Die Logik

Theoretisch hast du recht. Es gibt jedoch ein paar Fehler mit dieser Begründung:

  1. Nach Ihren Angaben ist nicht klar, ob Sie Ihre App tatsächlich getestet / profiliert haben. Mit anderen Worten, wissen Sie tatsächlich , dass die Netzwerkübertragungen von der App zur API die langsamste Komponente sind? Da dies intuitiv ist, ist es leicht anzunehmen, dass dies der Fall ist. Wenn Sie jedoch über die Leistung sprechen, sollten Sie niemals davon ausgehen. Bei meinem Arbeitgeber bin ich der Leistungsführer. Als ich anfing, redeten die Leute über CDNs, Replikation usw., basierend auf der Intuition darüber, was die Engpässe sein müssen. Es stellte sich heraus, dass unsere größten Leistungsprobleme darin bestanden, Datenbankabfragen schlecht durchzuführen.

  2. Sie sagen, dass, weil Datenbanken Daten gut abrufen können, die Datenbank notwendigerweise mit maximaler Leistung ausgeführt wird, optimal genutzt wird und nichts getan werden kann, um sie zu verbessern. Mit anderen Worten, Datenbanken sind so konzipiert, dass sie schnell sind, sodass ich mir darüber keine Sorgen machen sollte. Eine weitere gefährliche Denkrichtung. Das ist, als würde man sagen, ein Auto soll sich schnell bewegen, damit ich das Öl nicht wechseln muss.

  3. Diese Denkweise geht von einem einzelnen Prozess aus, oder anders ausgedrückt, von keiner Nebenläufigkeit. Es wird davon ausgegangen, dass eine Anforderung die Leistung einer anderen Anforderung nicht beeinflussen kann. Ressourcen werden gemeinsam genutzt, z. B. Festplatten-E / A, Netzwerkbandbreite, Verbindungspools, Arbeitsspeicher, CPU-Zyklen usw. Daher kann die Reduzierung der Nutzung einer gemeinsam genutzten Ressource durch einen Datenbankaufruf verhindern, dass andere Anforderungen verlangsamt werden. Als ich zum ersten Mal zu meinem derzeitigen Arbeitgeber kam, war das Management der Ansicht, dass die Optimierung einer Datenbankabfrage von 3 Sekunden Zeitverschwendung war. 3 Sekunden sind so wenig, warum Zeit darauf verschwenden? Wären wir mit einem CDN oder einer Komprimierung oder etwas anderem nicht besser dran? Wenn ich jedoch eine 3-Sekunden-Abfrage in 1 Sekunde ausführen kann, indem ich beispielsweise einen Index hinzufüge, der 2/3 weniger blockiert, 2/3 weniger Zeit für das Besetzen eines Threads benötigt und vor allem weniger Daten von der Festplatte liest,

Die Theorie

Es gibt eine verbreitete Vorstellung, dass es bei der Software-Leistung einfach um Geschwindigkeit geht .

Sie haben aus reiner Geschwindigkeitsperspektive Recht. Ein System ist nur so schnell wie seine langsamste Komponente. Wenn Sie Ihren Code profiliert haben und festgestellt haben, dass das Internet die langsamste Komponente ist, ist alles andere offensichtlich nicht der langsamste Teil.

Ich hoffe jedoch, dass Sie in Anbetracht der obigen Ausführungen sehen können, wie Ressourcenkonflikte, mangelnde Indizierung, schlecht geschriebener Code usw. zu überraschenden Leistungsunterschieden führen können.

Die Annahmen

Eine letzte Sache. Sie erwähnten, dass ein Datenbankaufruf im Vergleich zu einem Netzwerkaufruf von der App zur API günstig sein sollte. Sie haben aber auch erwähnt, dass sich die App und die API-Server im selben LAN befinden. Sind sie also nicht beide vergleichbar mit Netzanrufen? Mit anderen Worten, warum nehmen Sie an, dass die API-Übertragung um Größenordnungen langsamer ist als die Datenbankübertragung, wenn beide dieselbe verfügbare Bandbreite haben? Natürlich sind die Protokolle und Datenstrukturen unterschiedlich, das verstehe ich, aber ich bestreite die Annahme, dass es sich um Größenordnungen handelt, die sich unterscheiden.

Wo es Murkey bekommt

Bei dieser ganzen Frage geht es um "mehrere" oder "einzelne" Datenbankaufrufe. Es ist jedoch unklar, wie viele mehrere sind. Aufgrund der oben genannten allgemeinen Faustregel empfehle ich, so wenig Datenbankaufrufe wie nötig durchzuführen. Das ist aber nur eine Faustregel.

Hier ist warum:

  1. Datenbanken eignen sich hervorragend zum Lesen von Daten. Sie sind Speichermaschinen. Ihre Geschäftslogik lebt jedoch in Ihrer Anwendung. Wenn Sie die Regel festlegen, dass jeder API-Aufruf genau zu einem Datenbankaufruf führt, wird Ihre Geschäftslogik möglicherweise in der Datenbank gespeichert. Vielleicht ist das in Ordnung. Das machen viele Systeme. Aber manche nicht. Es geht um Flexibilität.
  2. Manchmal möchten Sie, um eine gute Entkopplung zu erzielen, zwei Datenbankaufrufe getrennt haben. Beispielsweise wird möglicherweise jede HTTP-Anforderung durch einen generischen Sicherheitsfilter geleitet, der aus der Datenbank überprüft, ob der Benutzer über die richtigen Zugriffsrechte verfügt. Wenn dies der Fall ist, führen Sie die entsprechende Funktion für diese URL aus. Diese Funktion kann mit der Datenbank interagieren.
  3. Aufruf der Datenbank in einer Schleife. Deshalb habe ich gefragt, wie viele mehrere sind. Im obigen Beispiel hätten Sie 2 Datenbankaufrufe. 2 ist in Ordnung. 3 kann gut sein. N ist nicht in Ordnung. Wenn Sie die Datenbank in einer Schleife aufrufen, haben Sie die Leistung jetzt linearisiert. Dies bedeutet, dass die Eingabe in der Schleife umso länger dauert. Wenn Sie also kategorisch sagen, dass die API-Netzwerkzeit die langsamste ist, übersehen Sie Anomalien wie 1% Ihres Datenverkehrs, die aufgrund einer noch nicht entdeckten Schleife, die die Datenbank 10.000 Mal aufruft, sehr lange.
  4. Manchmal gibt es Dinge, in denen Ihre App besser ist, wie zum Beispiel komplexe Berechnungen. Möglicherweise müssen Sie einige Daten aus der Datenbank lesen, einige Berechnungen durchführen und dann basierend auf den Ergebnissen einen Parameter an einen zweiten Datenbankaufruf übergeben (möglicherweise, um einige Ergebnisse zu schreiben). Wenn Sie diese zu einem einzigen Aufruf zusammenfassen (wie bei einer gespeicherten Prozedur), nur um die Datenbank nur einmal aufzurufen, müssen Sie die Datenbank für etwas verwenden, das der App-Server möglicherweise besser kann.
  5. Lastenausgleich: Sie haben (vermutlich) 1 Datenbank und mehrere Anwendungsserver mit Lastenausgleich. Je mehr Arbeit die App leistet und je weniger die Datenbank leistet, desto einfacher ist die Skalierung, da es im Allgemeinen einfacher ist, einen App-Server hinzuzufügen, als die Datenbankreplikation einzurichten. Basierend auf dem vorherigen Aufzählungspunkt kann es sinnvoll sein, eine SQL-Abfrage auszuführen, dann alle Berechnungen in der Anwendung durchzuführen, die auf mehrere Server verteilt ist, und anschließend die Ergebnisse zu schreiben. Dies kann zu einem besseren Durchsatz führen (auch wenn die gesamte Transaktionszeit gleich ist).

TL; DR

TLDR: Ist es wirklich wichtig, sich über mehrere Datenbankaufrufe Gedanken zu machen, wenn wir bereits einen Netzwerkanruf über das LAN tätigen? Wenn ja warum?

Ja, aber nur bis zu einem gewissen Grad. Sie sollten versuchen, die Anzahl der Datenbankaufrufe zu minimieren, wenn dies sinnvoll ist. Kombinieren Sie jedoch keine Aufrufe, die nichts miteinander zu tun haben, nur um sie zu kombinieren. Vermeiden Sie außerdem, die Datenbank unter allen Umständen in einer Schleife aufzurufen.

Brandon
quelle
3

Klingt so, als würde Ihr Team optimieren, bevor es einen Grund dazu hat. Haben Sie die Zeit gemessen, um diese Anforderungen auszuführen? Es besteht die Möglichkeit, dass dieses Paradigma zu einer schlechteren Leistung für den Endbenutzer führt, da Roundtrips zum Webserver eine viel höhere Latenz aufweisen als die Verbindungszeit vom Webserver zur Datenbank. Darüber hinaus stellen die meisten Webbrowser nur zwei gleichzeitige Verbindungen zu einem einzelnen Webserver her, sodass bei komplexen Seiten dort wahrscheinlich ein Engpass auftritt.

In beiden Fällen sollten Optimierungsentscheidungen nicht ohne Daten zur Sicherung getroffen werden. Messen Sie es und finden Sie heraus, was für Ihre Anwendung am besten ist.

brianfeucht
quelle
1
Dies ist ein guter Kommentar zu unseren schlechten Leistungspraktiken, beantwortet jedoch nicht meine Frage, ob DB-Aufrufe besorgniserregend sind, wenn ich bereits einen Netzwerkanruf habe.
Ashes999
1
Im Allgemeinen habe ich festgestellt, dass das Aufrufen mehrerer Datenbanken kein Problem darstellt. Dies ist hauptsächlich auf das Verbindungspooling und die geringe Latenz zwischen der Datenbank und dem Webserver zurückzuführen. Es gibt einen Punkt, an dem das Tätigen einer Reihe verschiedener db-Anrufe die Leistung negativ beeinflusst, aber ich habe keine harte Nummer für Sie. Alles hängt von der Umgebung und der Anwendung ab. Nur das Messen gibt Ihnen die Antwort, die Sie suchen.
brianfeucht
Es sollte nicht (unbedingt) von Besonderheiten abhängen, denn ich spreche von Größenordnungen.
Asche999
Nur grobe Vermutungen (Sie müssen messen): Durchschnittliche Zeit für die Verbindung zur Datenbank vom Webserver: 2 ms Durchschnittliche Zeit für die Verbindung zum Webserver vom Client: 20 ms Unter der Annahme, dass diese Zahlen, die ich zufällig aus der Luft gezogen habe, korrekt sind, könnten Sie 10 tun Datenbankaufrufe in der Zeit, die für einen Webdienstaufruf erforderlich ist. Angenommen, die Datenbankabfragen benötigen dieselbe Zeit. Diese Zahlen hängen stark von der Umgebung ab. Wenn der Client, der den Webdienstaufruf ausführt, lokal ist, kann dies zu einem Rückgang um mehrere Größenordnungen führen.
brianfeucht
2

Wir können es dir nicht sagen.

Wir wissen nicht, wie Ihre Anfragen aussehen. Wir wissen nicht, wie lange sie dauern. Wir wissen nicht, wie viel Aufwand mit jeder Anforderung an Ihren API-Server verbunden ist. Wir wissen nicht, wie geografisch Ihre Kunden verteilt sind. Etc.

Wenn dies ein Szenario ist, das optimiert werden muss und in dem Sie entscheiden können , ob Sie die Anrufe aufteilen oder zusammenführen möchten, müssen Sie es auf beide Arten vergleichen : Entscheiden Sie, für was Sie optimieren (Wartezeit auf der Benutzeroberfläche, Server-CPU-Auslastung, Konflikte, usw.) und wählen Sie die aus, die Ihr Optimierungsziel besser erreicht.


Abgesehen davon ist das Einzige , was ich mit relativer Sicherheit hinzufügen kann:

Innerhalb einer einzelnen Anforderung sollten Sie alle Abfragen ausführen, die zum Erstellen einer Antwort erforderlich sind.

Mit anderen Worten, wenn die Antwort erst generiert werden kann, wenn alle N Abfragen ausgeführt wurden, ist es normalerweise sinnlos, sie zu trennen. Wenn Sie nach jeder Abfrage aussagekräftige Ergebnisse erzielen können, starten Sie das Benchmarking.

Svidgen
quelle
1

Zwei Gedanken:

Erstens tätigt der Verbraucher, der die API verwendet, einen einzigen Aufruf, um eine Aufgabe auszuführen. Was passiert, nachdem Ihr Server den Anruf zum Befüllen der Anfrage erhalten hat, sollte nicht so starr sein. Wenn für diesen einen Anruf eines Verbrauchers 10 Unteraufgaben erforderlich sind, um die Daten zusammenzuführen und zurückzugeben, sollte dies akzeptabel sein.

Zweitens: Wird bei dem betreffenden Prozess ein tatsächliches Problem mit der Datenbankleistung festgestellt? Meine Erfahrung hat gezeigt, dass der Versuch, alle Aspekte einer Datenbankanforderung in einem einzigen Anruf zusammenzufassen, zu einem weniger effizienten Anruf führen kann, als wenn lediglich drei oder vier Datenanrufe getätigt werden. Moderne Datenbanken sind in Caching- und Ausführungsplänen sehr effizient. Wenn Sie versuchen, zu viel zu tun, sehen Sie häufig Prozeduren mit Cursorn (sehr schlecht für die Leistung, da die Daten zeilenweise und nicht als Satz gleichzeitig verarbeitet werden) und Code, der zu einem weniger effizienten Plan führt, als wenn Sie dies getan hätten Der Aufruf erfolgt in mehreren kleinen einfachen Schritten.

Aus Gründen der einfachen Organisation des Codes stimme ich zu, dass jeder API-Aufruf möglicherweise eine einzelne gespeicherte Prozedur (oder DB-Funktion) aufrufen sollte, die wiederum für das Ausfüllen der Anforderung verantwortlich ist. Das Verfahren kann mehr als einen Schritt umfassen.

Richard
quelle
Ich stimme Ihnen zu, wenn es darum geht, die Leistung zu messen, was anscheinend niemand tut. Es gibt keinen Beweis, dass dies schneller ist, aber es kommt immer wieder auf. Die Leistung tritt als Problem auf, wenn wir einige Anrufe haben, die beispielsweise 1000 DBs tätigen können SELECT.
Asche999
@ ashes999 Wenn Sie sich die Anzahl der Datenbankaufrufe genauer ansehen, ist dies wahrscheinlich in der Indizierungsstrategie usw. zu finden, nicht in der Anzahl der Aufrufe. Wie alle angegeben haben, schauen Sie sich die Leistungsdaten an.
Richard
Richard, dem stimme ich zu und das weiß ich auch. Meine Frage ist, warum verschiedene Leute immer wieder darauf hinweisen, dass "mehrere DB-Aufrufe langsam sind", wenn es sich um einen Netzwerkaufruf handelt. Ich verstehe wirklich nicht, wie wichtig es sein kann.
Ashes999
@ ashes999 Sorry, vielleicht solltest du etwas mehr Details über den Netzwerkanruf erfahren, da das offensichtlich ist, habe ich das Gefühl, dass deine Frage etwas mehr ist. Ich habe das Gefühl, wir vermissen etwas in Ihren Fragen. Sie werden immer eine gewisse Netzwerklatenz haben und jeder Anruf erhöht sich potenziell um das x-fache für jeden Anruf (in einfachen Worten). Die Aussage zum Nennwert ist wahr, mehrere Netzwerkanrufe sind langsamer als ein Netzwerkanruf an die Datenbank. Aus diesem Grund empfehle ich einen Aufruf einer gespeicherten Prozedur, die mehrere Aufrufe an die Datenbank ohne die Multi-Netzwerk-Aufrufe durchführen kann.
Richard
1

Befindet sich die Datenbank auf einem anderen Server als Ihr REST-Service, führt jeder Datenbankaufruf zu einem Netzwerk-Roundtrip, was die Leistung erheblich beeinträchtigen kann:

Ich habe einmal beobachtet, dass ein einziger Webservice-Aufruf in etwa 500 Datenbankabfragen übersetzt wurde - dies war kaum ein Problem, wenn sich sowohl der Webservice als auch die Datenbank auf demselben Computer befanden, aber wenn sie sich auf einem anderen Computer befanden, wurde daraus eine Antwortzeit von 6-7 Sekunden maschinen.

Offensichtlich sind 500 Roundtrips in die Datenbank ziemlich extrem. Ich bin mir nicht sicher, wie hoch Ihre Leistungsanforderungen sind, aber als Faustregel würde ich sagen, dass Sie keinen signifikanten Leistungseinbruch erleben sollten, wenn Sie unter 10 Datenbankabfragen pro REST-Aufruf bleiben.

Astrotrain
quelle
1

Wir haben ein paar Anwendungen, die sehr, sehr gesprächig sind. Es gibt für jeden einen Datenbankaufruf. Single. Wenig. Sache. Das wiederholte Bereitstellen von Referenzdaten ist ein wichtiger Teil der Arbeitsbelastung des Systems. Die gesamte Zeitplanung für Worker-Threads, das Erfassen und Löschen von Sperren, das Planen der Cache-Überprüfung usw. summiert sich auch dann, wenn keine tatsächliche Platten-E / A vorhanden ist. Die Konkurrenz ist höher, da Transaktionen Sperren über mehrere DB-Aufrufe hinweg halten müssen und der Durchsatz daher viel geringer ist als er sein könnte. Aus diesem Grund müssen diese Teams jetzt neue, sehr teure DB-Server kaufen.

Obwohl der Großteil der in der aktuellen Konfiguration Ihres Systems verstrichenen Zeit mit REST-API-Aufrufen verbracht wird, werden durch das Ignorieren der Leistung auf DB-Ebene Probleme für die Zukunft gespeichert.

Michael Green
quelle
0

Der dargestellte Optimierungspfad ist einfach die falsche Betrachtungsweise.

API-Aufrufe sollten atomar sein. Mit anderen Worten, ich sollte in der Lage sein, 1 Web-API-Aufruf durchzuführen, um die gewünschte Aktion auszuführen. Ob das zum Abrufen von Daten, Aktualisieren eines Datensatzes oder was auch immer ist. Es sollte NIEMALS mehr als ein Anruf erfolgen, um die Aktion auszulösen. Und der Versuch, Transaktionen über mehrere Anrufe hinweg wirksam einzusetzen, sollte wie die Pest gemieden werden.

Manchmal ist eine einzelne Aktion ziemlich komplex. Zum Beispiel das Abrufen von Daten, die aus mehreren Quellen kombiniert wurden: Auch dies sollte ein einzelner Aufruf sein. Entweder funktioniert das Ganze oder das Ganze scheitert.

Nun zu sagen, dass ein einzelner API-Aufruf nur eine DB-Abfrage ausführen sollte, ist ein bisschen schwachsinnig. Wie Sie bereits betont haben, ist der Aufwand für die Vermittlung des Anrufs über das Netzwerk im Hinblick auf die Gesamtzeit häufig um Größenordnungen höher.

Ich kann ihre Aussage einigermaßen verstehen, dass eine einzelne Abfrage schneller als mehrere ausgeführt werden kann; Dies gibt jedoch einen falschen Eindruck, da die gesamte Datenbank- und Netzwerklast ignoriert wird. Nur durch Profilerstellung der verschiedenen Methoden zum Abrufen von Daten aus der Datenbank können Sie das eigentliche Problem ermitteln. Ich bin sicher, jeder hat eine Geschichte, in der eine bestimmte Abfrage 100-mal häufiger als erwartet ausgeführt wurde und das System bis zur Einrichtung eines richtigen Index zum Erliegen gebracht hat ...

Letztendlich werden Sie sie nicht nur durch Gespräche überzeugen können. Richten Sie einen Testfall für beide Ansätze ein und profilieren Sie sie. Achten Sie auf die Gesamtzeit für die Erfassung der von Ihnen benötigten Daten, die Menge des generierten Netzwerkverkehrs, die Anzahl und den Zeitpunkt der Datenbankaufrufe usw. Nehmen Sie einen ganzheitlichen Ansatz - das heißt, Sie betrachten das gesamte System - und Sie sollten am Ende eine Menge davon haben Daten, um entweder Krähe zu essen oder ihnen den goldenen Weg zu zeigen.

Nicht ich
quelle