Zum Inhalt springen
EuroVPSEuroVPS

Ein ganzes Netzwerk von Websites wurde auf einmal langsam, und kein einziger Server war ausgelastet.

Ein Netzwerk von Stellenportalen fühlte sich auf einmal überlastet an. Jeder Server war im Leerlauf. Eine Städteabfrage machte mehr als die Hälfte der gesamten Datenbankzeit aus. So haben wir sie gefunden und mit einem einzigen Online-Index behoben.

Von EuroVPS EngineeringDer Maschinenraum

5 Min. Lesezeit
In diesem Artikel 8 Abschnitte
Kurz gesagt
  • Jede Website in einem Netzwerk von Stellenportalen wurde gleichzeitig langsam, während die Server im Leerlauf saßen, und der Instinkt war, größere zu kaufen.
  • Jede Ebene war im Leerlauf, außer einer: Die Load Balancer und die Webserver arbeiteten kaum. Die ganze Farm wartete auf die Datenbank, und die Datenbank wartete auf eine Abfrage.
  • Diese Abfrage, eine einfache Städteabfrage, war mehr als die Hälfte der gesamten Datenbankzeit und scannte bei jedem Aufruf eine ganze Tabelle, um eine einzige Zeile zurückzugeben.
  • Ein Online-Index, eingespielt ohne Unterbrechung der Websites, verwandelte den Scan in einen Seek. Keine größeren Server nötig.

Ein Netzwerk von Stellenportalen wurde zur selben Zeit weich. Nicht eine Seite, nicht eine Website: alle, zusammen, die Art farmweiter Nebel, die einen Betreiber annehmen lässt, die Server ertrinken und es sei Zeit, größere zu kaufen. Diese Annahme ist teuer, sie ist verbreitet, und in diesem Fall war sie falsch. Jede Maschine in der Farm langweilte sich. Die ganze Farm wartete auf die Datenbank, und die Datenbank wartete auf eine einzige Abfrage.

Jede Ebene war im Leerlauf, außer einer

Bei einer farmweiten Verlangsamung lesen Sie vor allem anderen die Auslastung jeder Ebene und sehen nach, welche tatsächlich am Anschlag ist. Die Load Balancer drehten Däumchen. Die Webserver langweilten sich. Nichts im Request-Pfad arbeitete hart genug, um eine langsame Website zu erklären.

DIE FARM, GEMESSEN
Load Balancer
im Leerlauf
Webserver
arbeiteten kaum
Datenbank
wartete auf eine Abfrage

Das ist das Zeichen, das die Kapazitätsdiskussion beendet, bevor sie beginnt. Wenn jede Ebene außer der Datenbank im Leerlauf ist, kauft Ihnen ein größerer Webserver eine schnellere Maschine im Leerlauf. Der Engpass war nie die Rechenleistung, und keine Hardwarebestellung hätte das behoben.

Die teure Antwort war, mehr Maschinen zu kaufen

Der InstinktDie Messung
Die ganze Farm ist langsam, also sind die Server überlastet. Kaufen Sie größere.Jede Ebene außer der Datenbank war im Leerlauf. Kapazität war nie der Engpass.
WAS DIE DATEN SAGTENEs muss ein Hardwareproblem sein.Die Hardware war gesund und nachweislich so: Load Balancer und Webserver arbeiteten kaum.
Verteilen Sie die Last auf mehr Maschinen.Eine Abfrage war mehr als die Hälfte der gesamten Datenbankzeit. Mehr Maschinen hätten sie öfter ausgeführt, weiterhin scannend.

Mehr Hardware hätte die Verschwendung skaliert, nicht beseitigt. Der einzige ehrliche nächste Schritt war herauszufinden, womit die Datenbank ihre Zeit tatsächlich verbrachte.

Wir haben die Abfragehistorie nach Fingerabdruck aggregiert

Eine Anwendung schreibt dieselbe Abfrage selten zweimal gleich. Dieselbe Abfrage taucht auf mehrere verschiedene Arten parametrisiert auf, sodass das Zählen des rohen Abfragetexts den Übeltäter im Rauschen versteckt. Die Lösung ist, nach einem Fingerabdruck zu aggregieren, der diese Inkonsistenz überlebt, und dann jedes Statement nach seinem Anteil an der gesamten Datenbankzeit zu ordnen. Wir haben das über die vollständige Abfragehistorie laufen lassen.

AGGREGATE BY FINGERPRINT, RANK BY SHARE OF DB TIME
-- reconstructed, de-identified rendering of cleared facts. not a real capture.
-- full query history, grouped by query_hash
-- (a fingerprint that survives inconsistent parameterisation)
SELECT TOP 5
  query_hash,
  SUM(execution_count)                AS calls,
  SUM(total_logical_reads)            AS pages_read,
  SUM(total_elapsed_time) * 100.0
    / SUM(SUM(total_elapsed_time)) OVER () AS pct_of_db_time
FROM   sys.dm_exec_query_stats
GROUP  BY query_hash
ORDER  BY pct_of_db_time DESC;

Eine Zeile stach heraus: mehr als die Hälfte von allem, was die Datenbank tat, für ein einziges Statement aufgewendet. Die Methode ist absichtlich langweilig, sie rät nicht, und sie ist jedes Mal dieselbe.

Eine Abfrage war mehr als die Hälfte der Datenbank

Das Statement, das herausfiel, ist von der Sorte, die Sie nie ein zweites Mal ansehen würden. Eine Städteabfrage, zwei Spalten, ein Filter.

THE QUERY AT THE TOP OF THE LIST
-- reconstructed, de-identified. table/column names renamed from the real schema.
SELECT CityID, CityName
FROM   Cities
WHERE  RoutingPath = @p;

-- Execution plan: Clustered Index Scan (a full scan)
--   Returns:          ONE row
--   Filtered column:  RoutingPath --> no index

Die Spalte, nach der sie filterte, hatte keinen Index. Also scannte jeder Aufruf die ganze Tabelle, um eine einzige Zeile zurückzugeben. Für sich allein ist das ein kleiner Preis. Wiederholt bei jedem Seitenaufruf im ganzen Netzwerk wurde es zum größten Posten, den die Datenbank hatte.

Eine Antwort aus einer Zeile sollte ein paar Seiten lesen, nicht die ganze Tabelle

“Eine Antwort aus einer Zeile sollte zwei oder drei Seiten lesen, nicht Hunderte.”

DIE ZEILE, DIE DIE DIAGNOSE ZUSAMMENFASST

Dieser Satz ist die ganze Diagnose, komprimiert auf etwas, das Sie der Person sagen können, der der Code gehört. Nichts anderes lässt eine Abfrage mit einer Ergebniszeile so viel lesen. Keine langsame Platte, keine ausgelastete CPU, keine schlechte Nacht. Eine Antwort aus einer Zeile, die die ganze Tabelle liest, ist ein Full Scan, und ein Full Scan auf einer gefilterten Spalte ist eine unverwechselbare Signatur: Die Spalte braucht einen Index. Wir mussten nicht über Hardware streiten, wir haben einfach die Reads gezeigt.

Ein Online-Index, vom Scan zum Seek

JEDER AUFRUF, VOR DEM INDEX
  • Ein Full Scan der Tabelle, für eine Zeile
  • Kein Index auf der gefilterten Spalte
  • Mehr als die Hälfte der gesamten Datenbankzeit
JEDER AUFRUF, NACH EINEM INDEX
  • Ein Index Seek, ein paar Seiten für dieselbe Zeile
  • Ein kleiner Bruchteil der Arbeit pro Aufruf
  • Der Index ging online rein, ohne Unterbrechung der Websites
ONE ONLINE INDEX ON THE COLUMN THE APP ACTUALLY FILTERS ON
-- reconstructed, de-identified. index name illustrative, on the renamed identifiers.
-- Index the column in the WHERE clause; cover the two columns it returns
CREATE NONCLUSTERED INDEX IX_Cities_RoutingPath
  ON Cities (RoutingPath)
  INCLUDE (CityID, CityName)
  WITH (ONLINE = ON);   -- applied without interrupting the site

-- New execution plan: Index Seek

Ein Index, auf der einen Spalte, nach der die Anwendung tatsächlich filterte. Er wurde online eingespielt, ohne Unterbrechung der Websites, und die Abfrage, die die Datenbank beherrscht hatte, wurde zu einer der günstigsten Dinge, die sie tat.

Der Ingenieur des Kunden ergänzte die Diagnose

Das Beste an dieser Zusammenarbeit war nicht unser Index. Es war das, was geschah, als der eigene Datenbankingenieur des Kunden dazukam und die Diagnose ergänzte: Er brachte ein Reader/Writer-Blocking-Muster von seiner Seite des Systems mit. Wir haben seine Lösung mit unserer zusammengeführt, und beide gingen gemeinsam rein.

“Der Ingenieur des Kunden verließ das Gespräch als Mitstreiter.”

WIE DIESE GESPRÄCHE ENDEN SOLLTEN

Das ist der Unterschied zwischen einem Support-Ticket und einer Diagnose. Wenn der Befund wahr ist und Sie die Belege zeigen, hören beide Seiten auf, Positionen zu verteidigen, und beginnen, die Plattform gemeinsam zu verbessern.

Wenn jede Ebene im Leerlauf ist, außer einer, hören Sie auf, auf die Kapazität zu schauen

Die Lektion ist kurz, und sie ist das Gegenteil des Reflexes. Farmweite Langsamkeit bei Maschinen im Leerlauf ist kein Kapazitätsproblem, sondern eine Softwarefrage im Kapazitätskostüm. Das prüfen Sie, bevor Sie eine Hardwarebestellung unterschreiben.

01

Lesen Sie zuerst die Auslastung jeder Ebene.

Load Balancer, Web, Datenbank. Wenn alles im Leerlauf ist, außer einer Ebene, ist Kapazität nicht Ihr Problem, und ein größerer Server wird es nicht beheben.

02

Aggregieren Sie die Abfragestatistiken nach Fingerabdruck.

Gruppieren Sie Ihre Historie nach einem Hash, der inkonsistente Parametrisierung überlebt, damit dasselbe Statement in mehreren Schreibweisen als eines zählt. Der schlimmste Übeltäter sticht meist mit großem Abstand heraus.

03

Vergleichen Sie gelesene Seiten mit zurückgegebenen Zeilen.

Eine Antwort aus einer Zeile, die Hunderte Seiten liest, scannt, statt zu suchen. Diese Lücke ist die Signatur eines fehlenden Index.

04

Indexieren Sie die Spalte, nach der die Anwendung tatsächlich filtert.

Nicht jede Spalte. Die in der WHERE-Klausel, die millionenfach läuft. Spielen Sie ihn online ein, ohne Unterbrechung der Website.

05

Cachen Sie die winzigen heißen Tabellen.

Kleine, selten veränderte Nachschlagetabellen gehören in den Cache, nicht in den kritischen Pfad der Datenbank bei jedem Seitenaufruf.

Die ganze Zeit über blieb die Maschine ruhig. Die Load Balancer drehten Däumchen, die Webserver saßen gelangweilt herum, und die Lösung war ein Index auf der Spalte, nach der die Anwendung filterte. Der Datenbankserver war gesund. Ein Statement in ihm brauchte einen Index.

Klare Antworten

Wie kann eine Abfrage mehr als die Hälfte der Last einer Datenbank sein?+

Wenn eine Abfrage oft genug läuft und jedes Mal zu viel Arbeit macht, dominiert sie alles andere. Diese lief bei jedem Seitenaufruf und scannte bei jedem Aufruf eine ganze Tabelle, um eine einzige Zeile zurückzugeben, weil die Spalte, nach der sie filterte, keinen Index hatte. Multiplizieren Sie eine kleine Verschwendung mit Millionen Aufrufen, und sie wird zur ganzen Datenbank.

Warum waren die Server im Leerlauf, wenn die Websites langsam waren?+

Weil der Engpass in der Datenbank lag, die auf eine Abfrage wartete. Die Load Balancer und Webserver arbeiteten kaum. Die ganze Farm wartete auf die Datenbank, und die Datenbank wartete auf eine Abfrage, die sofort hätte antworten müssen. Mehr Server hätten nur mehr Maschinen im Leerlauf gebracht.

Wie finden Sie die Abfrage, die tatsächlich das Problem ist?+

Sie aggregieren die vollständige Abfragehistorie nach einem Fingerabdruck, der inkonsistente Parametrisierung überlebt, und ordnen dann nach Anteil an der gesamten Datenbankzeit. Die Methode ist langweilig, und sie rät nicht.

Was ist die Signatur eines fehlenden Index?+

Eine Antwort aus einer Zeile, die Hunderte Seiten liest. Wenn eine Abfrage eine Zeile zurückgibt und der Plan einen Scan über die ganze Tabelle zeigt, ist die Spalte, nach der sie filtert, nicht indexiert.

Brauchte die Behebung eine Ausfallzeit?+

Nein. Der Index ging online rein, eingespielt ohne Unterbrechung der Websites, und verwandelte den Scan in einen Seek.

Wer hat es behoben, Sie oder der Kunde?+

Beide, gemeinsam. Wir haben die Abfrage gefunden und den Index hinzugefügt. Der eigene Datenbankingenieur des Kunden kam zur Diagnose dazu und brachte ein Reader/Writer-Blocking-Muster von seiner Seite mit. Wir haben seine Lösung mit unserer zusammengeführt.

Langsame Website, Server im Leerlauf? Wir finden die eigentliche Ursache.

Sprechen Sie mit einem Ingenieur, der Query Plans liest, nicht nur Dashboards, darüber, was Ihre Plattform tatsächlich ausbremst.

Mit einem Ingenieur sprechen

Der Instinkt bei einer langsamen Farm ist, sich freizukaufen. Die ehrliche Antwort ist fast immer günstiger: Lesen Sie jede Ebene, finden Sie die, die am Anschlag ist, und lesen Sie ihre Abfragen.

Sie haben das Ende erreicht.

Setzen Sie das Gelesene um

Was betreiben Sie?

Erzählen Sie uns von Workload, Traffic und Anforderungen an die Wiederherstellung. Beginnen Sie mit dem Server-Konfigurator oder besprechen Sie die Details mit einem Ingenieur.

Geschrieben von den Ingenieuren, die die beschriebene Infrastruktur bauen und betreiben.
Mit einem Ingenieur sprechen
XIn

Noch 5 Min.