![]() |
| | LinkBack | Themen-Optionen | Thema durchsuchen |
| | Nach oben #1 |
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
|
Hallo Javanesen Multithreading ist ja ein schöner Bestandteil von Java. Da gibt's tolle Stichworte wie "Parallelität", was soviel bedeutet wie "vermeide soviel sequenzielle Arbeit, wie nur möglich" und vieles mehr. Ich stehe im Moment vor einer Designfrage für eine Main-Methode. Ein bisschen was zur Problematik: Ich muss Daten aus einer Datenbank entnehmen. Diese Daten werden portioniert aus der Datenbank geholt (z.B. immer 1.000 Datensätze) und anschließend in einem eigenen Objekt verarbeitet und sollen danach in eine andere Tabelle der Datenbank geschrieben werden. Designüberlegungen: a) Ich habe einen oder mehrere Producer und einen oder mehrere Consumer. Die Producer holen die Daten aus der Datenbank, die Consumer nehmen sich die Daten der Producer, verarbeiten sie und schreiben sie in die neue Datenbanktabelle.Im Falle von a) und b) würden die Producer die Arbeit der Data- und SortingProducer aus c) übernehmen. Welchen Ansatz haltet ihr für den besten? EDIT: Das DB-Design konnte ich überarbeiten. Das Ganze ist jetzt mit einem Join möglich. Vielen Dank! Geändert von Sekundentakt (07.02.2010 um 16:14 Uhr) |
| | |
| | Nach oben #3 | ||
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
| Zitat:
Zitat:
Grundsätzlich gings mir aber um das Design der Producer-Threads und Consumer-Threads, um einen möglichst hohen Datendurchsatz zu erreichen. Ich hoffe, dass das Problem jetzt etwas klarer ist. | ||
| | |
| | Nach oben #4 |
| fka Gottzilla Registriert seit: 02.02.2005 Ort: Würzburg
Beiträge: 753
|
Generell wäre es natürlich am schönsten, wenn du alles in einem SQL unterbringen könntest - Auslesen, Aufbereitung und Einfügen. Je nach Komplexität der Aufbereitung wäre das natürlich nicht gerade trivial. Mehrere Producer, wie du sie nennst, machen eher weniger Sinn. Du hast trotzdem nur eine Datenbank im Hintergrund laufen, die auch nicht schneller fertig wird, wenn 50 Threads darauf zugreifen. Mehrere Consumer macht nur dann Sinn, wenn du einen entsprechenden Multicore-Prozessor hast, und die Verarbeitung der Daten deutlich mehr Zeit in Anspruch nimmt, als das Auslesen. Generell könntest du noch eine weitere "Schicht" (das Wort passt hier zwar nicht 100prozentig, aber ich denke du weißt, was gemeint ist) einführen. Und zwar die, die die Daten wieder in die DB schreibt => Leser, Verarbeiter, Schreiber. Ich seh jetzt nicht so den Sinn, warum der Aufbereiter die Daten auch wieder schreiben sollte, wenn es auch einen separaten Leser gibt. |
| | |
| | Nach oben #5 |
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
|
So, die Woche über war ich nicht erreichbar :). Klar, das in drei Teilprozesse aufzugliedern macht an dieser Stelle schon Sinn. :) Zur Datenbank: Ich nahm immer an, dass seitens der DB für jeden Lesezugriff ein einzelner Thread bei Bedarf gestartet wird. Bist Du dir da sicher, dass die das wirklich sequenziell abarbeiten??? |
| | |
| | Nach oben #6 |
| fka Gottzilla Registriert seit: 02.02.2005 Ort: Würzburg
Beiträge: 753
|
Kommt natürlich auf die Datenbank und ggf. die Konfiguration an. Und da du auch nicht schreibend darauf zugreifst, sollten die Tabellen auch nicht gesperrt werden. Aber wenn die DB mit einer Abfrage beschäftigt ist, dann wird sie mit der Abfrage nicht schneller fertig, wenn noch ein, zwei oder drei weitere Abfragen reinkommen. Stattdessen wird sie eher länger pro Abfrage benötigen. Kannste dir ganz leicht an nem Beispiel klar machen. Wenn du gleichzeitig ein Java-Programm für einen Kunden und die Hausaufgaben für die Schule machen musst, dann bist du zwar prinzipiell in der Lage beides gleichzeitig zu machen (wenn auch viel ineffektiver als ein Computer), aber im Endeffekt (wenn du so effizient wie ein Computer wärst) würdest du genau so lange für beides benötigen, wenn du das nacheinander gemacht hättest. Anders schaut es natürlich aus, wenn du Hausaufgaben machen musst, Kaffee kochen und 2000 Seiten kopieren. Du stellst vermutlich als erstes den Kopierer ein, der dann die Arbeit von alleine macht, als nächstes die Kaffeemaschine, die die Arbeit dann auch von alleine macht, und kümmerst dich dann um die Hausaufgaben. Ist vermutlich sehr viel schneller, als wenn du wartest, bis der Kopierer fertig ist, dann den Kaffee kochst, wartest bis die Maschine fertig ist, und dann mit deinen Hausaufgaben beginnst ;) . Wenn du also ein Single-Core System hast, nutzen dir Threads nur etwas, wenn die jeweiligen Arbeiten unterschiedliche Ressourcen beanspruchen. Alle Primzahlen in einem bestimmten Bereich zu bestimmen geht nicht schneller, wenn du den Bereich in zwei Bereiche unterteilst, die du dann parallel berechnen lässt (beansprucht ja beides ausschließlich CPU). Es macht jedoch durchaus Sinn, einen großen Festplattenzugriff, eine Anfrage an einen Server und eine komplexe Berechnung in jeweils einen Thread auszulagern, da alle drei Aufgaben unterschiedliche Ressourcen beanspruchen würden. Anders sieht es bei einem Mulitcore-System aus. Da hier wirklich mehrere physikalisch getrennte Recheneinheiten vorhanden sind, macht es hier auch Sinn, die Primzahlen getrennt voneinander zu berechnen. Vergleichbar damit, dass du einen Kollegen neben dir stehen hast, der für dich die Hausaufgaben macht, während du dich um das Java Programm kümmerst. Zugriffe von mehreren Threads auf eine DB macht also nur dann Sinn, wenn die Datenbank auch auf einem Multicore-System läuft. Und selbst dann ist es fraglich, ob mehrere Threads wirklich sinnvoll sind. Aber das kommt wieder auf die Arbeitsweise der DB an. |
| | |
| | Nach oben #7 |
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
|
Prinzipiell erzählst Du mir da nichts Neues ;). Wenn Du aber weißt, dass Du portioniert immer ungefähr 1.000 Datensätze aus einer DB lesen sollst, würde es schon Sinn machen, wenn Du mehrere Leute an verschiedene Stellen setzt. Du weißt, Deine IDs sind nummerisch und unique, also kannst Du portioniert Daten abfragen. Klar, vielleicht gibt es den Datensatz mit der ID 2.000 nicht mehr, aber dann kannst Du nachsehen, ob es eine 1.999 gibt. Das Paket ist dann zwar kleiner, aber du kannst es trotzdem verteilt abfragen. Alternativ könnte man jetzt auf die Idee kommen mit der LIMIT-Klausel zu arbeiten. Jeder, der glaubt, dass er einen Leser auf Datensatz 1 und einen anderen Leser auf Datensatz 50.000 setzen kann, sollte das mal versuchen und feststellen, dass es einen marginalen oder nichtvorhandenen Geschwindigkeitsvorteil gibt. Anders natürlich, wenn man das über IDs macht. Ansonsten hast Du Recht. :) Leider wird immer noch häufig versucht, Multithreading mit Single-Core-Abläufen zu erklären, was im Zeitalter der Mehrkernprozessoren irreführend ist. Sobald man mehrere Prozessoren hat, wird tatsächlich parallel gearbeitet, sofern man denn entsprechend parallelisierbare Anwendungen schreibt. |
| | |
| | Nach oben #8 | |||
| fka Gottzilla Registriert seit: 02.02.2005 Ort: Würzburg
Beiträge: 753
| Zitat:
Zitat:
Um es noch einmal zusammen zu fassen: Du fragtest, ob parallele Zugriffe auf eine DB Sinn machen. Ich antwortete, dass es darauf ankommt, ob die DB auf einem Multicore-System liegt und ob die DB dieses auch ausnutzt/ausnutzen kann. Es heißt ja nicht, dass die DB auch mehrere Threads eröffnet, nur weil du parallel anfragst. Außerdem nutzen dir mehrere CPUs wenig, wenn die meiste Zeit damit verbracht wird, die Daten aus dem Arbeitsspeicher, Cache oder gar von der Festplatte zu lesen. Zumindest weiß ich jetzt schon einmal, dass du vermutlich eine MySQL DB verwendest (Deine letzten Threads + LIMIT-Befehl ;) ), was mir aber nicht weiterhilft, da ich nicht weiß, wie MySQL mit den von mir angeführten Kritikpunkten umgeht. Solltest du vielleicht mal in Erfahrung bringen :) . Da ich nach wie vor keine Ahnung habe, wie sich deine Aussagen auf meine Antworen beziehen, gehe ich einfach mal davon aus, dass ich in allen Punkten recht habe Zitat:
| |||
| | |
| | Nach oben #9 | ||
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
| Zitat:
Gesetz dem Fall die Daten liegen nicht auf Platte, sondern in einer Memory-Table, dann macht es durchaus Sinn bei 4mio Datensätzen einen Producer an den Anfang der Tabelle zu setzen und von dort aus portioniert Daten aus der Tabelle zu holen und einen Producer in die ungefähre Mitte der Tabelle zu schicken und selbiges dort zu tun. Du kannst dann z.B. sagen "selektiere alle Datensätze mit einer ID größer als x LIMIT 0, 1000", wobei x die ID des tausendsten bzw. letzten Datensatzes der letzten Ergebnismenge ist. Soweit ich weiß kann man nämlich auf den Arbeitsspeicher frei zugreifen, anders als bei einer Festplatte. Insofern nutze ich zwar nicht unbedingt die Eigenschaften eines Multicores aus, das ist richtig, aber sinnvoll wäre es in diesem Falle schon. Zitat:
Zur LIMIT-Klausel: Zwei Beispiele: LIMIT 0, 1000 -> zeigt Dir die ersten 1.000 Ergebnisse deiner Abfrage an. LIMIT 100, 1000 -> überspringt die ersten 100 Ergebnisse und zeigt dann die darauf folgenden 1.000 Ergebnisse an. Wenn ich jetzt sage LIMIT 50000, 1000 überspringt er die ersten 50.000 Ergebnisse und zeigt mir die darauf folgenden 1.000 an. Das bringt mir aber kaum Geschwindigkeitsvorteile, weil die ersten 50.000 Ergebnisse trotzdem berechnet werden müssen und nur nicht zurückgegeben werden. Anders wäre es, wenn ich die ID nehme und sowas mache wie SELECT spalte FROM tabelle WHERE ID > 1 LIMIT 0, 1000 oder SELECT spalte FROM tabelle WHERE ID > 50000 LIMIT 0,1000 Hier nutze ich den Index aus, was mir tatsächlich Geschwindigkeitsvorteile bietet. | ||
| | |
| | Nach oben #10 | |||
| fka Gottzilla Registriert seit: 02.02.2005 Ort: Würzburg
Beiträge: 753
|
Ich glaub so wird das nichts Zitat:
Zitat:
Du kannst ja auch einfach deinen Code einmal mit und einmal ohne Threads durchlaufen lassen und die Ergebnisse vergleichen. Aber Achtung: Viele Datenbanken cachen Anfragen bzw. spielt der Java JIT Compiler auch eine Rolle bei Mikrobenchmarks. | |||
| | |
| | Nach oben #11 | |
| Sesselkleber Registriert seit: 17.01.2005
Beiträge: 626
| Zitat:
Mehr hier: Speicherzugriff ? Wikipedia Und deine Datenbank wird doch sicher nicht auf einem Magnetband liegen. Gruß Sparrow | |
| | |
| | Nach oben #12 | |||
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
| Zitat:
Zitat:
Zitat:
Bei der Festplatte hängt die Lesegeschwindigkeit von den Umdrehungen der Platte ab, der Zugriff auf den Arbeitsspeicher ist dagegen frei ansteuerbar. RAM | |||
| | |
| | Nach oben #13 | |
| Sesselkleber Registriert seit: 17.01.2005
Beiträge: 626
| Zitat:
Sowohl bei Fesplatten als auch beim RAM handelt es sich um wahlfreien Speicher. Im Groben: du kannst eine bestimmte Adresse direkt anspringen. Die "Lesegeschwindigkeit" hat damit aber nichts zu tun. Die ist, bedingt durch Achitektur und physikalischen Eigenheiten, beim RAM extrem schnell und bei der Fesplatten dagegen eher langsam (im direkten Vergleich). Hier muss man aber unterscheiden zwischen der Zugriffsgeschwindigkeit und der Übertragunggeschwindigkeit. Beides hängt aber nicht direkt von der Umdrehungsgeschwindigkeit der Scheiben ab. Die Geschwindigkeit der Datenübertragung ließe sich durch schneller drehende Scheiben steigern (in der gleichen Zeit rauschen mehr Daten am Kopf vorbei), in der Regel wird aber einfach die Dichte der Daten erhöht, das führt zum gleichen Effekt. Die Zugriffsgeschwindigkeit: Ich habe früher in einem Werbestudio gejobbt. Da war Internet noch out und 200 MB Festplatten riiiieeeeesig. Und die hatten damals für Fotozeugs eine Platte die war auf schnellen Datenzugriff optimiert indem sie viel mehr Scheiben hatte als die 'normalen' Festplatte. Dadurch hatte sie auch mehr Lese-/Schreibköpfe und somit im Zugriff schneller, da diese schneller an der benötigten Position waren. So, zurück zu deiner ersten Frage: Überleg dir mal was eine Datenbankabfrage auf der Datenbank tatsächlich macht. Vor allem wenn ein Index auf der einschränkenden Spalte liegt. Gruß Sparrow | |
| | |
| | Nach oben #14 | |
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
| Zitat:
Was die Hardware aber allgemein angeht, kann ich nicht wirklich viel mitreden, dafür lernt man immer wieder was dazu. Danke für die Erläuterungen! | |
| | |
| | Nach oben #15 |
| Sesselkleber Registriert seit: 17.01.2005
Beiträge: 626
|
Ich will hier auch nicht zu Oberlehrerhaft rüberkommen ;) Also ich sehe das mit der Datenbankabfrage in etwa so: SQL-Anfrage kommt beim Server an. Server untersucht die Abfrage (Syntax, Tabellen/Spalten vorhanden, etc.) Server erstellt einen Plan für die Abfrage Server führt Abfrage gemäß Plan aus Server liefert ein Resultat bestehend aus Tupel über die iteriert werden kann. Je nach Datenbank und Art der Abfrage ist es übrigens unterschiedlich wann tatsächlich der erste Datensatz zurück kommt. Entweder dann wenn der erste Datensatz gefunden wurde oder sobald die komplette Abfrage abgearbeitet wurde. So, zurück zu deiner Problemstellung. Es kommt tatsächlich stark auf die Umgebung an in der das DBMS eingesetzt wird. Um sich Gedanken darüber machen zu können wie man das ganze Beschleunigen kann müsste man zuerst herausfinden wo genau eigentlich der Flaschenhals sitzt der überhaupt Zeit kostet. Mir fällt auf jeden Fall im Augenblick nicht ein warum das Abfeuern von 2 Abfragen das Auslesen Beschleunigen sollte, denn dabei handelt es sich ja in der Regel nicht um einen 'teuren' Prozess was das Rechnen angeht. Wenn dann wartet das System wegen I/O-Sachen, und das wird durch mehrere gleichzeitige Abfragen vom gleichen Client mit Sicherheit nicht weniger. Aber ausprobiert habe ich das natürlich gerade nicht ;) Gruß Sparroe |
| | |
| | Nach oben #17 |
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
|
Hallo Gemeinde, ich bin da gerade auf ein grundlegendes Verständnisproblem meinerseits gestoßen. Im Augenblick versuche ich folgendes: Ich erzeuge verschiedene Runnable-Instanzen: Code: Runnable firstTask = new Runnable() {
@Override
public void run() {
//...
}
Runnable secondTask = new Runnable() {
@Override
public void run() {
//...
}
Runnable lastTask = new Runnable() {
@Override
public void run() {
//...
}
Wenn der Rückgabewert aber null ist, wird das Ganze noch ein paar mal probiert, bis dann endgültig via break; die Schleife verlassen wird. Ist der Thread, welcher diesen Task ausführt (aufgerufen via executorService.execute(anyTask)) dann auch grundsätzlich schon beendet, weil nichts mehr passiert? Ich frage mich nämlich momentan, wie ich den Thread, welcher den Task - der im null-Fall nichts mehr zu tun hat - killen kann, ohne über executorService.shutdown() aufzurufen. Der Aufruf würde nämlich alle Tasks beenden. Dankeschön! :) |
| | |
| | Nach oben #18 | |||
| fka Gottzilla Registriert seit: 02.02.2005 Ort: Würzburg
Beiträge: 753
| Haha Warum gehst du über einen ExecutorService? Ich arbeite seit über 5 Jahre mit Java und habe noch nie in meinem Leben einen ExecutorService eingesetzt ;) . Zitat:
Zitat:
Zitat:
Ein Tutorial zum Thema Threads von Sun: Processes and Threads (The Java™ Tutorials > Essential Classes > Concurrency) Kleiner Nachtrag, nicht dass das falsch verstanden wird: Ein ExecutorService macht bei mehreren Threads schon Sinn, aber braucht man imho eben nicht so oft. Bzw. ich nicht, da ich sowieso größtenteils mit EE oder ME Arbeite, und es da so etwas ohnehin nicht gibt/geben sollte ;) Geändert von The_S (19.02.2010 um 08:48 Uhr) | |||
| | |
| | Nach oben #19 | |||||
| Erfahrener Benutzer Registriert seit: 16.08.2008 Ort: Mecklenburg-Vorpommern
Beiträge: 314
| Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
| |||||
| | |
| | Nach oben #20 | ||
| fka Gottzilla Registriert seit: 02.02.2005 Ort: Würzburg
Beiträge: 753
| Zitat:
Zitat:
| ||
| | |
![]() |
| Lesezeichen |
| Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1) | |
| Themen-Optionen | Thema durchsuchen |
| |