2010-04-13 7 views
5

Wir haben eine Webanwendung, die in einer Produktionsumgebung ausgeführt wird und irgendwann klagte der Client darüber, wie langsam die Anwendung wurde.Wie vermeidet man diese sehr schwere Abfrage, die die Anwendung verlangsamt?

Wenn wir überprüft, was mit der Anwendung vorging und die Datenbank, die wir dieses „wertvolle“ Abfrage entdecken, die von mehreren Benutzern gleichzeitig ausgeführt wurde (also eine extrem hohe Last auf dem Datenbankserver zuzufügen):

SELECT NULL AS table_cat, 
     o.owner AS table_schem, 
     o.object_name AS table_name, 
     o.object_type AS table_type, 
     NULL AS remarks 
FROM  all_objects o 
WHERE o.owner LIKE :1 ESCAPE :"SYS_B_0" AND 
     o.object_name LIKE :2 ESCAPE :"SYS_B_1" AND 
     o.object_type IN(:"SYS_B_2", :"SYS_B_3") 
ORDER BY table_type, table_schem, table_name 

Unsere Anwendung führt diese Abfrage nicht aus, ich glaube, es ist eine interne Hibernate-Abfrage. Ich habe wenig Informationen darüber gefunden, warum Hibernate diese extrem schwere Abfrage durchführt, also jede Hilfe, wie man sie vermeidet, wird sehr geschätzt!

Informationen zur Produktionsumgebung: Red Hat Enterprise Linux 5.3 (Tikanga), JDK 1.5, Webcontainer OC4J (mit Oracle Application Server), Oracle Database 10.1.0.4, JDBC-Treiber für JDK 1.2 und 1.3, Hibernate Version 3.2.6 .ga, Verbindungspool-Bibliothek C3P0 Version 0.9.1.

UPDATE: Dank @BalusC für claryfing, dass in der Tat es Hibernate ist, dass die Abfrage ausgeführt wird, jetzt habe ich eine bessere Vorstellung davon, was vor sich geht. Ich werde erklären, wie wir die Hibernate-Sitzung behandeln (es ist sehr rudimentär ja, wenn Sie Vorschläge haben, wie man besser damit umgehen kann!)

Wir haben einen Filter (implementiert javax.servlet.Filter) Wenn es gestartet wird (init-Methode), baut es die Sitzungs-Factory auf (dies geschieht nur einmal). Dann geht jeder HttpRequest, der an die Anwendung geht, durch den Filter und erhält eine neue Sitzung und startet eine Transaktion. Wenn der Prozess vorbei ist, kommt es durch den Filter zurück, macht das Commit der Transaktion, tötet die Hibernate-Sitzung, dann weiter auf der Forward-Seite (wir speichern die Hibernate-Sitzung in der HTTP-Sitzung nicht, weil es nie funktioniert hat gut in unseren Tests).

Jetzt kommt hier der Teil, wo ich denke, das Problem ist. In unserer Entwicklungsumgebung stellen wir unsere Apps in Tomcat 5.5 bereit, und wenn wir den Dienst starten, starten alle Filter sofort und nur einmal. In der Produktionsumgebung mit OC4J scheint das nicht so zu funktionieren. Wir stellen die Anwendung bereit und nur wenn die erste Anfrage eintrifft, instanziiert OC4J die Filter.

Dies führt mich zu glauben, dass OC4J der Filter auf jeden Anfrage (oder zumindest mehrmals, die immer noch falsch ist), eine Sitzung ab Werk auf jede Anfrage so instanziiert zu schaffen, wich führt, dass% &% #% $ # Abfrage, die zu meinem Problem führt!

Nun, ist das korrekt? Gibt es eine Möglichkeit für mich, das OC4J so zu konfigurieren, dass Filter nur einmal instanziiert werden?

Vielen Dank an euch alle, dass ihr euch die Zeit genommen habt, darauf zu antworten!

+0

Haben Sie daran gedacht, die App so zu modifizieren, dass das Ergebnis dieser Abfrage zwischengespeichert wird? Das Ergebnis davon scheinen statische Daten zu sein, es sei denn, Sie erstellen ein DB-Verwaltungstool. –

+0

Verwenden Sie auch den richtigen Hibernate-Dialekt und die richtigen JDBC-Oracle-Treiber? – aperkins

+0

Ich bin sicher, wir verwenden den richtigen Dialekt, nicht so sehr auf den Treiber (aber es gab nie Probleme vorher). Und die Abfrage ist nicht Teil der Anwendung, es muss eine Ruhezustand, c3p0 oder Treiberabfrage sein, wir sind nicht sicher, wer es ausführt. Mein erster Gedanke ist der Treiber, ja, wir installieren das Neueste. –

Antwort

2

Alles klar, nach Monaten der Betrachtung der Sache stellt sich heraus, dass das Problem nicht meine Webanwendung war. Das Problem waren die anderen Oracle Forms-Anwendungen, die dieselbe Instanz (anderer Benutzer) der Datenbank verwenden.

In den Oracle Forms-Anwendungen wurden Datensätze in der Datenbank gesperrt, wodurch die gesamte Arbeit der Datenbank extrem langsam wurde (einschließlich meiner beliebten Hibernate-Abfrage).

Der Grund für die Sperren war, dass keiner der Fremdschlüssel der Oracle Forms-Anwendungen indiziert wurde. Als mein Chef es mir erklärte (er entdeckte den Grund), wenn ein Benutzer den Master-Datensatz einer Master-Detail-Beziehung in einer Oracle Form-Anwendung bearbeitet, sperrt die Datenbank die gesamte Detailtabelle, wenn es keinen Index dafür gibt Unbekannter Schlüssel. Das liegt daran, dass Oracle Forms so funktioniert, dass alle Felder des Master-Datensatzes aktualisiert werden, einschließlich der Primärschlüssel, auf die der Fremdschlüssel verweist.

Kurz gesagt, bitte lassen Sie Ihre Fremdschlüssel NIEMALS ohne Indizes. Wir haben sehr darunter gelitten.

Vielen Dank an alle, die sich die Zeit genommen haben zu helfen.

+0

Sie beziehen sich auf das fremde Schlüsselfeld, auf das verwiesen wird, indiziert, richtig? – rogerdpack

0

Ich glaube, dass diese Abfrage vom Oracle JDBC-Treiber stammt, um eine Hibernate-Anforderung zum Abrufen von Datenbankobjekt-Informationen über DatabaseMetaData zu implementieren.

Diese Abfrage sollte nicht zu teuer sein, oder zumindest ist nicht auf einem System, das ich praktisch habe. Was ist Ihre Zählung von all_objects und was noch wichtiger ist, was sehen Sie in den Zeilen/Bytes insgesamt für den EXPLAIN-Plan?

+0

Hängt von den Bind-Variablen ab. Ein Platzhalter für den Besitzer und den Objektnamen könnte eine Menge Daten zurückbringen. –

+0

Dies kann unmöglich vom JDBC-Treiber kommen, da es nichts über Hibernate weiß. – BalusC

+0

... kein Java-Programmierer, aber ich wollte sagen, dass Hibernate eine Methode der DatabaseMetaData-Klasse aufgerufen hat, die Teil des Treibers ist, um Tabellen-/Ansichtsinformationen abzurufen - ich sehe jetzt mehr Wissen Hibernate-related Antworten seit meinem ursprünglichen Beitrag – dpbradley

1

Wird das sys-Schema in Ihrer 10g-Datenbank mit aktualisierten Statistiken analysiert? Hast du Statistiken zu den festen Tabellen im Systemschema gesammelt? Abfragen auf all_objects sollten nicht so besteuert werden wie auf einem System. Wenn Sie die Abfrage über autotrace/tkprof ausführen, wo/wo ist der Hauptteil der Ressourcen ausgegeben werden.

+0

Wir haben eine Menge von Objekten in der Datenbank auf Purpose (es gibt eine andere Anwendung in PL/SQL entwickelt), so dass diese Abfrage eine Menge Zeit benötigt, um auszuführen (und zusätzlich durch die Zeit, als wir es entdeckt haben, haben 17 Benutzer es gleichzeitig ausgeführt. Und wieder, diese Abfrage ist nicht Teil unserer Web-Anwendung, muss eine Hibernate/C3P0/Oracle-JDBC-Treiber-Sache sein, wir haben es noch nicht herausgefunden. –

3

Es kommt tatsächlich von Hibernate und speziell org.hibernate.tool.hbm2ddl.TableMetadata. Es wird unter jedem verwendet, um das Schema zu validieren (Tabellen- und Spaltenzuordnung). Anscheinend wurde es unnötigerweise bei jeder erzeugten Anfrage oder Sitzung ausgeführt, und nicht nur einmal während des Starts der Anwendung. Rufen Sie beispielsweise den Hibernate Configurator bei jeder Anfrage oder Sitzung nicht unnötigerweise an?

+0

Eigentlich ist diese Klasse ein Teil von Hibernate Core. –

+0

Ja? OK, überarbeitet. – BalusC

+1

Ja, die Verpackung kann irreführend sein, ist aber Teil von 'hibernate-core.jar'. –

2

Wie von @BalusC darauf hingewiesen, wird diese Abfrage während der Schemaüberprüfung durchgeführt. Bei der Erstellung der SessionFactory (falls aktiviert) wird die Validierung jedoch in der Regel einmal durchgeführt. Rufen Sie die folgende Methode explizit auf: Configuration#validateSchema(Dialect, DatabaseMetadata)?


Jetzt ist das richtig? Gibt es eine Möglichkeit für mich, das OC4J so zu konfigurieren, dass Filter nur einmal instanziiert werden?

Ihre Implementierung der offenen Sitzung in der Ansicht sieht gut aus (und ist sehr ähnlich der in this page vorgeschlagenen). Und gemäß der Servlet-Spezifikation wird pro Java Virtual Machine (JVMTM) des Containers nur eine Instanz pro <filter> Deklaration im Deployment-Deskriptor instanziiert. Da es sehr unwahrscheinlich ist, dass dies bei OC4J nicht der Fall ist, bin ich versucht zu sagen, dass da noch etwas anderes sein muss.

Können Sie etwas Protokollierung in den Filter? Was ist mit der SessionFactory statischen (in einer guten alten HibernateUtil Klasse)?

+0

Sorry, ich sah Ihre Antwort * nach * Ich aktualisierte meine Antwort mit einem Gedanken über die Hibernate-Konfiguration, aber das ist in der Tat die erste Richtung, in der das OP nach der Ursache und der Lösung suchen sollte :) (+ 1) – BalusC

1

Dies stammt von der standardmäßigen C3PO-Testabfrage. Stellen Sie eine einfachere Abfrage in Ihrer Konfiguration bereit. Etwas wie, wählen Sie "X" aus Dual.

2

Insbesondere was passiert ist, dass Leute, die Software schreiben, die verschiedene Datenbanken unterstützen, ihre Software in einer datenbankneutralen Weise verpacken. dh. Wenn eine Überschreibung nicht vorhanden ist, verwenden sie jdbc db metadata getTables call, um zu überprüfen, ob die Verbindung noch gültig ist. Normalerweise überschreiben Sie mit Select * von Dual usw., aber wenn dies nicht erfolgt oder Sie nicht ausdrücklich angeben, welche Art von Datenbank Sie verwenden, wird die Software so geschrieben, dass sie etwas ausführt, das mit jedem JDBC-Treiber funktioniert. jdbc db metadatabase getTables wird das tun.

2

Ich wollte nur die Problemumgehung, die ich verwendet habe, um dieses Problem zu umgehen. Wir haben typischerweise viele Schemas in unseren Datenbanken und das würde Stunden dauern, um in der Anwendung zu enden, die wir wegen der großen Anzahl von Objekten, die es überprüft hat, verwendet haben (die Abfrage selbst würde schnell ausgeführt werden, aber es ist einfach so so viele von ihnen).

Was ich getan habe, überschreibt die Sicht ALL_OBJECTS in dem Schema, mit dem es verbunden ist, so dass es nur seine eigenen Objekte und nicht alle Objekte in der Datenbank zurückgebracht hat.

z.B.

VIEW ALL_OBJECTS ERSTELLEN ODER ERSETZEN ALS BENUTZERORDNER AUSWÄHLEN, O. * FROM USER_OBJECTS O;

Es ist nicht die beste Lösung, aber für diese Anwendung gibt es nichts anderes, das die Ansicht ALL_OBJECTS verwenden würde, so dass es gut funktioniert und wesentlich schneller startet.

+0

Danke! Es ist eindeutig ein Workaround, den man am besten in der Produktion vermeiden sollte, aber es funktioniert perfekt in unserer Testumgebung. – Siggen

+0

Danke! Das war es - meine Abfragezeit änderte sich von Minuten zu Sekunden! – Grzegorz

1

hat das gleiche Problem, die Ursache war genau die von Bob Breitling beschrieben, verwendet C3P0 von Standard-JDBC-API für den Anschluss Prüfung:

java.sql.DatabaseMetaData#getTables(....) 

Um muss die preferredTestQuery dieses Verhalten zu ändern eingestellt werden , oder wenn C3P0 durch den Ruhezustand verwendet wird - hibernate.c3p0.preferredTestQuery

Verwandte Themen