Von Zeit zu Zeit brauche ich Herausforderungen. Gerade steht wieder eine an und zwar ein High-Performance-System, das mit großen Datenmengen arbeiten soll. Einer der Knackpunkte ist die Gruppierung von zur Verarbeitung anstehenden Datensätzen einer MySQL-Datenbank, doch ich wünschte, es wäre eine MongoDB, die nativ Arrays und MapReduce unterstützt.
Gegeben ist eine Liste von Datensätzen, bei denen zwei Felder besonders interessant sind. Beide enthalten jeweils eine Liste von Einträgen aus einer mehr oder weniger begrenzten Gruppe. Da die tatsächlichen Daten Firmen-internas sind, muss ein Beispiels genügen:
id | A | B |
---|---|---|
1 | foo, bar | eins, zwei, drei |
2 | bar, baz | vier, fünf, drei |
3 | foo, baz | eins, vier, fünf |
4 | foo | eins, drei, vier |
5 | foo, bar, baz | eins, drei |
Für die Verarbeitung wird jeweils ein beliebiger Wert aus A und B benötigt (also beispielsweise "foo" und "eins" für id 1, 3, 4 und 5). Welcher der zur Auswahl stehenden Werte Anwendung findet, ist dabei nicht relevant.
MySQL unterstützt keine Arrays, also bleibt nur, die Werte der Spalten A und B als Liste mit Trennzeichen in einem VARCHAR zu speichern oder eine entsprechende Mappingtabelle je Spalte zu erstellen.
Das Grundproblem bleibt aber unabhängig von der Speicherart bestehen: Für die Abwicklung werden möglichst gleichmäßige Gruppen benötigt und halbwegs effizient soll die Auswahl nebenbei auch noch erfolgen.
Ginge es nur um einen Wert, könnte ich in einer Mappingtabelle alle id + Wert - Kombinationen abspeichen und dort einfach gruppieren. Dann müsste zwar noch ausgeschlossen werden, dass ein id (der in mehreren Gruppen vorkommen wird), mehrfach verarbeitet wird, aber mit einem entsprechenden Locking ist das sogar Prozess- und Serverübergreifend kein ernsthaftes Problem.
Bei zwei Gruppen gibt es allerdings eine viel größere Anzahl von möglichen Kombinationen. Nur die fünf Zeilen im Beispiel ermöglichen schon eine breite Auswahl:
- foo, eins
- foo, zwei
- foo, drei
- foo, vier
- foo, fünf
- bar, eins
- bar, zwei
- bar, drei
- bar, vier
- bar, fünf
- baz, eins
- baz, drei
- baz, vier
- baz, fünf
In der Realität gibt es allerdings etwa 400 verschiedene Werte für A und mindestens einige tausend für B. Die Tabelle umfasst dann auch einige Hunderttausend oder sogar Millionen Zeilen. Die Anzahl der möglichen Kombinationen will ich gar nicht erst ausrechnen...
Bisher habe ich noch keine auch nur halbwegs schöne Lösung gefunden. Viel mehr, als nach dem häufigsten A zu suchen und dann alle möglichen B's zu gruppieren um dort am Ende wieder den häufigsten Wert zu nutzen, ist mir leider noch nicht eingefallen, aber wer weiß, vielleicht kommt die Erleuchtung über Nacht (oder ein netter Kommentator steuert die zündende Idee bei).
4 Kommentare. Schreib was dazu-
Mirko
20.10.2013 22:32
Antworten
-
Sebastian
21.10.2013 7:36
Antworten
-
Mirko
21.10.2013 11:49
Antworten
-
Tamaro
21.10.2013 1:42
Antworten
Ich hab solches Design schon bei mehreren Unternehmen im Einsatz gesehen und überall macht dieses Datenbankdesign Schwierigkeiten.
Das Problem ist, dass die Daten für die Verarbeitung mit der Datenbank in einer "nicht optimalen" Form gespeichert sind -- offensiv würde ich sagen: nicht richtig normalisiert.
Theoretisch betrachtet ist ein Array nichts anderes als eine Liste und das ist wiederum eine Eindimensionale Tabelle, und mit Tabellen kann die MySQL Datenbank sehr wohl umgehen (auch wenn es da andere Aussagen gibt ;-)). Ich würde für A und B eine weitere Tabelle anlegen wo jeder Eintrag in der bestehenden Tabelle eine Referenz ist, dann ist es ziemlich einfach zu ermitteln "Jeder Eintrag wo A = "foo" und B = "eins" ist".
Die Kehrseite der Medaille ist, dass die Software auf diese Strukturen eingehen muss anstatt man einfach dem User die Möglichkeit gibt eine Komma Separierte Liste einzugeben. Im Endeffekt spart man sich aber ne menge Ärger was die Qualität der Datenhaltung angeht und eben auch eben die Datenbank mit den Muskeln spielen kann und dadurch die Performance steigt.
Genau das meinte ich mit "Mappingtabelle", aber auch dabei bleiben mehrere Einträge einem Datensatz zugeordnet, die (für Menschen optimierte) Darstellung im Beispiel bleibt also gültig. Die Daten werden komplett von Software generiert, ich würde mich nicht drauf verlassen wollen, dass ein User eine Liste in richtiger Form eingibt :)
Durch die Normalisierung wird das Grundproblem der Gruppierung allerdings auch nicht gelöst, weil am Ende immer eine n-zu-n-Beziehung zwischen A und B bleibt.
MongoDB kann wirklich mit Arrays arbeiten, dabei werden alle Elemente einzeln indiziert und können gesucht werden.
Das "Grundproblem der Gruppierung" habe ich noch nicht verstanden.
Ich nenne mal zwei Schlüsselworte: Zusätzliche Indexierung der Felder für die Ordnung und binäre Suche innerhalb der Datensätze/Felder.
Ich weiß aber nicht, ob das bei diesem Problem hier weiterhelfen könnte.