Blog

DDL in Transaktionen: was fünf Datenbanken wirklich können

· Datenbanken · Praxis

Es gibt eine Annahme, die in Migrationsskripten erstaunlich oft steckt und in etwa der Hälfte der Fälle falsch ist: dass eine Schemaänderung sich zurückrollen lässt, wenn etwas schiefgeht.

Ob das stimmt, hängt allein von der Datenbank ab. Und die Unterschiede sind größer, als man erwartet.

Der Befund

Wir unterstützen mit RayMigrator fünf Engines und mussten das für jede beantworten. Das ist das Ergebnis:

Engine DDL in Transaktionen Schemas Bezeichner-Quoting Blocktrenner
SQL Server ja ja [ ] GO
PostgreSQL ja ja " " ;
SQLite ja nein " " ;
MySQL nein nein ` ;
MariaDB nein nein ` ;

Die Spalte ganz links ist die entscheidende. Sie bestimmt, was im Fehlerfall passiert.

Warum MySQL und MariaDB anders sind

Beide führen bei DDL-Anweisungen ein implizites Commit aus. Sobald ein CREATE TABLE oder ALTER TABLE abgesetzt wird, ist die laufende Transaktion beendet — und zwar erfolgreich, unabhängig davon, was danach passiert.

Ein Migrationsskript mit fünf Anweisungen, das bei der vierten scheitert, hinterlässt dort also drei angewandte und eine fehlgeschlagene Änderung. Ein ROLLBACK an dieser Stelle bewirkt nichts. Die Datenbank steht in einem Zwischenzustand, den kein Skript vorgesehen hat.

Bei PostgreSQL und SQL Server ist derselbe Fall unspektakulär: Die Transaktion wird zurückgerollt, das Schema steht wie vorher, die Migration kann nach der Korrektur erneut laufen.

Was das für Migrationen bedeutet

Auf MySQL und MariaDB muss jede Migration für sich vorwärtskompatibel sein. Nicht „das Skript läuft ganz oder gar nicht”, sondern „jede einzelne Anweisung muss auch dann sinnvoll sein, wenn die folgende scheitert”.

Praktisch heißt das:

  • Eine Anweisung pro Datei, statt fünf zusammenhängende in einer
  • Schreibweisen bevorzugen, die ein zweites Ausführen vertragen — CREATE TABLE IF NOT EXISTS statt CREATE TABLE
  • Datenumzüge von Strukturänderungen trennen; DML ist auch dort transaktional
  • Bei kritischen Änderungen den Weg über eine neue Tabelle gehen: anlegen, befüllen, umbenennen. Jeder Schritt für sich ist unkritisch

Auf PostgreSQL und SQL Server kann eine Migration größer sein — aber nur, wenn das Werkzeug die Transaktion auch tatsächlich aufspannt. Ein Werkzeug, das jede Anweisung einzeln absetzt, verschenkt den Vorteil.

Der Fehler, den Werkzeuge machen

Die verbreitete Abkürzung ist, sich auf die eigene Entwicklungsdatenbank zu verlassen. Wer auf PostgreSQL entwickelt und auf MySQL ausliefert, testet das Rollback-Verhalten nie unter den Bedingungen, unter denen es zählt.

Noch unangenehmer sind Werkzeuge, die den Unterschied verschweigen und überall dieselbe Zusicherung geben. Sie erzeugen ein Sicherheitsgefühl, das auf zwei von fünf Engines nicht gedeckt ist.

RayMigrator macht die Fähigkeit deshalb explizit: Jede Engine meldet, ob sie DDL transaktional ausführen kann, und die Transaktionssteuerung lässt sich pro Migration konfigurieren — im Rahmen dessen, was die jeweilige Datenbank hergibt.

Die Nebenspalten sind auch nicht egal

Die übrigen Spalten der Tabelle sehen nach Kleinkram aus, kosten in der Praxis aber Zeit:

Schemas gibt es nur bei SQL Server und PostgreSQL. Wer Migrationen schreibt, die schemaqualifizierte Namen verwenden, kann sie auf MySQL, MariaDB und SQLite nicht ausführen.

Bezeichner-Quoting unterscheidet sich in drei Varianten. Ein generiertes Skript, das Backticks verwendet, läuft auf PostgreSQL nicht.

Der Blocktrenner ist bei SQL Server GO — und das ist kein SQL, sondern eine Anweisung an das Client-Werkzeug. Wer Skripte am Trennzeichen zerlegt, muss diesen Sonderfall kennen.


Die Werte in der Tabelle stammen aus den Engine-Implementierungen von RayMigrator; der Quelltext ist auf GitHub einsehbar.