Abrufen der Zeilen bei extrem hoher Geschwindigkeit
Ich habe eine sehr große Tabelle (Hunderte von Millionen Zeilen, enthält zahlen und Zeichenketten) in Oracle und ich müssen Lesen Sie der gesamte Inhalt dieser Tabelle formatieren und schreiben einer Datei oder eine andere Ressource.
In der Regel meine Lösung sieht wie folgt aus:
package my.odp;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.lang.Throwable;
import java.sql.*;
public class Main {
public static volatile boolean finished = false;
public static void main(final String[] args) throws InterruptedException {
final ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10000);
final Thread writeWorker = new Thread("ODP Writer") {
public void run() {
try {
File targetFile = new File(args[0]);
FileWriter fileWriter = new FileWriter(targetFile);
BufferedWriter writer = new BufferedWriter(fileWriter);
String str;
try {
while (!finished) {
str = queue.poll(200, TimeUnit.MILLISECONDS);
if (str == null) {
Thread.sleep(50);
continue;
}
writer.write(str);
writer.write('\n');
}
} catch (InterruptedException e) {
writer.close();
return;
}
}
catch (Throwable e) {
e.printStackTrace();
return;
}
}
};
final Thread readerThread = new Thread("ODP Reader") {
public void run() {
try {
Class.forName("oracle.jdbc.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@//xxx.xxx.xxx.xxx:1521/orcl", "user", "pass");
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(500000);
ResultSet rs = stmt.executeQuery("select * from src_schema.big_table_view");
System.out.println("Fetching result");
while (rs.next()) {
StringBuilder sb = new StringBuilder();
sb.append(rs.getString(1)).append('\t');//OWNER
sb.append(rs.getString(2)).append('\t');//OBJECT_NAME
sb.append(rs.getString(3)).append('\t');//SUBOBJECT_NAME
sb.append(rs.getLong(4)).append('\t');//OBJECT_ID
sb.append(rs.getLong(5)).append('\t');//DATA_OBJECT_ID
sb.append(rs.getString(6)).append('\t');//OBJECT_TYPE
sb.append(rs.getString(7)).append('\t');//CREATED
sb.append(rs.getString(8)).append('\t');//LAST_DDL_TIME
sb.append(rs.getString(9)).append('\t');//TIMESTAMP
sb.append(rs.getString(10)).append('\t');//STATUS
sb.append(rs.getString(11)).append('\t');//TEMPORARY
sb.append(rs.getString(12)).append('\t');//GENERATED
sb.append(rs.getString(13)).append('\t');//SECONDARY
sb.append(rs.getString(14)).append('\t');//NAMESPACE
sb.append(rs.getString(15));//EDITION_NAME
queue.put(sb.toString());
}
rs.close();
stmt.close();
conn.close();
finished = true;
} catch (Throwable e) {
e.printStackTrace();
return;
}
}
};
long startTime = System.currentTimeMillis();
writeWorker.start();
readerThread.start();
System.out.println("Waiting for join..");
writeWorker.join();
System.out.println("Exit:"+ (System.currentTimeMillis() - startTime));
}
}
Da sind zwei threads: einer für das abrufen der Zeilen aus dem result-set und einer zum schreiben von string-Werten. Gemessene Geschwindigkeit beim laden war etwa 10 MB/s und in meinem Fall muss ich es 10 mal schneller.
Profiler zeigt, dass die zeitaufwendige Methoden sind
oracle.jdbc.- Treiber.OracleResultSetImpl.getString()
und
oracle.net.ns.Paket.receive()
Haben Sie Ideen, wie man jdbc-zum laden von Daten viel schneller?
Irgendwelche Ideen, query Optimierung, string laden Optimierung, optimieren JDBC-Treiber oder mit anderen, die direkte Verwendung von oracle-JDBC-Implementierungen, Optimierungen Oracle geschätzt wird.
UPDATE:
Ich zusammengestellt und aufgelistet Diskussion der Ergebnisse im folgenden:
- Habe ich keinen Zugriff auf DBMS-server außer Verbindung zur Oracle-db und dem server kann keine Verbindung zu einer externen Ressource. Alle dump-und Extraktions-utils, die mit server-oder remote-Dateisystem kann nicht angewendet werden, auch ist es unmöglich, zu installieren und zu verwenden, keine externe java-oder PL/SQL-Routinen auf dem server. Nur-Schnittstelle, um Abfragen auszuführen - das ist alles.
- Ich verwendet profiler und grub in der Oracle-JDBC-Treiber. Ich fand heraus, dass die teuersten Betrieb ist, die Daten zu Lesen, also Sockel.read(). Alle string-Felder werden dargestellt als ein char-array und haben fast keinen Einfluss auf die perfomance. Generell habe ich überprüft, mit der profiler-die ganze app und Steckdose.read() ist definitiv die teuerste operation. Extrahieren Felder, Gebäude, Streicher, das schreiben von Daten, die verbrauchen fast nichts. Das problem ist nur, die Daten zu Lesen.
- Irgendwelche Optimierungen in der Darstellung der Daten auf der server-Seite keinen realen Effekt. Die Verkettung von strings und konvertieren von timestamps haben kein Ergebnis für die Leistung.
- App wurde neu geschrieben, um mehrere Leser-threads, die bereit Daten in writer Warteschlange. Jeder thread hat seine eigene Verbindung, keine pools verwendet werden, da Sie verlangsamen die Extraktion (die ich verwendet, UCP-pool von oracle empfohlen und es verbraucht etwa 10% der Ausführungszeit, so gab ich aus). Auch ResultSet fetchSize erhöht wurde, weil die Umschaltung vom voreingestellten Wert (10) 50000 gibt bis zu 50% perfomance Wachstum.
- Getestet habe ich, wie multithreaded-version arbeitet mit 4 threads Lesen und fand heraus, dass die zunehmende Leser zählen nur verlangsamt die Extraktion.
Ich habe versucht, zum starten von 2 Instanzen, in denen jeder von Ihnen hat zwei Leser und arbeiteten beide die gleiche Zeit als einzige Instanz, also das doppelte Datenextraktion erfordert gleichen Zeit als single. Weiß nicht, warum dies geschieht, aber es sieht aus wie oracle-Treiber haben einige performance-Einschränkungen. Anwendung mit 4 unabhängigen Anschlüssen arbeitet langsamer, dann 2 App-Instanzen mit 2 Anschlüssen.
(Profiler wurde verwendet, um sicherzustellen, dass Fahrer-Buchse.read() ist noch immer die wichtigste Frage, die alle anderen Teile funktioniert im Multithread-Modus). - Habe ich versucht zu Holen, alle Daten mit SAS und kann durchführen gleichen Extraktion 2-mal schneller als JDBC, beide single-Verbindung zu Oracle und können nicht alle dump-Operationen. Oracle stellt sicher, dass der JDBC-thin-Treiber ist so schnell wie eine native..
Vielleicht Oracle haben sich weitere Möglichkeiten, um eine schnelle Extraktion zu remote-host über ODBC oder etwas anderes?
Es gibt bessere Wege zu Holen, alle Daten von Oracle als mit Java. Überprüfen Sie die Oracle toolset, oder Fragen Sie Ihren DBA. Sie können blast die Daten in eine Datei. Dann können Sie es kopieren, wo es gehen muss.
Dies ist betteln für einen ETL-Prozess.
Was genau ist der Punkt, von polling mit einem timeout 200ms und dann schlafen für 50ms? Der Schlaf-Teil dies ist nur eine Verschwendung von Zeit: Sie habe schon 200mS in der
poll()
Methode, per definition. So können Sie erhöhen Ihre Geschwindigkeit, mit der rechten gibt. Es gibt nicht viel Punkt in der Angabe einer so kurzen poll()
timeout. Ein oder zwei Sekunden tun würde.Sie haben bereits setFetchSize, die oft die fehlenden Dinge, die der trick funktioniert. Ein paar andere Dinge zu versuchen, zuerst können Sie die Abfrage ändern, so dass es nicht die Verkettung, so dass, wenn Sie erhalten die Daten zurück, die Sie nur tun müssen, 1 getString? Zweite, kann dieser Vorgang ausgeführt werden, auf dem Datenbank-server, und verwenden Sie dann eine HINTERLÄSST, Verbindung, D. H. nehmen Sie das Netzwerk-element aus? Machst du einen full table scan auf die Tabelle (vermutlich ja), also warum nicht noch ein /*+ PARALLEL */ Tipp ( der Grad abhängig von Ersatz-CPU zu der Zeit).
InformationsquelleAutor user3007501 | 2014-08-16
Du musst angemeldet sein, um einen Kommentar abzugeben.
Angenommen, Sie haben bereits überprüft die grundlegenden Netzwerk-Sachen wie interfaces, firewalls, proxies, als auch der DB-server ist der hardware-Elemente.
Option 1 :
Statt :
versuchen Sie es mit :
Mehr details hier
Option 2 : Fetchsize
Als sehr spitz von Stephen, die fetchsize-scheint zu groß.
Und für fetch-Größe von 500.000, was Ihr -Xms und -Xmx. Auch in profiler, was ist das höchste-heap-Größe?
Option 3 : DB
Check Indizes und Abfrage-plan für
src_schema.big_table_view
Ist das ein tool oder eine Anwendung system. Wenn nur ein Werkzeug, Sie könnte
fügen Sie parallel Grad, index-hints, partitioning etc, basierend auf DB-Systeme
Fähigkeiten
Option 4 : Threads
Sagen
n
< Anzahl der Kerne, die auf application serverKönnen Sie beginnen
n
Threads Schriftsteller, die jeweils konfiguriert sind zum verarbeiten einer bestimmten Eimer z.B. thread1 Prozesse 0 bis 10000, schreibenn
verschiedenen Dateien, und wenn alle theads getan -, post-join, merge-Dateien zusammen vorzugsweise mit einem low-level-OS-command.Sagte, all dies sollte nie pre-defined code, wie ist das jetzt.
'n'
und der Eimer sollte zur Laufzeit errechnet werden. Und erstellen Anzahl der threads, die mehr als das, was Ihr system unterstützt nur die Schrauben.Option 5 :
Statt
Könnten Sie
Diese vermeidet das erstellen von 500000
StringBuilders
undStrings
. (Vorausgesetzt, dass keine anderen komplexen Formatierungen). CHR(9) ist das tab-Zeichen.Option 6 :
Inzwischen, Sie könnte auch überprüfen Sie mit Ihrem DBA für jedes DB-system Probleme und werfen eine SR mit Oracle-Unterstützung.
Auch ich machte einige Untersuchungen und gegraben in Treiber-Implementierung. Es spielt keine StringBuilders zu erstellen, Streicher, alle varchar-Daten intern dargestellt als single-char-array und verbraucht nicht viel Zeit zum abholen.Mit StringBuidler im Lesen Feldern ist zu wenig Zeit für die Arbeit (fast nichts profiling-Zeit). Über die Option 3 - Schreiben-thread wartet fast die ganze Zeit, weil JDBC-reader produziert Daten zu langsam. Problem tatsächlich ist in JDBC-Treiber Lesegeschwindigkeit. JDBC-reader wurde umgeschrieben, um Multithread(jeder thread extrahiert seine eigenen Daten und schreibt Sie in die Warteschlange)
Ich habe versucht, zu extrahieren, die Daten in mehrere Teile gleichzeitig, aber je mehr Leser ich verwende, desto schlechter die performance-Ergebnisse, die ich habe. Ich habe versucht, zum starten von 2 Instanzen, in denen jeder von Ihnen hat zwei Leser... und beide arbeiteten gleichen Zeit als einzige Instanz, also das doppelte Extraktion erforderliche gleichen Zeit als single. Weiß nicht, warum dies geschieht, aber es sieht aus wie oracle-Treiber haben einige performance-Einschränkungen. Anwendung mit 4 unabhängigen Anschlüssen arbeitet langsamer, dann 2 App-Instanzen mit 2 verbindungen.
Überprüfte ich den code mit der profiler in Situationen mit 2 und 4 threads, und nichts in meinem code verbraucht viel cpu-Zeit. Größte Verbraucher ist immer die Buchse.read() in JDBC-Treiber... Haben keine Ideen, warum es auf diese Weise verhält
Ich habe versucht zu Holen, alle Daten mit SAS und durchführen können-Extraktion 2-mal schneller als JDBC, beide single-Verbindung zu Oracle und können nicht alle dump-Operationen. Oracle stellt sicher, dass der JDBC-thin-Treiber ist so schnell wie eine native.. Vielleicht Oracle haben sich weitere Möglichkeiten, um eine schnelle Extraktion zu remote-host über ODBC oder etwas anderes?
InformationsquelleAutor Rajeev Sreedharan
Ihre profiling ist fehlerhaft
Den Methoden, die Sie Liste sind wahrscheinlich stark optimiert bereits. Ich habe analysiert, Systeme, in denen die meisten genannt und die meiste Zeit wurde damit verbracht, innen
StringBuffer.append()
innerhalb der Oracle-JDBC-code, da das gesamte system verwendetPreparedStatement
und ruft es diese Methode viel!. Unnötig zu sagen, das war ein red herring in unserem Fall.Profil der Datenverkehr im Netzwerk:
Wenn Ihre Verbindung gesättigt ist, ist Ihr Engpass nicht auf den code, die Sie aufgelistet.
Diese muss auf der server-Seite, wenn es sein muss Oracle als Quelle der Daten. Sie wird nie ziehen Hunderte von Millionen von Datensätzen über eine Netzwerk Verbindung und dann wieder zurück auf das 10-fache der Geschwindigkeit, die Sie jetzt zu bekommen es sei denn, Sie haben 10X die Netzwerkkarten in beiden Endpunkten und alle von Ihnen sind miteinander verbunden. Selbst dann bin ich skeptisch, erhalten Sie 10X den Durchsatz
Wenn Sie wirklich sind beschränkt auf Java und Oracle, die der einzige Weg, Sie bekommen mehr Durchsatz als die, die Sie bekommen jetzt ist führen Sie die Java als eine gespeicherte Prozedur auf dem server(s) generieren Sie die Dateien, die Sie benötigen, und dann rufen Sie aus dem remote-system.
Ich gebaut habe-Systeme, die sich mit Millionen von Transaktionen als die minute, diese Art der Durchsatz ist nicht passiert, über eine einzige Netzwerk-Verbindung, geschieht über ein Netz von Maschinen mit mehreren Netzwerk-Schnittstellen auf dedizierten send/receive switches auf einem dedizierten Subnetz isoliert vom rest der Datenverkehr im Rechenzentrum.
Auch
Ihrem threading-code ist bestenfalls als naiv. Sie sollten nie erstellen und verwalten von threads manuell.
ExecutorService
hat schon seit 10 Jahren, es zu benutzen!ExecutorCompletionService
ist, was Sie verwenden möchten, in diesem Fall, eigentlich in fast allen Fällen.ListenableFuture
ist eine noch bessere Wahl, wenn Sie verwenden können, Guave.InformationsquelleAutor
Wie es aussieht, haben Sie bereits gefunden und optimiert die Zeilen-prefetch-parameter. Jedoch, nach der Oracle-Dokumentation:
Sind Sie es bis 500000. Versuchen Wicklung es wieder rund 50 ... als Oracle empfehlen. (Warum? Nun, es könnte, dass ein enorm übermäßige prefetch-Größe ist, wodurch der server oder client zu verwenden, die große Mengen an Arbeitsspeicher zur Zwischenspeicherung der zuvor abgerufener Daten. Das könnte ein "knock-on-Effekt" auf andere Dinge konzentrieren, was zu weniger Durchsatz.)
Referenz (aus der Oracle-10g-Dokumentation):
Könnten Sie in der Lage, einen höheren Durchsatz durch ausführen von gleichzeitigen Abfragen in mehrere Java-threads (z.B. auf separaten "sections" in der Tabelle), schreiben jedes resultset zu einem separaten stream /Datei. Aber dann haben Sie das problem der Nähte der output-streams /Dateien zusammen. (Und ob Sie tun erhalten eine Allgemeine Verbesserung hängt von der Anzahl der client-und server-side-Kerne -, Netzwerk-und NIC-Kapazität und Disk-I/O-Kapazität.)
Abgesehen davon, kann ich mir nicht vorstellen, dass es eine Möglichkeit, dies zu tun schneller in Java. Aber Sie könnten versuchen, PL/SQL, oder etwas niedrigeren Niveau. (Ich bin kein Oracle-Experte. Sprechen Sie mit Ihrem DBAs.)
Einen Faktor 10 zu beschleunigen, in Java ist ... ehrgeizig.
Nach der OP, den Engpass (wie angezeigt durch den profiler) ist beim Lesen der Daten-Pakete von Oracle und drehen Sie Sie in Java-strings. Er ist nicht eine Liste erstellen. Er setzt Strings (Zeilen geschrieben werden) in eine Warteschlange. Diese sollte haben bewirkt, dass die Datenbank ausliest und die Datei schreibt, passiert gleichzeitig ... anstatt Sie zu zwingen, verzahnt werden. (Ob es tatsächlich wirksam ist schwer vorherzusagen, aber ich gehe davon aus, dass die OP ' s interpretation seiner profiling-Ergebnissen ist, dass es wirksam ist.)
Mein Fall zeigt sich, dass die FetchSize-Wert um 50000 gibt bis zu 50% performance-Wachstum, glaube ich default-Werte eignen sich für kleinere Ergebnismengen und häufige Anfragen
InformationsquelleAutor Stephen C