Spring Batch: Tasklet mit Multi-Thread-Executor hat sehr schlechte Leistungen im Zusammenhang mit Throttling-Algorithmus

8

Mit Spring Batch 2.2.1, ich habe einen Spring Batch Job konfiguriert, habe ich diesen Ansatz verwendet:

Die Konfiguration ist die folgende:

  • Tasklet verwendet ThreadPoolTaskExecutor auf 15 Threads beschränkt

  • throttle-limit ist gleich der Anzahl der Threads

  • Chunk wird verwendet mit:

    • 1 synchronisierter Adapter von JdbcCursorItemReader, damit er von vielen Threads gemäß der Spring-Batch-Dokumentationsempfehlung verwendet werden kann

        

      Sie können den Aufruf von read () synchronisieren, und solange die Verarbeitung und das Schreiben der teuerste Teil des Chunks ist, kann Ihr Schritt noch viel schneller abgeschlossen werden als in einer Konfiguration mit einem einzigen Thread.

    • saveState ist false für JdbcCursorItemReader

    • Ein benutzerdefinierter ItemWriter basierend auf JPA. Beachten Sie, dass die Verarbeitung eines Elements in Bezug auf die Verarbeitungszeit variieren kann. Es kann einige Millisekunden bis wenige Sekunden dauern (& gt; 60s).

    • commit-interval auf 1 gesetzt (Ich weiß, es könnte besser sein, aber es ist nicht das Problem)

  • Alle jdbc-Pools sind in Bezug auf die Spring Batch doc-Empfehlung

  • in Ordnung

Das Ausführen des Stapels führt aufgrund der folgenden Punkte zu sehr seltsamen und schlechten Ergebnissen:

  • Wenn die Elemente einige Zeit brauchen, um von einem Writer verarbeitet zu werden, tun fast alle Threads im Thread-Pool nichts, anstatt zu verarbeiten, nur der langsame Writer funktioniert.

Wenn Sie Spring Batch-Code betrachten, scheint die Ursache in diesem Paket zu liegen:

  • org / springframework / batch / wiederholen / unterstützen /

Ist diese Art zu arbeiten ein Feature oder ist es ein Limit / Bug?

Wenn es sich um ein Feature handelt, wie sieht die Konfiguration aus, um alle Threads zu erstellen, ohne durch lange Verarbeitungszeit ausgehungert zu werden, ohne alles neu schreiben zu müssen?

Beachten Sie, dass, wenn alle Elemente die gleiche Zeit benötigen, alles gut funktioniert und Multithreading in Ordnung ist. Wenn jedoch eine Elementverarbeitung viel länger dauert, ist Multithreading für die Zeit des langsamen Prozesses nahezu nutzlos.

Hinweis: Ich habe dieses Problem behoben:

pmpm 15.08.2013, 22:20
quelle

4 Antworten

5

Wie Alex gesagt hat, scheint dieses Verhalten ein Vertrag nach javadocs von:

zu sein
  

Unterklassen müssen nur eine Methode bereitstellen, die das nächste Ergebnis * abruft und eine, die darauf wartet, dass alle Ergebnisse von gleichzeitig ablaufenden * Prozessen oder Threads

zurückgegeben werden

Schau dir an:

  

TaskExecutorRepeatTemplate # waitForResults

Eine andere Option für Sie wäre Partitionierung:

  • Ein TaskExecutorPartitionHandler, der Elemente von Partitioned ItemReader ausführt, siehe unten
  • Eine Partitioniererimplementierung, die die Bereiche angibt, die von ItemReader verarbeitet werden sollen, siehe ColumnRangePartitioner unter
  • Ein CustomReader, der Daten liest, indem er den Inhalt der Partitionierung verwendet, siehe myItemReader-Konfiguration unter

Michael Minella erklärt das in Kapitel 11 seines Buches Pro Spring Batch :

%Vor%

Partitionierer.java:

%Vor%
    
UBIK LOAD PACK 19.08.2013, 17:17
quelle
3

Hier ist, was ich denke, passiert:

  • Wie Sie bereits gesagt haben, ist Ihr ThreadPoolTaskExecutor auf 15 Threads beschränkt
  • Der "Chunk" des Frameworks bewirkt, dass jedes Element in JdbcCursorItemReader (bis zum Thread-Limit) in einem anderen Thread ausgeführt wird
  • Aber das Spring Batch-Framework wartet auch auf jeden der Threads (dh auf alle 15), um ihren individuellen Lese- / Prozess- / Schreibfluss abzuschließen, bevor sie mit dem Commit-Intervall von 1 zum nächsten Chunk übergehen verursacht 14 Threads, die fast 60 Sekunden auf einen Geschwister-Thread warten, der ewig dauert.

Mit anderen Worten, für diesen Multi-Threading-Ansatz in Spring Batch hilfreich sein, muss jeder Thread in etwa der gleichen Zeit verarbeiten. In Anbetracht Ihres Szenarios, in dem die Verarbeitungszeit bestimmter Elemente sehr unterschiedlich ist, gibt es eine Einschränkung, bei der viele Ihrer Threads abgeschlossen sind und auf einem seit langem laufenden Geschwister-Thread gewartet wird, um auf den nächsten Verarbeitungsabschnitt zugreifen zu können.

Mein Vorschlag:

  • Generell würde ich sagen, dass eine Erhöhung des Commit-Intervalls etwas helfen sollte, da mehr als ein Cursor-Element in einem einzigen Thread zwischen Commits verarbeitet werden sollte, selbst wenn einer der Threads auf einem Long-Running festsitzt schreiben. Wenn Sie jedoch Pech haben, können mehrere lange Transaktionen in demselben Thread auftreten und die Sache verschlimmern (z. B. 120 Sekunden zwischen Commits in einem einzelnen Thread für ein Commit-Intervall von 2).
  • Insbesondere würde ich vorschlagen, die Größe Ihres Threadpools auf eine große Zahl zu erhöhen, sogar Ihre maximalen Datenbankverbindungen um 2x oder 3x zu übertreffen. Was passiert, obwohl einige Ihrer Threads blockieren (wegen der großen Thread-Pool-Größe), eine Verbindung zu erhalten, sehen Sie tatsächlich eine Steigerung des Durchsatzes, da Ihre lang laufenden Threads nicht mehr andere Threads stoppen neue Elemente vom Cursor nehmen und die Arbeit des Batch-Jobs in der Zwischenzeit fortsetzen (zu Beginn eines Chunks übersteigt die Anzahl der ausstehenden Threads die Anzahl der verfügbaren Datenbankverbindungen erheblich. Der OS-Scheduler wird also ein wenig durchdrehen, wenn er Threads aktiviert Diese werden beim Erwerb einer Datenbankverbindung blockiert und müssen den Thread deaktivieren. Da jedoch die meisten Ihrer Threads ihre Arbeit beenden und ihre Datenbankverbindung relativ schnell freigeben, sollten Sie feststellen, dass Ihr Durchsatz insgesamt verbessert wird, da viele Threads weiterhin Datenbankverbindungen erwerben , Arbeit machen, Datenbankverbindungen freigeben und weiteren Threads erlauben, dasselbe zu tun, selbst wenn Ihre lang laufenden Threads ihre Sache machen) .
Alex 19.08.2013 13:50
quelle
1

In meinem Fall, wenn ich das Drosselungslimit nicht setze, kommen nur 4 Threads in die read () -Methode von ItemReader, die auch die Standardanzahl von Threads ist, wenn sie nicht in tasklet-Tags gemäß der Spring-Batch-Dokumentation angegeben ist .

Wenn ich mehr Threads angeben möchte, z. B. 10 oder 20 oder 100, dann kommen nur 8 Threads in die read () - Methode von ItemReader

    
Harsh Gupta 21.09.2013 06:07
quelle
1

Das Limit von 8 aktiven Threads, unabhängig vom Wert von throttle-limit, wird möglicherweise durch Konflikte im Spring Batch-Job-Repository verursacht. Jedes Mal, wenn ein Chunk verarbeitet wird, werden einige Informationen in das Job-Repository geschrieben. Erhöhen Sie die Poolgröße, um die Anzahl der Threads zu berücksichtigen, die Sie benötigen!

    
Filoche 08.01.2016 14:44
quelle