Was qualifiziert zu viele Datenbankanforderungen im Code?

17

Dies ist eine Diskussion, die ich selbst und einige meiner Kollegen führen, und ich denke, ich komme hierher, um zu sehen, was passiert, wenn ein allgemeiner Konsens darüber besteht.

Grundsätzlich ergeben sich die folgenden 2 Meinungen zu Datenbankaufrufen: 1. Führen Sie einen großen Aufruf durch, um alles zu erhalten, was zur Reduzierung der Anzahl der Datenbankaufrufe erforderlich ist. 2. Führen Sie kleinere separate Aufrufe aus, je nachdem, was zur Reduzierung der Größe erforderlich ist DB ruft auf

Wo dies besonders ins Spiel kommt, ist allgemeiner Code. Wir werden das Beispiel einer Employee-Klasse verwenden, da dies ziemlich einfach ist.

Angenommen, Ihre Employee-Klasse verfügt über 10 Werteattribute (Vorname, Nachname, Angestellter usw.) und dann über 2 Klassenattribute. 1 verweist auf eine Department-Klasse und 1 Supervisor, der auf ein anderes Employee-Objekt verweist.

In Mindset 1 würden Sie einen Anruf tätigen, der die Mitarbeiterdaten sowie die Felder zurückgibt, die zum Auffüllen der Abteilungs- und Vorgesetztenattribute erforderlich sind, oder zumindest die Felder, die am häufigsten von diesen Unterobjekten verwendet werden.

In Mindset 2 sollten Sie zuerst nur das Employee-Objekt und dann nur die Department- und Supervisor-Objekte auffüllen, wenn sie tatsächlich angefordert werden.

Die Haltung von 2 ist ziemlich einfach ... minimieren Sie die Größe der Anforderungen und die Anzahl der Datenbankobjekte, die bei jeder Anforderung getroffen werden müssen. # 1 ist der Meinung, dass selbst wenn es richtig implementiert werden könnte, die bloße Tatsache, dass der Code mehrere Verbindungen herstellen müsste, die Verbindung zwischen dem Webserver und der Datenbank mehr belasten wird, als sie zu reduzieren.

Die treibende Kraft hinter dieser Untersuchung ist, dass der Datenverkehr zwischen unserem Webserver und dem Datenbankserver außer Kontrolle gerät.

user107775
quelle
7
Nach meiner Erfahrung gibt es keine "richtige Antwort" darauf. Es gibt ein Gleichgewicht zwischen Latenz und Durchsatz. Eine niedrige Latenz kann viele kleine oder sogar große Anforderungen tolerieren. Verbindungen mit hoher Latenz sind jedoch in der Regel besser für das gleichzeitige Verschieben vieler Daten geeignet. Wenn der Durchsatz in einer Konfiguration mit hoher Latenzzeit niedrig ist, sollten Sie jedoch kleinere Datenblöcke abrufen, um eine schnellere Reaktion zu erzielen.
3
Wahrscheinlich im Zusammenhang mit n + 1 Problem stackoverflow.com/questions/97197/…
Valera Kolupaev
@Valera: Der Einfachheit halber finden Sie hier den Link zu dieser Frage: realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=n1selects
rwong
4
"Der Datenverkehr zwischen unserem Webserver und dem Datenbankserver gerät außer Kontrolle." Was bedeutet das? Können Sie genau sagen, worin das eigentliche Problem besteht? Haben Sie Leistungsprobleme? Haben Sie profiliert und gemessen? Bitte geben Sie die tatsächlichen Ergebnisse der tatsächlichen Messungen als Teil der Frage an. Ansonsten raten wir nur.
S.Lott

Antworten:

8

Wenn die treibende Kraft hinter dieser Frage zu viel Verkehr ist, haben Sie sich mit dem Zwischenspeichern häufig verwendeter Objekte befasst? Beispiel: Nachdem Sie die Objekte "Employee", "Department" und "Supervisor" erhalten haben, ist es möglicherweise eine gute Idee, ihnen einen Cache hinzuzufügen, damit sie sich bereits im Cache befinden und nicht abgerufen werden müssen, wenn sie in naher Zukunft erneut angefordert werden nochmal. Natürlich muss der Cache selten verwendete Objekte auslaufen lassen und Objekte entfernen können, die von der Anwendung geändert und wieder in der Datenbank gespeichert wurden.

Je nachdem, welche Sprache und Frameworks Sie verwenden, gibt es möglicherweise bereits ein Caching-Framework, das einige (oder die meisten) der von Ihnen benötigten Funktionen ausführen kann. Wenn Sie Java verwenden, können Sie in den Apache Commons-Cache schauen (ich habe ihn eine Weile nicht mehr verwendet, und obwohl er ruhend aussieht, ist er immer noch verfügbar und beim letzten Mal ziemlich anständig).

FrustratedWithFormsDesigner
quelle
3

Achten Sie immer auf Lesbarkeit und Klarheit, wenn Sie zum ersten Mal etwas schreiben. Sie können dann bei Bedarf umgestalten. Führen Sie Lasttests durch, um die Engpässe zu ermitteln. In vielen Fällen ist dies nicht die Anzahl der Anrufe, die das Problem verursachen, sondern die Anzahl der schlecht geschriebenen Anrufe.

Was als zu viele klassifiziert wird, hängt von der Anwendung ab. Für die meisten Webanwendungen ist alles unter 30 Sekunden annehmbar. Ich würde mit Ihren Nutzern über deren Erwartungen sprechen.

Tom Squires
quelle
Was ist ein schlecht geschriebener DB-Anruf?
Nu Everest
3

Ihre Frage scheint auf der Annahme zu beruhen, dass Sie raten müssen, welche Daten für eine bestimmte Seite benötigt werden. Das ist nicht der Fall. Es ist nicht so einfach wie der naive Ansatz, aber Sie können Ihren Code so gestalten, dass Sie wissen, ob Sie Abteilungs- oder Supervisor-Attribute benötigen, bevor Sie Datenbankaufrufe durchführen.

Karl Bielefeldt
quelle
3

Dies sind die Regeln, die ich verwende. Vielleicht helfen sie Ihnen.

  1. Zuerst messen! Ich werde mir nicht einmal den Code ansehen, der "langsam sein könnte", es sei denn, ich sehe tatsächlich, dass Verkehr zu dieser Ressource fließt und diese Ressource langsam reagiert.
  2. 1 Anfrage = K Abfragen. Die Häufigkeit, mit der ich mit der Datenbank spreche, hängt vollständig von der Art der angeforderten Ressource ab. und niemals aufgrund der Art der Anforderung oder des Zustands dieser Ressource; In Ihrem Beispiel sind das wahrscheinlich höchstens 3 Abfragen: 1 für Mitarbeiter, 1 für Abteilungen und 1 für Vorgesetzte; Es ist egal, wie viele von jedem es gibt.
  3. Fragen Sie nicht ab, was Sie nicht verwenden möchten . Wenn es sich um HTTP handelt, hat es keinen Sinn, Daten für später abzufragen. es gibt kein späteres; Jede Anfrage beginnt mit einer leeren Tafel. Manchmal brauche ich die meisten Spalten aus einer Tabelle, aber gelegentlich brauche ich nur eine oder zwei; Wenn ich genau weiß, welche Felder ich brauche, werde ich genau danach fragen.
  4. Wirf Hardware auf das Problem. Server sind billig; Manchmal können Sie genug Leistung erzielen, indem Sie die Datenbank in eine stärkere Box verschieben. oder einige Abfragen an ein schreibgeschütztes Replikat senden.
  5. Machen Sie zuerst den Cache ungültig und implementieren Sie dann das Caching. Der Drang, häufig verwendete oder schwer abfragbare Daten in einen Cache zu stellen, ist groß. Aber allzu oft wird übersehen, dass nicht verwendete Daten gelöscht werden oder abgelaufene Daten abgelaufen sind. Wenn Sie wissen, wie Sie Daten aus dem Cache entfernen können; Dann können Sie es sicher in den Cache stellen. Wenn sich herausstellt, dass es teurer ist, den Cache ungültig zu machen, als nur die Abfrage durchzuführen; Dann brauchten Sie keinen Cache.
SingleNegationElimination
quelle
2

Beide Strategien sind hier vollkommen gültig. Jeder hat Vor- und Nachteile:

Ein Aufruf für alle 3 Objekte:

  • wird schneller durchführen
  • Sie bekommen genau das, was Sie brauchen, wenn Sie es brauchen
  • wird wahrscheinlich nur in einem Fall verwendbar sein (es kann jedoch ein sehr häufiger Fall sein)
  • wird schwieriger zu pflegen sein
  • muss öfter gepflegt werden (da es sich ändert, wenn sich eines der 3 Objekte oder die benötigten Daten ändern)

Ein Anruf pro Objekt (3 Anrufe insgesamt)

  • Ruft Sie allgemein auf, um eine einzelne Instanz jedes Objekttyps zu füllen. Sie können dann unabhängig voneinander verwendet werden
  • Ist wartungsfreundlicher, da die Abfragestruktur einfacher wird.
  • Wird langsamer sein (nicht unbedingt dreimal so langsam, aber der Overhead wird für die gleichen Daten erhöht)
  • Kann Probleme beim Abrufen nicht benötigter Daten verursachen (das Abrufen des gesamten Datensatzes, wenn Sie ein Feld benötigen, ist verschwenderisch)
  • Kann zu N + 1-Problemen führen, wenn eine Viele-zu-Eins-Beziehung besteht, wenn die Einzeldatensatzabfrage N-mal gesendet wird, und zwar einmal pro Datensatz in der Sammlung.
KeithS
quelle
Als Reaktion auf einige Ihrer Bedenken (Nr. 3 und 5 in der zweiten Liste) ... Was ist, wenn Vorgesetzter und Abteilung nur 1/3 (oder weniger) der Zeit verwendet werden? Was wäre, wenn der Code so konzipiert wäre, dass alle untergeordneten Objekte abgerufen werden, sobald das List <> -Objekt, das sie enthält, zum ersten Mal referenziert wurde? ... würde das den größten Teil der Vorsicht lindern?
user107775
Wenn die zusätzlichen Objekte nur selten benötigt werden, ist dies im Allgemeinen schneller (weniger abzurufende Daten), im schlimmsten Fall jedoch langsamer (gleiche oder mehr Daten werden abgerufen, wenn der dreifache Kommunikationsaufwand von Ihrem Computer verwendet wird). Was das N + 1-Problem betrifft, müssen Sie lediglich in der Lage sein, die Abfrage zu erstellen, die eine Liste von Objekten abruft, um dies auf der Grundlage des Fremdschlüssels auf der "einen" Seite der Beziehung zu tun, und dann mehrere Zeilen zu ziehen aus dem Abfrageergebnis. Sie können keine Version der Abfrage verwenden, für die der Primärschlüssel des Datensatzes erforderlich ist.
KeithS
1

Zu viele DB-Anforderungen stellen für mich mehr Anforderungen, als Sie zum Laden der jeweils benötigten Daten benötigen.

Wenn Sie die Daten nicht benötigen, verschwenden Sie keinen Speicher, um eine spätere zweite Auslösung zu vermeiden. Wenn Sie jedoch die Datenmenge benötigen, sollten Sie die Aufrufe der Datenbank minimieren.

Also haben Sie beide Möglichkeiten und nutzen Sie jede, wo es die Situation erfordert.

EDIT: Denken Sie daran, dass dies natürlich auch von Ihrer Situation abhängt. Wenn es sich zum Beispiel um eine WebApp handelt, sollten Sie andere Überlegungen anstellen, als wenn es sich um eine Desktop-App handelt, die auf die Datenbank in Ihrem Netzwerk zugreift, und nicht über das Internet für die WepApp.

AJC
quelle
Was ist mit dem Fall, dass Sie allgemeinen Code schreiben und nicht sicher sind, wie Ihr Code verwendet wird? Vielleicht würden Sie sich nie vorstellen, dass jemand den Supervisor nicht benötigt, aber es stellt sich heraus, dass die Anwendung, an der Sie arbeiten, die einzige ist, die ihn benötigt. Sicher, Sie könnten separate Funktionen schreiben ... eine, um sie nicht einzuschließen, und eine, um sie einzuschließen, aber ab wann erfordert Ihr allgemeiner Code zu viel Detailwissen, um ihn zu verwenden?
user107775
@ user107775 Ich schreibe normalerweise nur zwei Funktionen für jeden Fall; Eine, die nur die Eigenschaftswerte zurückgibt, und eine, die die Klasse mit allen zugehörigen Klassen zurückgibt. Dies liegt daran, dass Sie meistens nur die Eigenschaften benötigen. Auf diese Weise brauchen Sie keine Detailkenntnisse, nur das eine bekommt die Grundlagen und das andere alles. Ich finde es eine vernünftige Balance. (In einigen Fällen ist jedoch eine weitere Optimierung erforderlich, dies ist jedoch von Fall zu Fall unterschiedlich.)
AJC
1

Stellen Sie eine Verbindung zur Datenbank her, senden Sie eine Anfrage und lassen Sie sie analysieren. Dies dauert in der Regel erheblich länger als das Abrufen von Ergebnissen. Daher besteht der allgemeine Trend darin, so viele Abfragen wie möglich in einer Anfrage zusammenzufassen.

Wenn Sie dies alles auf einmal tun, ist der Code nicht mehr zu warten. Stattdessen wird dies normalerweise durch eine zusätzliche Abstraktionsschicht erreicht: Der Code plant mehrere Anforderungen nach Bedarf, dann analysiert die Engine diese als eine große Anforderung (möglicherweise unter Verwendung des Caches unterwegs) und sendet die Antworten nach Bedarf.

Natürlich können nicht immer alle in einer Abfrage abgerufen werden - häufig wird eine Abfrage erstellt, die die für die Erstellung der nächsten Abfrage erforderlichen Daten enthält, sodass Sie diese wiederholen müssen. Immer noch mehrere Anfragen gleichzeitig zu bearbeiten, ist besser als hunderte kleine Schüsse in die Datenbank.

Planen Sie also, was Sie benötigen, fordern Sie es an und rufen Sie es ab. Wenn mehr erforderlich ist, fordern Sie es an und rufen Sie es erneut ab, und verwenden Sie die Daten dann zum Generieren von Inhalten. Vermeiden Sie auf jeden Fall Datenbankanforderungen wie die Initialisierung lokaler Variablen, die über den gesamten Code verteilt sind.

SF.
quelle
1

Wir wissen nicht genug über Ihre Anwendung, um zu wissen, welche Auswahl Sie zu früh optimieren müssen. Wie oft werden die Supervisor-Daten verwendet? Scheint als könnte es eine Verschwendung sein, aber wir wissen es nicht. Wenn Sie sie getrennt halten, können Sie möglicherweise Ihr System überwachen, um festzustellen, wie oft sie gemeinsam verwendet werden. Dann können Sie die Entscheidung treffen, sie einfach in einem Anruf zu kombinieren. Ansonsten, wenn Sie mit diesem einen großen Aufruf beginnen, einen Flaschenhals zu kreieren, wo fangen Sie an, Probleme zu beheben? Schwer zu identifizieren, was Sinn macht, wegzulassen. Weitere Datenfelder können zu diesem Prozess hinzugefügt werden.

Es wäre interessant zu wissen, wie viel davon von db memory vs disk kommt. Ich habe nicht das Gefühl, dass sich die Abteilung im Vergleich zur Adresse mehr oder weniger wahrscheinlich ändert.

JeffO
quelle