"Christmas - the time to fix the computers of your loved ones" « Lord Wyrm

Verwaltung großer Datenmengen (binär) in PHP/MSSQL

jives 03.09.2008 - 18:03 3122 21
Posts

jives

And the science gets done
Avatar
Registered: Sep 2001
Location: Baden
Posts: 3548
Aufgabenstellung
Ich brauche in einer PHP (5.2.6, IIS 6) Applikation eine große Menge an Daten in Form eines 2D-Arrays. Diese Daten kommen aus einzelnen Files in welchen diese Daten in binärer Form abgespeichert sind.

Die Frage ist nun, wie ich die Daten in einer Datenbank (MSSQL Express 2005) abspeichern kann, so dass der Zugriff auf große Mengen (mit bis zu mehreren Millionen Einträgen im 2D-Array) dieser Daten einerseits möglichst schnell erfolgt, andererseits aber die Datenbank nicht nach kurzer Zeit an ihre Grenzen stößt.

Iststand
Momentan speichere ich die Daten eines jeden Einzelfiles als eigene Zeile pro File in der DB, wobei ein Feld die binären Nutzdaten Base64-codiert enthält. PHP holt diese Daten ab und "entpackt" die Daten in 2D-Array-Form.

Problem
Bechmarks haben folgendes ergeben: Das Holen der Daten eines durchschnittlichen Files dauert 126ms, wobei 124,9ms lang entpackt wird. Da nun durchaus mehrere 100-1000 "Files" gelesen werden müssen, dauert alleine das Holen der Daten mehrere Sekunden bis Minuten, was wir dem User nicht antun wollen.

Lösungsansätze
Eine Alternative wäre das Entpacken der Daten beim Eintragen in die DB, wobei man um die n x m -Struktur zu wahren zwei Tabellen gebraucht werden - die eine bekommt für jedes neue File n Einträge (erste Dimension) mit Verweis auf die File-id, die zweite n x m Einträge mit Verweis auf die entsprechende id in der ersten Tabelle. Das Problem hierbei ist, dass mir ganz schnell die IDs ausgehen werden fürchte ich, denn: Ein (durchschnittliches) File hat 15x1000 Einträge und ich habe meherere 100-1000 Files täglich in die DB einzupflegen.

Die zweite Alternative ist das Entpacken beim Eintragen in die DB, aber speichern in einem 2D-Array, das mit serialize() bearbeitet wurde. Hier entstehen durch serialize() natürlich enorm große Datenmengen. Weiters hat diese Variante schon große Probleme mit der DB gemacht, und ich kenne die Performance von unserialize() nicht.

Andere Möglichkeiten die mir einfallen sind: Auslagern der entsprechenden Operationen in ein C-Programm (schneller?) und Stored Procedures auf der DB (keine Ahnung ob die wirklich was bringen würden).


Habt ihr Erfahrung mit solchen Anwendungen? Wie würdet ihr dieses Problem lösen? Ich wäre für jeden Input und jeden Tipp sehr dankbar :)
Bearbeitet von jives am 03.09.2008, 23:00 (Typos)

><))))°>

Idle ...
Avatar
Registered: Sep 2002
Location: Wien
Posts: 1586
ich hab zwar schon länger nicht mehr beruflich mit IT zu tun, aber damals wurde mir beigebracht, dass man in so einem fall die daten als dateien auf der platte ablegt, und in der datenbank nur einen link dort hin einträgt. damit überlastet man die datenbak software nicht, und die grösse der dateien ist auch egal.

edit: ich hoff ich hab dein problem richtig verstaden. ansonsten meinen beitrag einfach ignorieren :p

jives

And the science gets done
Avatar
Registered: Sep 2001
Location: Baden
Posts: 3548
Den Gedanken hatte ich auch schon :)

Das Problem des Konvertierens von Binär- zu "Nutz"-Daten bleibt bestehen. Auch wenn ich die Daten als "Klartext" mit serialize() oder einem eigenen Format speichere, bleibt das Problem des Umwandelns bestehen.

UncleFucka

-
Avatar
Registered: Jun 2002
Location: CH
Posts: 4737
die frage ist wohl eher ob das nicht die komplett falsche anwendung einer datenbank ist.. ich mein.. du speicherst in der datenbank files ab die aus zeilen und spalten bestehen.. so wie eine datenbank..

das beste wäre wohl ein gscheites schema zu machen und die files richtig in die datenbank einzutragen so das du direkt drauf selektieren kannst. so wirst du die files komplett los und hast die performance.

jives

And the science gets done
Avatar
Registered: Sep 2001
Location: Baden
Posts: 3548
Zitat von Uncle****a
das beste wäre wohl ein gscheites schema zu machen und die files richtig in die datenbank einzutragen so das du direkt drauf selektieren kannst. so wirst du die files komplett los und hast die performance.
Zitat von jives
Eine Alternative wäre das Entpacken der Daten beim Eintragen in die DB, wobei man um die n x m -Struktur zu wahren zwei Tabellen gebraucht werden - die eine bekommt für jedes neue File n Einträge (erste Dimension) mit Verweis auf die File-id, die zweite n x m Einträge mit Verweis auf die entsprechende id in der ersten Tabelle. Das Problem hierbei ist, dass mir ganz schnell die IDs ausgehen werden fürchte ich, denn: Ein (durchschnittliches) File hat 15x1000 Einträge und ich habe meherere 100-1000 Files täglich in die DB einzupflegen.
Das ist das, was mir dazu eingefallen ist. Meinst du sowas? Wenn ja, was sagst du dann zu dem von mir angesprochenen Problem? Oder macht man das dann anders?

Edit:
Man könnte auch immer eine neue Tabelle mit einem eindeutigen, generierten Namen für jedes File anlegen und die Files einfach in Tabellenform ohne id oder ähnlichem schreiben. Ich weiß aber nicht, ob man sowas wirklich machen soll/kann? Das werden dann ja unzählige Tabellen.
Dafür könne das Selektieren mehrerer Datensätze dann vielleicht mit JOINs durchgeführt werden...
Bearbeitet von jives am 03.09.2008, 19:03

that

Moderator
Hoffnungsloser Optimist
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
Brauchst du für Operationen immer das ganze Array? Falls ja -> BLOBs oder Files außerhalb der Datenbank. Falls nein -> aufteilen.

jives

And the science gets done
Avatar
Registered: Sep 2001
Location: Baden
Posts: 3548
In 99% der Fälle brauche ich das ganze Array bzw. mehrere komplette Spalten daraus.

In welcher Form meinst du soll ich die Daten ablegen?
Aber wenn ich die Daten als BLOB 1:1 im Dateisystem ablege, muss ichs in PHP ja erst entpacken - das ist genau die Operation, die jetzt Probleme macht.
Ich könnte probieren die Daten mit serialize() abzuspeichern, wobei die Operationen viel Speicher fressen und vermutlich nicht ausreichend perfomant sind.

Habe ich hier einen Denkfehler? Müsste ich nicht eigentlich die Daten auf jeden Fall in der DB verwalten, um ausreichende Performance gewährleisten zu können?

Meinst du mit aufteilen so wie von mir vorgeschlagen (eigene Tabelle pro File/zwei neue Tabellen für alle Files)?

that

Moderator
Hoffnungsloser Optimist
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
Wenn du mehrere komplette Spalten brauchst, würde sich vermutlich anbieten, eine Zeile in der DB-Tabelle pro Spalte deiner binären Tabelle zu machen. Und du willst auf keinen Fall eine dynamische Anzahl von DB-Tabellen.

Und entpacken würde ichs möglichst gar nicht, sondern erst direkt beim Zugriff - gibts in PHP Byte-Arrays?

COLOSSUS

Administrator
GNUltra
Avatar
Registered: Dec 2000
Location: ~
Posts: 12070
Ich hab mir Muehe beim Lesen der Aufgabenstellung gegeben, aber mein geistiges Auge schafft es einfach noch nicht, das Szenario vor sich aufleben zu lassen :D

Ist es so, dass du
... N Files hast, die N Arrays repraesentieren
... jedes dieser N Arrays aus x*y Elementen besteht
... du jedes dieser Arrays in eine Tabelle einer SQL-DB schreiben willst, um dort auf Rows und Cols Zugriff nehmen zu koennen

oder habe ich da was falsch verstanden? (Es faellt mir mit dieser Anime-MMORPG-Gedudelmusik im Hintergrund hier grade etwas schwer, mich zu konzentrieren, sorry :D)

jives

And the science gets done
Avatar
Registered: Sep 2001
Location: Baden
Posts: 3548
Zitat von that
Wenn du mehrere komplette Spalten brauchst, würde sich vermutlich anbieten, eine Zeile in der DB-Tabelle pro Spalte deiner binären Tabelle zu machen.

Wenn ich das richtig verstanden habe wäre das meine erste Idee:

Code:
+-----------+
|   Files   |
+-----------+
| fid | ... |
+-----------+

+-----------------+
|     Columns     |
+-----------------+
| cid | fid | ... |
+-----------------+

+-------------+
|     Data    |
+-------------+
| cid | Data  |
+-------------+

Wobei fid und cid eindeutige, fortlaufende ID's sind. Die Spalte Data in der Tabelle Data würde dann floats enthalten.
Beispiel: Bei einem File mit 10x1000 Einträgen wird ein Eintrag mit fid 1 in "Files" angelegt, 10 Einträge mit cid 1 - 10 und fid 1 in "Columns" und 10000 Einträge OHNE fortlaufende ID und cid 1 - 10 in "Data". Mit
Code:
SELECT * FROM Data WHERE cid > 0 AND cid < 11
könnte ich dann den gesamten Inhalt des Files mit der fid 1 auslesen.

Das halte ich für eine relativ gute Lösung - was meinst du dazu?

Aus Neugier noch eine Frage: Was ist an einer Lösung mit dynamisch generierten Tabellen verkehrt/schlimm? Intuitiv wollte ich das eh nicht, aber richtigen Grund habe ich keinen finden können.

Zitat von COLOSSUS
Ich hab mir Muehe beim Lesen der Aufgabenstellung gegeben, aber mein geistiges Auge schafft es einfach noch nicht, das Szenario vor sich aufleben zu lassen :D

Ist es so, dass du
... N Files hast, die N Arrays repraesentieren
... jedes dieser N Arrays aus x*y Elementen besteht
... du jedes dieser Arrays in eine Tabelle einer SQL-DB schreiben willst, um dort auf Rows und Cols Zugriff nehmen zu koennen

oder habe ich da was falsch verstanden? (Es faellt mir mit dieser Anime-MMORPG-Gedudelmusik im Hintergrund hier grade etwas schwer, mich zu konzentrieren, sorry :D)
Exakt das will ich :) Problem dabei ist eben, dass das Umwandeln der binären Repräsentation der Arrays nicht ausreichend performt.

Meine obige Lösung war schon früh angedacht, aber dank eines Rechen- und eines Denkfehlers habe ich mir eingebildet in der "Data" Tabelle eindeutige ids zu brauchen, die auch noch schnell zur Neige gegangen wären. Aber bei 2^64 Möglichkeiten sollte das doch eine Zeit lang gehen ;)

COLOSSUS

Administrator
GNUltra
Avatar
Registered: Dec 2000
Location: ~
Posts: 12070
Wenn dich in diesen Arrays immer nur bestimmte Zeilen interessieren, dann waere es vielleicht performanter, diese alle in eine Verzeichnisstruktur zu speichern (es ist generell besser fuer die Performance, nicht ein Directory mit tausenden Files vollzumuellen; ziemlich unabhaengig vom FS. Deshalb solltest du deinen Index, ueber den die einzelnen Files/Arrays adressiert werden, auf Verzeichnis- und Dateinamen aufteilen: DATA/1/7/901.bin statt DATA/17901.bin zum Beispiel), und die einzelnen Files jeweils von einer BDB (Berkeley Database) verwalten lassen. Da kannst du deinen wie auch immer serialisierten Inhalt der Rows (Google Protocol Buffers waeren hier vielleicht eine nuetzliche Sache, wenn PHPs internes serialize() suckt (was ich mal annehme)?) mit deren Row-Nummer als Index ablegen, und wohl auch entsprechend schnell rausholen.

that

Moderator
Hoffnungsloser Optimist
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
Zitat von jives
Code:
+-------------+
|     Data    |
+-------------+
| cid | Data  |
+-------------+

D.h. die Reihenfolge deiner Daten ist irrelevant? Weil die bekommst du so nicht mehr heraus.

jives

And the science gets done
Avatar
Registered: Sep 2001
Location: Baden
Posts: 3548
Vielen Dank an COLO und that für die (wie immer ;)) hervorragenden Inputs :)

@ COLO
An sowas hab ich noch gar nicht gedacht (wär auch nie darauf gekommen). Das scheint eine sehr gute Methode zu sein, die wir uns sicher genau anschauen werden.

@ that
Doch die Reihenfolge ist enorm wichtig. Ich hätte fast vergessen, dass es nicht reicht die Daten in der richtigen Reihenfolge in die DB zu schreiben. Stimmt ja leider nicht, weil ich die ja irgendwie zurück bekommen kann beim Lesen. Ich führe dann wohl am besten eine 64bit-ID mit, nach der ich sortieren kann. Die sollte auch für die nächsten tausend Jahre funktionieren ;)

that

Moderator
Hoffnungsloser Optimist
Avatar
Registered: Mar 2000
Location: MeidLing
Posts: 11338
Dumme Frage #1: Warum gerade PHP? In einer Microsoft-Umgebung würde sich doch .Net anbieten.
Dumme Frage #2: Was macht das PHP-Programm mit den Daten? Davon hängt die passende Datenbankstruktur ab.
Dumme Frage #3: Warum überhaupt Datenbank? Werden die Daten nach dem Laden irgendwann nochmal verändert?

jives

And the science gets done
Avatar
Registered: Sep 2001
Location: Baden
Posts: 3548
#1: Um ehrlich zu sein: Weil das Know-How für .NET nicht vorhanden ist :(
#2: Graphische Darstellung in Form von Diagrammen, die nach Benutzereingaben generiert werden.
#3: Weil zu den Daten ein Haufen Metadaten gehört, die alle mit einander verknüpft sind. Nachdem die Daten in der DB sind, werden unter Umständen die zugehörigen Metadaten verändert.
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz