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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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).
  6. 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?

RDBMS ist nicht das richtige Werkzeug, um zu manipulieren Hunderten von Millionen von Zeilen, viel weniger in einem einzelnen Thread Ressource wie ein Netzwerk-socket oder file, was erwartest du? Mit I/O-eingeschränkte Ressourcen mehr threads == langsamer, nicht schneller. Karte Reduzieren und NoSQL-Datenbanken erstellt wurden, für einen Grund.
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

Schreibe einen Kommentar