tutorials.de Buch-Aktion 05/2012
ERLEDIGT
NEIN
ANTWORTEN
10
ZUGRIFFE
269
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    AvS AvS ist offline
    Registriert seit
    Mar 2002
    Ort
    borken|nrw
    Beiträge
    367
    Hi,

    in welcher Form sollte man INSERT-Anweisungen auf ein DBMS absetzen ? Ich habe es jetzt erstmal zum Testen in folgender Form gemacht :

    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    public static void fill_branches(Connection conn, int n, int i) {
        double start = System.currentTimeMillis();
        try {
            while (i <= n) {
            Random rand = new Random();
            int x = rand.nextInt(n) + 1;
            PreparedStatement stmt = conn
                .prepareStatement("INSERT INTO BRANCHES (BRANCHID, BRANCHNAME, BALANCE, ADDRESS)"
                    + "VALUES ("
                    + i
                    + ", 'string' , "
                    + x
                    + " , 'string')");
            stmt.executeUpdate();
            i++;
            }
            conn.commit();
            double stop = System.currentTimeMillis();
            double ergebnis = (start - stop) / 1000;
            System.out.println("Neue Daten erfolgreich " + ergebnis
                + " Sekunden in BRANCHES eingetragen !");
        } catch (SQLException e) {
            e.getMessage();
        }
        }

    Nur wird dort ja bei jedem Schleifendurchlauf ein neues Statement aufgerufen, was ich mit bei vielen Einträgen ( n > 100000 ) schon als problematisch vorstellen könnte.
    Gibt es also Möglichkeiten, die das irgendwie performanter machen ?
     
    cash rules everything around me

  2. #2
    Biber2 Biber2 ist offline Mitglied Gold
    Registriert seit
    Jan 2007
    Ort
    Bremen
    Beiträge
    215
    Moin AvS,

    es läßt sich ein bisschen schwer erkennen, was denn in der Echt-Applikation der Inhalt der vielen neuen Sätze Deiner proof-of-concept-Skizze sein werden, also was der fachliche Hintergrund ist.

    Wenn die Mimik wirklich so simpel ist wie oben, dann würde ic (auch in dieser Reihenfolge) diese Änderungen prüfen:
    - wenn machbar, lass eine Stored Procdure diesen Massen-Insert machen (mit dem Parameter n für n Sätze sind anzulegen). Macht ja keinen Sinn, da 100000 Statements vom Client aus abzufeuern.
    - wenn die BranchId wirklich einfach nur hochgezählt wird, dann mach dieses Feld auch zum IDENTITY-Feld und überlass die ID-Vergabe der Datenbankengine. Raus damit aus dem INSERT-Statement

    Randbemerkung: Den Commit-nach-x-Sätzen solltest Du selbst übernehmen, also alle 5000 oder 10000 Sätze einen COMMIT machen und nicht pauschal einen nach allen Sätzen.

    Grüße
    Biber
    Geändert von Biber2 (22.11.08 um 13:51 Uhr) Grund: Tippfehlerkorrektur
     

  3. #3
    Registriert seit
    Jun 2002
    Ort
    Saarbrücken (Saarland)
    Beiträge
    9.886
    Blog-Einträge
    29
    hallo,

    du verwendest PreparedStatements nicht richtig...
    Schau mal hier:
    http://java.sun.com/javase/6/docs/ap...Statement.html

    Weiterhin sollte man die Statements wo möglich in Batches absetzen, also nicht jedes einzeln.

    Schau mal hier:
    http://www.java2s.com/Code/JavaAPI/j...ntaddBatch.htm

    Gruß Tom
     
    Java rocks!
    How to become a good Java Programmer?
    Does IT in Java and .Net
    The only valid measurement of code quality: WTFs / minute
    Blog
    Xing
    Twitter

  4. #4
    AvS AvS ist offline
    Registriert seit
    Mar 2002
    Ort
    borken|nrw
    Beiträge
    367
    Hi,

    also es werden mit dieser Funktion schon Daten eingetragen, allerdings auch nur bis ca. n = 1000. Dann macht er nicht mehr mit ! Da ich die Datenbank ja auf meinem Laptop installiert habe, spielen da nicht Sachen wie Arbeitsspeicher und Festplattenspeicher eine begrenzende Rolle ?

    Edit : Habe die Funktion für eine andere Tabelle umgeschrieben. Eingetragen wird jetzt allerdings nicht !

    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
        public static void fill_accounts_diff(Connection conn, int n, int i) throws SQLException
        {
        double start = System.currentTimeMillis();
        PreparedStatement stmt = null;
        try
        {
            int count = n * 10;
            String sql = "INSERT INTO accounts values(?, ?, ?, ?, ?)";
            stmt = conn.prepareStatement(sql);
            while( i <= count)
            {
            Random rand = new Random();
            int x = rand.nextInt(n) + 1;
            stmt.setInt(1, i); 
            stmt.addBatch();
            stmt.setString(2, "aaaaaaaaaaaaaaaaaa"); 
            stmt.addBatch();
            stmt.setInt(3, 0); 
            stmt.addBatch();
            stmt.setInt(4, x); 
            stmt.addBatch();
            stmt.setString(5, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 
            stmt.addBatch();
            stmt.executeBatch();
            }
            stmt.close();
            conn.commit();
    }catch(SQLException e)
        {
            e.getSQLState();
        }
        }
    Geändert von AvS (22.11.08 um 13:59 Uhr)
     
    cash rules everything around me

  5. #5
    Biber2 Biber2 ist offline Mitglied Gold
    Registriert seit
    Jan 2007
    Ort
    Bremen
    Beiträge
    215
    Moin AvS,

    ich will da Darimont nicht vorgreifen, weil das ohnehin nicht der Weg wäre, den ich eingeschlagen hätte, aber...
    IMHO verwendest Du jetzt die PreparedStatement:.addBatch()-Methode falsch.
    Du musst schon zu jedem einzelnen Statement die vollständige Parameterliste mit setString()/setLong() befüllen (oder zumindest alle Parameter, die einen not-default-Wert haben sollen).

    Also bitte NICHT so wie oben:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    ....
    // hier füllst Du NUR Param1 des PreparedStatementNo#1
    setString( parameterIndex1, 'bla');
     addBatch();
    // hier füllst Du NUR Param2 des PreparedStatementNo#2
    setLong( parameterIndex2, 4711); 
    addBatch();
     
    // hier füllst Du NUR Param3 des PreparedStatementNo#3
    setString( parameterIndex3, 'bla'); 
    addBatch();
    ....
    // und abfeuern

    -->führt dazu, das jedes der einzelnen PrepStatement einen Insert versuchen soll, der ein INSERT mit 3 NULL-Werten und einem "richtigen" Wert machen soll.

    Aber so weit kommt es vermutlich gar nicht - jedenfalls ist ja kein SQLError bei Dir hochgepoppt.->also ist es ein Nicht-SQLFehler.

    Anyhow, lies bitte noch einmal die beiden Links, die Darimont gepostet hat und korrigiere das Statement dahingehend, dass Du erst alle 5 Parameter setzt (wenn das INSERT 5 Werte braucht) und dann dieses "gefüllte" bzw. parametrisierte Statement cacht mit addBatch().
    Fülle so in einer dieser putzigen Schleifen ja 20 Inserts und feuere dann ein Execute ab.
    Alle 5000 Inserts oder umgerechnet alle 250 Schleifen ein Commit.

    Und dann sag an, ob die Performance sich spürbar verbessert hat oder ob wir mal was anderes versuchen.

    Grüße
    Biber
    Geändert von Biber2 (23.11.08 um 02:23 Uhr)
     

  6. #6
    AvS AvS ist offline
    Registriert seit
    Mar 2002
    Ort
    borken|nrw
    Beiträge
    367
    Hä ? Die geänderte Funktion macht doch genau das, was du gerade forderst oder nicht ?

    Dein Code :
    ....
    // hier füllst Du NUR Param1 des PreparedStatementNo#1
    setString( parameterIndex1, 'bla');
    addBatch();


    Mein Code :
    stmt.setInt(1, i);
    stmt.addBatch();


    Und in der Form steht es doch auch in der API
     
    cash rules everything around me

  7. #7
    Biber2 Biber2 ist offline Mitglied Gold
    Registriert seit
    Jan 2007
    Ort
    Bremen
    Beiträge
    215
    Moin AvS,

    sorry, wenn mein letzter Kommentar so zu Missverständnissen geführt hat.
    Frei zitiert hatte in der Tat DEINE fill_accounts_diff().
    Alledings mit Kommentaren an den Stellen, an denen ich der Meinung bist, dass Du die Funktion falsch verstanden hast.
    Ich versuche es mal anders. Richtiger wäre IMHO
    [[ das ist DEINE Funktion an den Stellen kommentiert , wo ich ändern würde!!]
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    
        public static void fill_accounts_diff(Connection conn, int n, int i) throws SQLException
        {
        double start = System.currentTimeMillis();
        PreparedStatement stmt = null;
        try
        {
            int count = n * 10;
            String sql = "INSERT INTO accounts values(?, ?, ?, ?, ?)";
            stmt = conn.prepareStatement(sql);
            while( i <= count)
            {
            Random rand = new Random();
            int x = rand.nextInt(n) + 1;
            stmt.setInt(1, i); 
            // der soll raus: stmt.addBatch();
            stmt.setString(2, "aaaaaaaaaaaaaaaaaa"); 
            // der soll raus: stmt.addBatch();
            stmt.setInt(3, 0); 
            // der soll raus: stmt.addBatch();
            stmt.setInt(4, x); 
            // der soll raus: stmt.addBatch();
            stmt.setString(5, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 
                  // .... Jetzt sind ALLE Parameter EINES Statements 
                  // .........Jetzt ist auch ein .addBatch() dran
            stmt.addBatch();
    // .............. aber: jetzt ist noch KEIN executeBatch() dran, sondern erst,
    //............. wenn Du 20 oder 50 INSERTs nach diesem Muster vorbereitet hast.
     
    // DIESE ZEILE SOLL ERST nach 20 vorbereiteten Inserts kommen
    /// ---------<      stmt.executeBatch();
            }
            stmt.close();
            conn.commit();
    }catch(SQLException e)
        {
            e.getSQLState();
        }
        }

    --> Du brauchst also wie gestern versucht herzuleiten, ZWEI Schleifen/Loop
    - die innere [" prepareInsertsAsBatch()" ] bereitet jeweils 20 oder 50 Inserts vor, danach wird das als EIN Statement executed
    - die äußere Schleife portioniert die Gesamt-Inserts, also ruft die innere Funktion so lange auf, bis alle 100000 Datensätze häppchenweise per executeBatch() gegen den Server gesendet worden sind.

    Aber ich glaube, ich lass mal jemand anders erklären; das ist nicht meine Stärke.

    Grüße
    Biber
    Geändert von Biber2 (23.11.08 um 13:38 Uhr) Grund: Sonntagstippfehler
     

  8. #8
    AvS AvS ist offline
    Registriert seit
    Mar 2002
    Ort
    borken|nrw
    Beiträge
    367
    Hi,

    habe selber noch ein wenig probiert und getestet und jetzt werden die Daten korrekt eingetragen. Habe zusätzlich auch noch den Speicherplatz für die Logfile der DB2-Datenbank aufs Maximum hochgeschraubt.

    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    public static void fill_branches(Connection conn, double n, int i) {
            double start = System.currentTimeMillis();
            try {
                String sql = "INSERT INTO BRANCHES (BRANCHID, BRANCHNAME, BALANCE, ADDRESS) VALUES (?, ?, ?, ?)";
                PreparedStatement stmt = conn.prepareStatement(sql);
                for(i=1; i<=n; i++){            
                stmt.addBatch(sql);
                stmt.setInt(1, i);
                stmt.setString(2, "aaaaaaaaaaaaaaaaaaaa");
                stmt.setInt(3, 0);
                stmt.setString(4, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
                stmt.executeUpdate();
                }
                conn.commit();
                double stop = System.currentTimeMillis();
                double ergebnis = (start - stop) / 1000;
                System.out.println("Neue Daten erfolgreich " + ergebnis
                    + " Sekunden in BRANCHES eingetragen !");
            }catch(SQLException e)
            {
                e.getMessage();
            }
        }
     
    cash rules everything around me

  9. #9
    AvS AvS ist offline
    Registriert seit
    Mar 2002
    Ort
    borken|nrw
    Beiträge
    367
    Hallo,

    ich muss nochmal nerven
    Ich habe die Inserts nun folgendermaßen implementiert. Die laufen auch alle durch, nur für n = 2.500.000 brauche ich mit meinem Laptop rund 4 Minuten. Liegt es primär an der Hardware oder doch am Skript ?

    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    PreparedStatement stmt_te = conn.prepareStatement("INSERT INTO TELLERS VALUES (?, ?, ?, ?, ?)");
            while(i<=count)
            {           
            Random rand = new Random();
            int x = rand.nextInt(n) + 1;
            stmt_te.setInt(1, i);
            stmt_te.setString(2, "aaaaaaaaaaaaaaaaaaaa");
            stmt_te.setInt(3, 0);
            stmt_te.setInt(4, x);
            stmt_te.setString(5, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
            stmt_te.addBatch();
            i++;
            }
            stmt_te.executeBatch();
            stmt_te.close();
            conn.commit();
     
    cash rules everything around me

  10. #10
    Registriert seit
    Jun 2002
    Ort
    Saarbrücken (Saarland)
    Beiträge
    9.886
    Blog-Einträge
    29
    Hallo,

    es reicht wenn du außerhalb der Schleife ein Random instanziierst.
    Weiterhin solltest du alle 2500 / 5000 Sätze einmal executeBatch() machen.

    Gruß Tom
     
    Java rocks!
    How to become a good Java Programmer?
    Does IT in Java and .Net
    The only valid measurement of code quality: WTFs / minute
    Blog
    Xing
    Twitter

  11. #11
    Biber2 Biber2 ist offline Mitglied Gold
    Registriert seit
    Jan 2007
    Ort
    Bremen
    Beiträge
    215
    Moin AvS;

    das wichtigste hat Darimont schon gesagt - im Moment machst Du nach jedem Einzel-Addbatch(), also für EIN Insert, sofort ein ExecuteBatch().
    Okay, aber eine Zeile am Stück würde ich nicht wirklich als Stapelverarbeitung bezeichnen.
    Weiterhin solltest du alle 2500 / 5000 Sätze einmal executeBatch() machen.
    Und, wie weiter oben schon mal vorgeschlagen, alle paar 1000 Sätze (z.B. nach 100000 Inserts) ein klitzekleines COMMITchen zwischendurch.

    Grüße
    Biber
     

Ähnliche Themen

  1. Performance Frage
    Von chefsalat- im Forum VisualStudio & MFC
    Antworten: 1
    Letzter Beitrag: 12.06.07, 12:10
  2. Performance Frage
    Von SantaCruze im Forum Relationale Datenbanksysteme
    Antworten: 2
    Letzter Beitrag: 28.02.07, 13:03
  3. Frage zur Performance
    Von SantaCruze im Forum PHP
    Antworten: 4
    Letzter Beitrag: 28.01.07, 13:41
  4. Performance-Frage
    Von SantaCruze im Forum PHP
    Antworten: 4
    Letzter Beitrag: 23.08.06, 20:15
  5. Performance Frage
    Von dave_ im Forum PHP
    Antworten: 1
    Letzter Beitrag: 31.07.02, 08:18