Ich benutze Gpars, um eine 250M Zeile MySQL-Datenbanktabelle parallel zu verarbeiten. Ich erstelle 8 gpars threads, 8 unabhängige Datenbankverbindungen und teile die Daten so auf, dass jeder Thread unabhängig auf verschiedenen Reihen von Reihen operiert ... eine Art billiges MapReduce-Konzept. Im Kern ist die Logik wie folgt aus:Groovy App mit Gpars verlangsamt nach vielen Iterationen
withExistingPool(pool)
{
connection_array.collectParallel()
{
// Figure out which connection this thread can use.
// We use the index into the array to figure out
// which thread we are, and this tells us where to
// read data.
int i
for (i = 0; i < connection_array.size(); i++)
if (it == connection_array[i])
break
// Each thread runs the same query, with LIMIT controlling
// the position of rows it will read...if we have 8 threads
// reading 40000 rows per call to this routine, each thread
// reads 5000 rows (thread-0 reads rows 0-4999, thread-1 reads
// 5000-9999 and so forth).
def startrow = lastrow + (i * MAX_ROWS)
def rows = it.rows("SELECT * ... LIMIT ($startrow, $MAX_ROWS)")
// Add our rows to the result set we will return to the caller
// (needs to be serialized since many threads can be here)
lock.lock()
if (!result)
result = rows
else
result += rows
lock.unlock()
}
}
Der Code funktioniert zunächst groß, mich mehr als 10.000 Zeilen pro Sekunde geben, wenn es beginnt. Aber nach ein paar Millionen Zeilen beginnt es langsamer zu werden. Bis wir 25 Millionen Zeilen anstatt 10.000 Zeilen pro Sekunde haben, bekommen wir nur 1.000 Zeilen pro Sekunde. Wenn wir die App beenden und von dem Punkt aus starten, an dem wir aufgehört haben, geht sie für eine Weile wieder auf 10K Zeilen pro Sekunde zurück, aber sie wird immer langsamer, wenn die Verarbeitung fortgesetzt wird.
Es ist ausreichend Prozessorleistung verfügbar - dies ist ein 8-Wege-System und die Datenbank ist über ein Netzwerk, so dass es ziemlich viel Wartezeit gibt, egal was passiert. Die Prozessoren laufen im Allgemeinen nicht mehr als 25-30% beschäftigt, während dies ausgeführt wird. Scheint auch keine Speicherlecks zu sein - wir überwachen Speicherstatistiken und sehen keine Änderung, sobald die Verarbeitung läuft. Der MySQL-Server scheint nicht gestresst zu sein (er läuft anfangs zu etwa 30% ausgelastet und sinkt, wenn die App langsamer wird).
Gibt es irgendwelche Tricks, die dazu beitragen können, dass diese Art von Dingen bei einer großen Anzahl von Iterationen konsistenter ausgeführt wird?
Es ist wahrscheinlich, weil Sie ständig die Größe der Ergebnisliste ändern. Haben Sie versucht, eine Anfangsgröße für das Ergebnis anzugeben? Sie zeigen nicht, wie/wo es initialisiert wird –
Sie können wahrscheinlich auch '(0 ..
@ tim - danke für die Vorschläge ... Ihr erster Kommentar ist ein guter - da wir wissen, wie viele Zeilen abgerufen werden, können wir das Ergebnis vorab zuordnen und als Parameter übergeben, anstatt es jedes Mal dynamisch zu erstellen. Dies half der Performance um fast 5%.Der zweite Vorschlag erwies sich als etwas langsamer als unser ursprünglicher Ansatz - ich denke, es dauert nicht lange, ein 8-Item-Array zu durchsuchen. Und leider existiert das ursprüngliche Problem immer noch ... die Routine wird immer langsamer, je mehr Datensätze sie verarbeitet. –