Befüllen einer MySQL DB mit großen Daten

ElJarno

Mitglied
Hi Leute,
soll in einer MySQL DB Datein speichern und bei bedarf auch wieder auslesen. Mein Code unfktioniert soweit. Jedoch hab ich das Problem dass sobald eine Dtaie ein größe von zirka 15 MB überschreitet der Heap von Java überläuft.

Hier mal meine Methode zum befüllen der DB:

Code:
	private void schreibePDFinDB(String dateiID, String pfad, int offsetDatei, int packetGroesse)
			throws IOException, SQLException {
		int partOffset = 0;
		int partBytesRead = 0;
		int partNr = 0;
		int partGroesse = 0;
		File datei = new File(pfad);
		byte[] bytes = this.liesDatei(datei, offsetDatei);
		int bytesLeft = bytes.length;

		while (bytesLeft > 0) {
			if (bytesLeft < packetGroesse) {
				partGroesse = bytesLeft;
			} else {
				partGroesse = packetGroesse;
			}

			partNr += 1;

			ByteArrayInputStream byteInpStr = new ByteArrayInputStream(bytes, partOffset,
					partGroesse);
			dbBinaerDaten.writeBinaerDaten(dateiID, partNr, partBytesRead, byteInpStr);
			bytesLeft -= partGroesse;
			partOffset += partGroesse;
		}
	}
Code:
	public void writeBinaerDaten(String dateiID, int part, int leng, ByteArrayInputStream daten)
			throws SQLException {
		String objid = ObjectID.getOBJID(199);

		java.sql.PreparedStatement ps = this.getConnection().prepareStatement(
				"INSERT into BINAER_DATEN VALUES(?,?,?,?,?)");
		ps.setString(1, objid);
		ps.setString(2, dateiID);
		ps.setInt(3, part);
		ps.setInt(4, leng);
		ps.setBlob(5, daten);
		ps.execute();
	}

Bei der Methode liesDatei(datei, offsetDatei) entsteht noch kein Fehler. Also muss es an der Befüllung der Datenbank liegen.
Falls dies nicht genügt kann ich den Rest noch hinterher werfen.


Gruß Jan
 
Zuletzt bearbeitet:
Hallo,

die JVM - vor allem die aus standardisierten Containern wie Tomcat, appservern - starten eine jvm mit standard Parametern.
Falls du mehr brauchst, parsen von xml ist immer recht teuer (siehe http://de.wikipedia.org/wiki/Streaming_API_for_XML dazu), dann muss man entsprechend die jvm vergrössern.

Beim Starten aus Kommandozeile ist es recht simpel:

Code:
java -Xms64m -Xmx256m MyClass

Xms -> initial heapsize
XmX -> maximum heapsize
 
Oke hab ich mir schon gedacht. Wollte halt nur diese Methode wenn möglich ungehen, desshalb wollte ich die Daten Aufsplitten um genau das zu verhindern dass der Heap Space nicht reicht. Kann es sein dass die Garbage Collection nicht hinetrhekommt. Weil wenn ich die Datei ohne zu splitten der DB übergeb kommt es zu keinem Mangel an Heap Space. Und was genau meinst du mit dem Parsen von XML teuer sei. Wo geschieht dass den in meinem Code. Sorry für die blöde Frage.

Schon mal danke.

Gruß Jan
 
XML hatte ich gerade im Kopf, war thematisch voll daneben xD.

Die Garbagecollection kann man recht schwer optimieren. Die Logik dahinter ist komplex und hängt von der Nutzung der vm ab. Bei einer grösseren jvm beispielsweise arbeitet die gargabe collection einfach nur "fauler". Sprich sie räumt später auf, da das Speicherlimit später erreicht wird.

Man könnte herausfinden, welche Objekte da den heap so vollmachen. Verschiedene Schleifenkonstrukte über grosse Strukturen können viel Platz verschwenden.

Bei deiner Struktur hat man nach der Einlesemethode schonmal die 15 MB in dem ByteArray stehen. Die bleiben dort bis zum Ende des Programmes.
Bekommst du einen java.lang.OutOfMemoryError? Kannst du den mal posten?

Wenn du dich mit dem heapdump auseinandersetzten möchtest, HAT https://hat.dev.java.net/ ist recht bedienungsfreundlich.
Ansonsten würde ich einfach mal grössere heapsizes testen und schauen ab wann das Programm die 15MB durchbringt.
 
Wie blöd von mir der Fehler lag nicht beim schreiben in die Datenbank sondern beim wieder heraus holen:D.
Hier mal der Code für das Zusammensetzten der Pakte aus der DB:
Code:
	private void lesePDFausDB(String dateiID, String pfad) throws SQLException, IOException {
		String where = "DATEI_ID = '" + dateiID + "'";
		String sortBy = "PART";
		Vector<Byte> byteDaten = new Vector<Byte>();
		Vector<Vector<Object>> vectorDaten = this.dbBinaerDaten.readBinaerDaten(where, sortBy, 0);
		BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(pfad, true));
		for (Vector<Object> daten : vectorDaten) {
			byte[] bytePart = (byte[]) daten.get(4);
			for (byte by : bytePart) {
				byteDaten.add(by);
			}
		}
		byte[] by = new byte[byteDaten.size()];
		for (int i = 0; i < byteDaten.size(); i++) {
			by[i] = byteDaten.get(i);
		}
		output.write(by);
		output.close();
	}
Kann mir vorstellen dass es an den beiden Schleifen liegt. Vielleicht hast du ja einen anderen Ansatz.
Hier noch der Code zum Einlesen der Pakete aus der DB:
Code:
	public Vector<Vector<Object>> readBinaerDaten(String where, String sortBy, int sortOrder)
			throws SQLException {
		Vector<Vector<Object>> daten = null;
		StringBuffer sqlB = new StringBuffer();
		sqlB.append("select * from BINAER_DATEN ");
		sqlB.append("where ");
		sqlB.append(where);
		sqlB.append(" order by ");
		sqlB.append(sortBy);
		if (sortOrder == 1) {
			sqlB.append(" desc");
		}
		if (!where.contains(";"))
			sqlB.append(";");
		daten = this.readMySQLTable(sqlB.toString());

		return daten;
	}// end of readBlob where
Und Ja es handelt sich um eine OutOfMemory Exception
Code:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:2760)
	at java.util.Arrays.copyOf(Arrays.java:2734)
	at java.util.Vector.ensureCapacityHelper(Vector.java:226)
	at java.util.Vector.add(Vector.java:728)
	at com.ed.bp.DB.DBMethods.lesePDFausDB(DBMethods.java:78)
	at com.ed.bp.DB.DBMethods.main(DBMethods.java:26)
 
toArray() erzeugt eine Kopie der Daten der Collection als ObjectArray.

Als kleine Optimierung könntest du während der letzten Schleife die Vectordaten direkt rausschreiben, anstatt erst alles in einem byteArray zu sammeln.
Oder schon in der Schleife davor, direkt in den OutputStream schreiben, anstatt die bytes des ganzen Vectors zu sammeln.

Führe doch mal das Programm im debug modus aus und schau dir vor der letzten und vorletzten Schleife dir instanziierten Objekte an. Eventuell braucht man davon einige grosse gar nicht.

edit:
kleiner exkurs
OutOfMemoryError extends java.lang.Error implements java.lang.Throwable
IOException extends java.lang.Exception implements java.lang.Throwable
 
Zuletzt bearbeitet:
So habs nun hinbekommen.
Danke noch mal.
Hier noch der Code für spätere Generationen:D

Zum schreiben einer Datei in die DB:
Code:
    public void writeBinaerDaten(String dateiID, int packetGroesse, File datei)
            throws SQLException, IOException {
        BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(datei));
        long dateiGroesse = datei.length();
        int partNr = 0;
        if (dateiGroesse <= Integer.MAX_VALUE) {
            int bytesLeft = (int) dateiGroesse;
 
            while (bytesLeft > 0) {
                String objid = ObjectID.getOBJID(199);
 
                java.sql.PreparedStatement ps = this.getConnection().prepareStatement(
                        "INSERT into BINAER_DATEN VALUES(?,?,?,?,?)");
                ps.setString(1, objid);
                ps.setString(2, dateiID);
                ps.setInt(3, partNr);
                ps.setInt(4, packetGroesse);
                ps.setBinaryStream(5, inputStream, packetGroesse);
                ps.execute();
                ps.close();
                partNr += 1;
                bytesLeft -= packetGroesse;
            }
        } else {
            throw new IOException("Datei ist zu groß! " + datei.getName());
        }
 
    }

Zum herausholen einer Datei aus der DB und die abschließende Speicherung auf der Festplatte.
Code:
    public void readBinaerDaten(String dateiID, int packetGroesse, File datei) throws SQLException,
            IOException {
        ResultSet resultSet = null;
        StringBuffer sqlB = new StringBuffer();
        byte[] bytes = null;
        BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(datei, false));
 
        sqlB.append("select DATEN from BINAER_DATEN ");
        sqlB.append("where DATEI_ID = '" + dateiID + "'");
        sqlB.append(" order by PART");
        sqlB.append(";");
        resultSet = this.getStatement().executeQuery(sqlB.toString());
 
        while (resultSet.next()) {
            bytes = resultSet.getBytes("DATEN");
            output.write(bytes);
 
        }
        output.close();
        resultSet.close();
    }

Gruß Jan
 

Neue Beiträge

Zurück