Input/Output verschiedene Datentypen als Bytes in Datei speichern

ralfz

Aktives Mitglied
Hallo,

ich muss Daten in eine Datei speichern und natürlich auch wieder auslesen.

Die Daten entsprechen einer (riesigen) Tabelle, dessen Spalten nur Werte eines einzelnen Typs enthalten z.B. int, short, String, usw.

Das ganze muss jetzt in eine oder mehrere Dateien gespeichert werden und die Datenmenge nimmt rapide zu. (Datenbankanbindung ist leider nicht erwünscht)

Ich füge also immer "hinten" eine neue Datenzeile an und falls eine Spalte eine Key-Spalte ist, speichere ich den Wert und die Zeilennr. in einem Index. Primär interessiert nur das Einfügen und Lesen von Daten, evtl. noch das Updaten. Das Löschen bleibt aussen vor.

Falls es nur Spaltenwerte mit fixer Breite wären, könnte ich ja einfach die Bytes aneinanderfügen und in die Datei schreiben.
Bsp.: Spalten 1-3 mit int, short,long
-> byte[] bytes= new bytes[4+2+8] und die Werte in bytes umwandeln.
Oder macht es mehr Sinn, die Werte direkt mit writeXX() reinzuschreiben?

Was mache ich aber wenn ich auch noch String-Spalten habe(= variable Breite)?
Eine Idee wäre, die Länge des Strings mit zu speichern oder gleich für alle Spalten Tupel aus Datenlänge und Daten zu speichern? Spricht etwas dagegen? Gibt es eine andere/bessere Möglichkeit?

Was passiert, wenn sich Werte ändern? z.B. ein String-Wert überschrieben wird? Müsste ich dann die ganzen Daten in der Datei verschieben?

Zu berücksichtigen ist leider auch noch, dass nachträglich Spalten angefügt werden können.
Würde es daher Sinn machen, pro Spalte eine Datei anzulegen?
Ich vermute zumindest, dass würde die Performance beim speichern einer "Zeile" arg negativ beeinflussen, oder? Schliesslich müsste ich n-mal die passende Datei öffnen und den Wert am Ende eintragen!?

Wie realisiere ich das am Besten? Mir dreht sich schon ein wenig der Kopf...

Hoffe ihr könnt helfen...

Ralf
 
Zuletzt bearbeitet:
Die Spalten müssen ja irgendwie durch einen Trenner getrennt werden, dann hättest Du mit variablen Stringlängen keine Probleme. Eine Datei pro Spalte würde die ganze Sache noch komplexer machen. Vielleicht kannst Du so eine Art CSV-Format machen?
Ansonsten würde ich sehen, das ich zwischen dem Anwender und der Struktur eine Schnittstelle einbauen kann, mit der auf die Daten zugegriffen wird. Wenn Du so etwas realisieren kannst, könntest Du auch eine Datenbank lokal (vlt. javadb oder so) ansprechen, ohne das der Anwender/Entwickler davon etwas mitbekommt, Ich denke das wäre das Optimum, Du mußt den Leuten halt nur die Benutzung dieser Schnittstelle entsprechend schmackhaft machen.
 
Hallo,

also csv brauche ich nicht, wenn ich weiss, welcher Datentyp der Spalte zugrunde liegt, denn dann hat jede Spalte (bis auf String) eine fixe Datenlänge, wenn ich den Wert als Bytes speichere.

Ich hab mir das nun folgerndermassen überlegt:
Jede Datei hat n Zeilen (z.B. 1000), am Anfang jeder Datei stehen n (=1000) long Werte (FilePointer) für die Anfangspositionen der Zeilen.

Jede Zeile beginnt mit einem int als Bytes Wert, der die Länge der Datenzeile angibt und dann Tupeln aus entweder [Datenelementlänge und Daten] als Bytes, oder [Spaltennummer und Daten] als Bytes.

Somit kann ich die ganze Zeile im Arbeitsspeicher zusammenbauen bzw. zerlegen und leere Einträge einsparen und dann die Zeile jeweils in einem Rutsch lesen bzw. schreiben. (+ den Dateiindex aktualisieren)

Sollte zu einem späteren Zeitpunkt eine neue Spalte hinzugefügt werden, sollte im einfachen Fall das auch nur für neue Zeilen gelten, somit werden dann einfach die neuen Zeilen länger. Greift man auf "alte" Zeilen zu, die die neue(n) Spalte(n) nicht haben, dann ist das einfach wie eine Spalte, die keinen Wert hat. (ich weiss nur noch nicht, wie ich das im Speicher repräsentiere: Null, Platzhalter oder Exception)

Hässlich wird es natürlich, wenn sich Zeilen nachträglich ändern und sich dadurch die Zeilenlänge insgesamt ändert. Wie könnte ich das einbauen? Jedesmal die ganze Datei neu zu schreiben (Stream-copy) ist sicherlich unschön, aber schlimmer wäre es vermutlich die ganzen nachfolgenden Zeilen zu verschieben, oder?

Wenn ich bei länger gewordenen Zeilen diese einfach hinten anfüge, dann habe ich aber "Lücken" und Schrott in der Datei stehen...

Welcher Weg wäre hier der Beste, bzw. wie seht ihr meine Idee oben?

Gruß
Ralf
 
Vielleicht kannst du die Typeninformationen zu einer Tabelle und später vlt. nicht mehr in einer extra Definitionsdatei schreiben. Dann wäre in den eigentlichen Tabellen nur noch reine Daten und die Informationen über deren Länge und Typ etc. in einer separaten.
Wenn sich die Strukturinformationen ändern, müssen im schlechten Fall auch die Daten angepaßt werden (wie bei Db's halt). Bei Spalten anfügen wäre hier die Idee, pro Spalte eine Datei sehr effizient, da Du nur die Strukturdatei anpassen und eine neue Datei für die Spalte erzeugen müßtest (mit sovielen Werten, wie es Datenzeilen gibt). Du mußt aber prüfen, ob das wirklich spart.
Lücken mußt Du irgendwie wegoptimieren, bei DB' ist das z.B. optimize table, oder vacuum analyze usw.

Kurzum:

* Ich würde eine Datei pro Tabelle schreiben, in der Strukturinformation enthalten sind (Datentypen, Längen etc)
* Dann würde ich testen: Lücken wegoptimieren und Spalte anfügen/ändern/löschen für die Lösungen jeweils : pro Datei eine Spalte - gesamte Tabelle in einer Spalte. Was läuft schneller oder besser und ist aber auch verständlich zu programmieren.
* Auf jeden Fall prüfen: Falls Du die Benutzung an ein Interface festmachen kannst, das der "Benutzer" deiner API dann benutzt, kannst Du auch unter Hand eine Datenbank (javadb etc.) nutzen, was viel viel Arbeit erspart.
 
Hallo,

1. zu deiner Idee: [...Tabellen nur noch reine Daten und die Informationen über deren Länge und Typ etc. in einer separaten...]

Ich möchte nach dem Speichern mit den Dateien 2 Dinge machen können:
a) schnell gezielt einen Datensatz finden und lesen -> dafür der Datei-Index
b) über die Datenzeilen iterieren -> durch das Mitspeichern der Zeilenlänge muss ich nicht jedes mal zurück zum Index springen, sondern kann gleich weiter lesen wo ich vorher aufgehört habe

=>Daher sollten diese Infos direkt dort stehen. Natürlich benötige ich jetzt noch einen "Datei-Header", damit ich die Daten aus den Zeilen wieder "entpacken" kann.

=> Ein Artikel zu Java & Performance bzw. ein Datenbankbuch besagt, dass es beim Arbeiten mit Festplatten am Besten ist Daten am Stück zu Lesen bzw. zu Schreiben und das gerade das Suchen (Seek) sehr teuer ist und die Performance erheblich beeinflusst...; Daher habe ich mich auch schon von der Idee "Pro Spalte eine Datei" verabschiedet.

2.Durch das Speichern von Datentupeln: z.B. Spaltennr+DatenAlsByte umgehe ich das Problem, dass bei dem nachträglichen hinzufügen von Spalten der komplette Datenbestand angepasst werden muss.

3.Das sich der Typ von Spalten ändert ist so nicht vorgesehen (hoffe ich 😉 )

4. Header: Wie könnte bzw. sollte denn so ein Dateiheader am Besten realisiert werden?

(a) Eigene Typen für den Spaltentyp definieren? z.B. 1=byte, 2=short, usw. => die Spalten [int,int,short] würden dann als 3 3 2 geschrieben und ich müsste das ganze über switch abfragen...

oder (b) die Typnamen in die Header Datei:
bsp int = int oder Integer oder java.lang.Integer

Gruß
Ralf
 
Zuletzt bearbeitet:
Falls du unter Windows bist:

Java:
		Connection connection;
		Statement statement;
		String path = new File(".").getAbsolutePath();

		String driver = "jdbc:odbc:Driver={Microsoft Text Driver (*.txt; *.csv)};DBQ="
				+ path + ";Extensions=asc,csv,tab,txt";

		Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		connection = DriverManager.getConnection(driver, "", "");
		statement = connection.createStatement();

		statement.execute("Select * from data.csv where index>5");
		ResultSet results = statement.getResultSet();
		while(results.next())
		{
			System.out.println("Found Value(" + results.getInt(1)+") = " + results.getInt(2));
		}
		
		
		statement.close();
		connection.close();

schema.ini
Code:
[data.csv]
ColNameHeader=False
Format=Delimited(;)
MaxScanRows=0
CharacterSet=ANSI
Col1=Index Integer 
Col2=Value Text Width 30

data.csv:
Code:
1;1
2;2
3;3
4;4
5;5
6;6
7;7
8;8
9;9
10;10
11;11
12;12
13;13
14;14
15;15
16;16
17;17
18;18
19;19
20;20
21;21
22;22
23;23
24;24
25;25
26;26
27;27
28;28
29;29
30;30
31;31
32;32
33;33

Weitere Infos:
Schema.ini File (Text File Driver)
starcatcher.ca Blog Archive DSN-less Microsoft Text Driver
 
Hallo,

@Crian: Riesig heisst etwa +1GB / Monat. Die Daten kommen aus einer Simulationsanwendung für die Fabrikation

@ariol: Das sieht ganz spannend aus, aber es gäbe für mich hier 2 Probleme:
1.) es muss sich nicht unbedingt um Windows handeln
und
2. kann ich bei String bzw. Texttyp leider vorher nicht die Länge fest definieren

Grüße
Ralf
 
Noch ein paar Fragen:
- Muss das Format menschenlesbar sein?
- Bedeutet keine Datenbank auch: Keine eingebettete Datenbank (wie z.B. Derby)
- Kannst du uns einen Beispieldatensatz zeigen?
- Existieren diese Daten in einer Klasse?
- Dürfen die einzelnen Datensätze über mehrere Dateien verteilt sein. (Datensatz 1-10 in Datei 1, 11-20 in Datei 2, usw.)
- Geht es mehr um Geschwindigkeit oder eher um Größe?
 
Imho fängst du gerade an eine embedded-Datenbank nachzuprogrammieren. Nimm doch besser gleich H2, damit bleibst du plattformunabhänig und du erfindest das Rad nicht neu. Ich hab manches nur überflogen, aber anscheinend spricht ja nichts gegen eine embedded DB.
 
Hallo...

@Ariol:
- Mem to File: Serialisierung weil "schnellste" Speichermethode (aber ohne die direkte Objektserialisierung)
= Dateien müssen/sollen nicht menschenlesbar sein
- Mehrere Dateien sind ok und vermutlich auch die einfachste Lösung für das "flexible" hinzufügen neuer Spalten
- Haupt-Ziel ist es viele Daten (aus der Simulationsanwendung) schnell wegzuschreiben; nebenbei soll natürlich möglichst viel Speicher für die Sim-Anwendung zur verfügung stehen.

Als vereinfachtes Szenario gibt es z.B.: Tokens laufen von Quelle ->Puffer-> Stationen M1..10 -> Senke
-> Messung des Durchlaufs; Pufferbelegung usw. zu bestimmten Zeiten
oder-> Messung der Durchlaufdauer für x Tokens

Ich habe die Anwendung aber nicht gesehen.
Es gibt eine Schnittstellendefinition für das Datenmodell; bisher nur als in-Mem realisiert.
Explizit kein SQL.

Meine Vorschläge waren anfangs: (Standard-)Serialisierung oder eine OO-DB z.B. db4o. Die Datentabelle (quasi Collection aus Rows) stückeln, neue Zeilen hinten anfügen und die (key;Zeile) in einem Index (B+ Baum) verwalten.

Bei fester Größe einer Datei kann dann die n-te Zeile einfach über mod und div in der entsprechenden Datei gefunden werden. Da die Datenbank irgendwann zu groß für den Speicher wird, soll es für den Durchlauf einen Iterator geben.

@Tomate-Salat:
Sehe ich ja ähnlich, nur der Auftraggeber hat sich ziemlich an der Serialisierung (klein, schnell) zum Schreiben von Daten verbissen... H2 sieht soweit auch ganz spannend aus, ich fürchte nur ich muss mindestens die Serialisierungslösung implementieren und als "Vergleich" die OO-DB

Gruß
Ralf
 
Gerade wenn es ums schnelle geht, sollte man zu Datenbanksystemen greifen. Vllt lässt sich ja nochmal mit deinem Auftragsgeber reden. Vllt solltest du ihm nochmal klar machen, dass Datenbanken dazu erfunden worden sind um Daten zu halten.
 
@Tomate-Salat:

Das Datenbanken dafür gemacht sind ist ihm und mir klar, ABER Datenbanken sind in der Regel ein Multifunktionstool, daher ist es denkbar, dass eine spezialisierte Lösung in gewisser Hinsicht besser oder schneller ist.

Einfaches Beispiel: Die Java Collections vs. Gnu.Trove Collections, die weitestgehend auf Objekte verzichten und mit primitiven Datentypen arbeiten statt mit Objekten.

Weiterhin habe ich mal nach Performance-Vergleich bei DBs geschaut (z.B. PolePosition -> DB Benchmark) und es gibt dort deutliche Unterschiede zwischen den einzelnen DBs je nach Aufgabe...

Ich habe mal selbst nen einfachen Benchmark(Schreiben von 1000 Objekten nach DB4O vs. XML mit JAXB und Objektserialisierung) durchfeführt und die Serialisierung war deutlich schneller und die Zieldatei zusätzlich am Kleinsten.

BTW: H2 erwartet wiederum SQL, was es gerade schwierig macht "nachträglich" mal eben eine Spalte hinzuzufügen. Bsp: Für ein neues Sim-Modell benötige ich eine weitere Arbeitsstation. Bei einer normalen DB müsste man nun die ganze DB anhalten und über SQL eine neue Spalte einfügen...

Gruß
Ralf
 
Zuletzt bearbeitet:
SQL:
ALTER TABLE xyz ADD age INTEGER BEFORE name
Wenn du das mit Spalte meintest, kein Problem in SQL. Beim Benchmark musst du aber alles Beachten: Lesen/Schreiben. Zudem kommt, dass die Daten in einer DB oft wesentlich effizienter abgelegt werden. Dass kommt gerade dem Lesen von Daten zu gute.
 
Also mein Vorschlag ist ja immer noch, dem Kunden einfach ein Interface anzubieten. Die Implementierung (von Dir) davon kann dann eine Datenbank oder was immer sie möchte nutzen, dem Kunden bleibt dies verborgen. Ob und wie das intern mit einer DB oder auch nicht gehandhabt wird, bleibt dem Kunden dabei völlig verborgen. Er arbeitet halt mit dem Interface, wobei Du natürlich recht hast, es soll natürlich schnell sein.
TomateSalat hat auch recht, du programmierst gerade eine Art DB nach. Aber wie gesagt, mit der Schnittstelle für den Kunden wäre das völlig schnuppe.
 
Also eine gute Db sollte deine Idee sowohl in punkto geschwindigkeit als auch im speicherverbrach deutlich toppen dank wesentlich intelligenter algorithmen. Insbesonder wenn Datensätze gelesen werden sollen wird sich dies auswirken.
 

Neue Themen


Zurück
Oben