Java >> Java Tutorial >  >> Java

3 Katastrophen, die ich mit JProfiler gelöst habe

Ich habe ein Toolkit, das mir hilft, meine Arbeit zu erledigen. Es ist nicht einfach, sich einen Platz in meinem Werkzeugkasten zu verdienen, weil ich möchte, dass meine Werkzeuge mein Leben einfacher machen.

Das bedeutet, dass sie einfach zu bedienen (oder leicht zu erlernen) sein müssen. Leider ist die Welt voll von nützlichen Entwicklungstools, die nicht einfach zu verwenden oder zu erlernen sind.

Zu meinem Glück gibt es Ausnahmen von dieser Regel, und heute werde ich eine solche Ausnahme nennen.

JProfiler ist seit vielen Jahren mein vertrauenswürdiger Freund und Verbündeter. Ich mag die Benutzeroberfläche, die gut aussieht und (meiner Meinung nach) recht einfach zu bedienen ist. Aber was noch wichtiger ist, ich mag JProfiler, weil es in diesen Jahren viele Male meine Haut gerettet hat.

Dieser Blogbeitrag beschreibt drei Katastrophen, die ich mit JProfiler gelöst habe. Fangen wir an.

Haftungsausschluss:Dies ist ein gesponserter Beitrag, aber ich empfehle nur Produkte, die ich selbst verwende. Außerdem wurde dieser Beitrag von mir geschrieben und all diese Szenarien sind real. Einige Details wurden zum Schutz der beteiligten Personen geändert.

1. Das schreckliche N+1-SELECT-Problem

Ich wurde gebeten, einen Blick auf eine Suchfunktion zu werfen, die sehr langsam war. Ich begann mit einem kurzen Blick auf den Code und stellte fest, dass er mithilfe von Hibernate eine Liste von Entitäten aus der Datenbank holte, diese Entitäten in DTOs konvertierte und die Suchergebnisse zurückgab.

Da ich bemerkte, dass diese Entitäten viele Eins-zu-Eins-Beziehungen hatten und alle eifrig geladen wurden, habe ich Hibernate so konfiguriert, dass die aufgerufenen SQL-Anweisungen in das Protokoll geschrieben werden, und die Suchfunktion getestet. Das Ergebnis meines Tests war schockierend. Hibernate hat so viele SQL-Anweisungen aufgerufen, dass es offensichtlich war, dass diese Suchfunktion unter dem N+1-SELECT-Problem litt.

Bevor ich mit der Behebung dieses Problems begann, wollte ich wissen, welche Informationen aus der Datenbank abgerufen werden und wie lange es dauert, diese Informationen abzurufen. Auch hier war der einfachste Weg, diese Informationen zu erhalten, die Verwendung von JProfiler. JProfiler hat eine eingebaute JPA/Hibernate-Sonde, die mir die Informationen geben kann, die ich brauche.

Nachdem ich die Daten erhalten hatte, habe ich das Problem behoben, indem ich die Kombination aus Lazy Fetching und Joins verwendet habe (diese Funktion wurde später durch eine Implementierung ersetzt, die SQL verwendete). Außerdem ist es wichtig zu verstehen, dass Sie beim Beheben eines solchen Problems viele inkrementelle Änderungen vornehmen und Ihren Code nach jeder Änderung profilieren müssen. Auf diese Weise können Sie sicherstellen, dass Sie die Situation nicht verschlimmern.

2. Das „unerwartete“ Speicherproblem

Ein Projekt hatte einen Batch-Job, der viele Entitäten innerhalb einer einzigen Transaktion verarbeitete. Das Problem war, dass der Batch-Job sehr langsam war und zu viel Speicher beanspruchte. Tatsächlich stürzte es manchmal ab, weil der JVM der Speicherplatz ausging.

Als ich anfing, dieses Problem zu untersuchen, wusste ich, was falsch war. Das Problem war, dass der Batch-Job zu viele Entitäten innerhalb einer einzigen Transaktion aktualisierte, und da wir Hibernate verwendet haben, musste Hibernate:

  • Behalten Sie all diese Änderungen im Auge.
  • Diese Änderungen in der Datenbank beibehalten, wenn die Transaktion festgeschrieben wurde.

Es war offensichtlich, dass ich das Problem beheben konnte, indem ich den Batch-Job so modifizierte, dass viele kleine Transaktionen verwendet wurden. Ich wusste jedoch nicht, wie kleine Transaktionen ich verwenden sollte. Da ich eine faktenbasierte Entscheidung treffen wollte, musste ich verschiedene Transaktionsgrößen testen und messen, wie viel Speicher der Batch-Job benötigt.

Ich startete JProfiler und suchte mithilfe der VM-Heap-Telemetrieansicht nach der „besten“ Transaktionsgröße. Es hat einige Zeit gedauert, aber ich konnte die Transaktionsgröße auswählen, die das Problem (zumindest vorerst) löste.

3. Die langsame Find-by-ID-Methode

Ich hatte eine einfache Dienstmethode implementiert, die die Informationen einer Entität aus der Datenbank abholte, indem sie ihre ID als Suchkriterium verwendete. Das Problem war, dass diese Methode extrem langsam war, und ich konnte nicht verstehen, warum.

Die Entität hatte keine Beziehungen, die das N+1-Auswahlproblem hätten verursachen können, und die aufgerufene Abfrage war so einfach, dass sie sehr schnell hätte sein sollen. Es war an der Zeit, JProfiler zu starten.

Bei der Untersuchung dieses Problems bin ich folgendermaßen vorgegangen:

  1. Ich habe das JPA/Hibernate-Probe verwendet und herausgefunden, dass das Aufrufen der Datenbankabfrage nur wenige Millisekunden dauerte.
  2. Ich habe die langsamen Methodenaufrufe durchsucht und den Übeltäter identifiziert. Die betreffende Entität hatte 6 DateTime Felder, und ich habe sie mit Jadira UserTypes in der Datenbank gespeichert. Das Problem war das Konvertieren der Spaltenwerte von timestamp Spalten in DateTime Objekte haben zu lange gedauert. Wenn ich mich richtig erinnere, dauerte das Erstellen dieser Objekte 0,6 Sekunden (ich weiß nicht 100 % über die genaue Zeit).

Ich habe dieses Problem behoben, indem ich DateTime ersetzt habe Felder mit Datum -Felder und Modifizieren der Getter-Methoden, um neue DateTime zurückzugeben Objekte. Dies war eine einfache Lösung, aber ohne JProfiler hätte ich wahrscheinlich Hibernate für dieses Problem verantwortlich gemacht, es als "Feature" bezeichnet und weitergemacht.

Die Moral dieser Geschichte

Donald Knuth hat einmal geschrieben, dass voreilige Optimierung die Wurzel allen Übels ist. Obwohl ich ihm zustimme, denke ich auch, dass es sehr einfach ist, dieses Zitat als Entschuldigung dafür zu verwenden, unsere Arbeit nicht zu tun.

Es ist überraschend einfach, "sauberen Code" zu schreiben, der schnell aussieht, wenn er in der Entwicklungsumgebung ausgeführt wird. Wenn wir glauben, dass vorzeitige Optimierung wirklich die Wurzel allen Übels ist, stehen die Chancen gut, dass wir unseren Code der Versionskontrolle unterwerfen und mit unserer nächsten Aufgabe fortfahren.

Wenn unser Code Leistungsprobleme hat, wenn er in der Produktionsumgebung ausgeführt wird, lösen wir dieses Problem, indem wir angeben, dass sie durch technische Schulden verursacht werden. Wir wollen uns dieses Problem nicht näher ansehen, da wir wissen, dass die Behebung viel Zeit in Anspruch nehmen wird. Also machen wir es uns einfach.

Diese Art von Verhalten ist jedoch unprofessionell und ehrlich gesagt handeln nur Idioten so, weil wir einen einfachen Weg haben, um sicherzustellen, dass unser Code fehlerfrei ist:

Wir sollten einen Profiler starten und uns unseren Code genau ansehen, BEVOR wir ihn dem Versionskontrollsystem übergeben .

Stimmen Sie mir zu?


Java-Tag