Blog

Kleine Daten: Workflow, lange Transaktionen und DB2

Gerard van de Glind

Aktualisiert Oktober 22, 2025
4 Minuten

Nach all den Big-Data-Beiträgen nun etwas über traditionelles SQL, das mit DB2 9 läuft. Wir hatten eine Web-Anwendung erstellt, die im Grunde eine Ansicht für eine Datenbank war. Die Anwendung zeigte den Inhalt einer Datenbank an, nachdem der Benutzer Suchparameter eingegeben hatte, oder alles, wenn kein Parameter eingegeben wurde. Die Datenbank enthielt ein paar hunderttausend Datensätze. Alles in allem sehr einfach. Die Software funktionierte in unserer Testumgebung einwandfrei. Aber als wir die Software in der Produktion einsetzten, zeigte sie kaum noch Daten an. In den Protokollen fanden wir häufige Sperrfehler wie folgt: 15:23 ERROR (org.hibernate.engine.jdbc.spi.SqlExceptionHelper) - DB2 SQL Error: SQLCODE=-911, SQLSTATE=40001, SQLERRMC=68, DRIVER=4.1 Eine Suche im Internet ergab, was dieser Fehler bedeutete: ein Deadlock oder Timeout ist aufgetreten. Wir wussten, dass auf dieselbe Datenbank auch eine andere Anwendung zugriff, nämlich Activiti, die in BPMN geschriebene Prozesse ausführt. Die Prozesse in der Activiti-Engine fügten nur ein paar Datensätze pro Minute ein, so dass wir nicht davon ausgingen, dass diese Anwendung einen großen Einfluss hatte. Doch wie können ein paar Einfügungen pro Minute einen so großen Einfluss haben? Nach einigen Gesprächen mit einem DBA und eigenen Experimenten fanden wir das Problem heraus, das wir hier beschreiben werden.

Wie gesagt, die andere Anwendung war eine Activiti, die Prozesse ausführte (um genau zu sein: Prozessdefinitionen in BPMN). Bei mehreren Prozessschritten wurde Java-Code durch den Delegatenmechanismus von Activiti ausgeführt. Dieser Java-Code würde Daten in unsere Datenbank einfügen. Schermafbeelding 2013-02-12 om 16.45.21 Ein DB2-DBA erzählte uns, dass, wenn Sie während einer Datenbanktransaktion einen neuen Datensatz einfügen, dieser Datensatz so lange gesperrt ist, bis Sie die Transaktion durch einen Commit oder Rollback freigeben. Das wussten wir natürlich schon. Er erklärte uns aber auch, dass andere Transaktionen immer noch von dieser Sperre beeinflusst werden können: Ein SELECT ohne oder mit breiter WHERE-Klausel muss warten, bis die erste Transaktion beendet ist. Der Grund dafür: DB2 sperrt nicht nur den Datensatz, sondern auch einen Teil des Tablespace, in den dieser Datensatz gerade eingefügt wird. Während das Einfügen dieser Daten nur sehr wenig Zeit in Anspruch nehmen würde, könnte die Ausführung der gesamten Prozessausführung 5 bis 20 Sekunden dauern. Der Grund für die lange Dauer: Ein Teil der Schritte beinhaltete den Aufruf anderer Webservices, die nicht immer schnell reagierten. Da erst am Ende des Prozesses ein Commit durchgeführt wurde, bestand für einen Teil der Datenbank bis zu 20 Sekunden lang eine Sperre. Das bedeutet, dass unsere Konsole keine Daten zurückgeben konnte, wenn der andere Prozess noch beschäftigt war, wie unten dargestellt:
ZeitProzess a(ctiviti)Prozess c(onsole)
1INSERT etwas INTO mytableProzess a ist einsatzbereit
2SELECT * FROM mytableProzess c muss auf Prozess a warten
3INSERT etwas INTO myothertableProzess c muss noch warten
4ÜbertragenProzess a überträgt, schließlich kann Prozess c fortfahren
5übertragenProzess c ist nun auch beendet
Wir haben es ausprobiert, indem wir in unserer Konsole nach Primärschlüsseln gesucht haben. Dann reagierte unsere Konsole sofort, wie erwartet: Denn dann ist nur eine Sperre für einen kleinen Teil der Tabelle erforderlich.Um das Problem zu umgehen, beschlossen wir, aus jedem Delegaten heraus zu committen, also nach jedem Schritt im Prozess. Wir haben das natürlich mit dem Product-Owner abgeklärt: Für die Konsistenz war es kein Problem, dass einige Daten während der Ausführung des Prozesses sichtbar waren. DB2 unterstützt keine verschachtelten Transaktionen, so dass in unserem Delegaten eine neue dbsession geöffnet (und geschlossen) werden musste:
EntityManagerFactoryImpl entityManagerFactory = (EntityManagerFactoryImpl) Persistence.
  createEntityManagerFactory(properties.getProperty("meinEinheitsname");
sessionFactory = entityManagerFactory.getSessionFactory();
Session session = sessionFactory.openSession();
session.getTransaction().begin();
...
session.getTransaction().commit();
session.close();
Hinweis: Wenn Sie Spring-Transaktionen verwenden, übernimmt Spring den obigen Textbaustein für Sie. Die Sperren wurden nun für eine viel kürzere Zeit gehalten. Nach dieser Änderung verhielt sich unsere Konsole normal. Activiti mit Commit Das Verhalten des Prozesses, wie oben aufgeführt, war in der Tat von vornherein erwünscht. Nach allen Schreibvorgängen gab es im ursprünglichen Code eine Flush-Anweisung. Bei DB2 würde diese Anweisung tatsächlich dazu führen, dass die Daten auf die Festplatte geschrieben werden, aber die Daten wären erst sichtbar, wenn der Commit durchgeführt wird.

Verfasst von

Gerard van de Glind

Contact

Let’s discuss how we can support your journey.