Datenbankmanagement und Versionierung mit Liquibase
Die Schemaversionierung mit Liquibase erfolgt gemäß den Richtlinien in Versionierung mit Liquibase mit Fokus auf die Integration in Spring Boot und die Nutzung mit Maven. Darüber hinaus werden Informationen zur Nutzung von Liquibase im IsyFact-Kontext bereitgestellt.
Liquibase wird in Spring Boot über die Konfigurationsdateien eingebunden, während das zugehörige Maven-Plugin Schemaänderungen direkt in der Build-Pipeline ausführt.
Durch strukturierte Änderungsprotokolle lassen sich Schema-Anpassungen kontrolliert, nachvollziehbar und automatisiert verwalten. Beim Baseline-Vorgehen bleiben bestehende Datenbanken unverändert und dienen als Basis für zukünftige Änderungen. Die Kombination mit JDBC erlaubt eine flexible Anbindung an verschiedene Datenbanken.
Zudem lässt sich Liquibase nahtlos in CI/CD-Pipelines wie GitLab CI oder GitHub Actions integrieren, um Migrationen und Rollbacks zu automatisieren und so eine sichere Bereitstellung zu gewährleisten.
1. Einrichtung von Liquibase
IT-Systeme nutzen Liquibase direkt sowie als Maven-Plugin. Neben der Einbindung in Maven ist auch eine Konfiguration über Spring Boot notwendig.
1.1. Maven-Konfiguration
Liquibase benötigt JDBC, um mit der Datenbank zu kommunizieren und Änderungen auszuführen.
Daher ist neben der Bibliothek liquibase-core
, die Kernfunktionen von Liquibase enthält, auch der jeweilige JDBC-Treiber der verwendeten Datenbank notwendig.
<dependencies>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<!-- JDBC-Treiber der Datenbank -->
</dependency>
</dependencies>
Neben den Abhängigkeiten ist die Konfiguration des Liquibase Maven Plugin erforderlich.
<build>
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<configuration>
<changeLogFile>src/main/resources/db/changelog/db.changelog-master.xml</changeLogFile>
<url>${spring.datasource.url}</url>
<username>${spring.datasource.username}</username>
<password>${spring.datasource.password}</password>
<driver>${spring.datasource.driver-class-name}</driver>
</configuration>
</plugin>
</plugins>
</build>
Offizielle Dokumentation von Liquibase: Using JDBC URL in Liquibase |
1.2. Spring Boot Konfiguration
Um Liquibase beim Start eines IT-Systems automatisch Schemaänderungen durchführen zu lassen, kann dies in der application.properties
konfiguriert werden.
spring.liquibase.enabled=true
Standardmäßig verwendet Liquibase das Root Changelog db/changelog/db.changelog-master.xml
.
Es dient als zentrale Steuerungsdatei, in der alle Schemaänderungen verwaltet werden.
Es enthält entweder direkte Änderungen oder bindet weitere Changelog-Dateien ein, um eine strukturierte Verwaltung zu ermöglichen.
Es ist empfohlen den Pfad immer explizit anzugeben.
spring.liquibase.change-log=classpath:pfad/zur/changelog.xml
Stattdessen empfiehlt sich die Nutzung von:
-
Umgebungsvariablen (
$DB_USER
,$DB_PASSWORD
), -
einer zentralen Konfiguration (zum Beispiel Spring Cloud Config), oder
-
Secrets-Management-Tools (zum Beispiel AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets).
1.3. Rechte-Trennung
Um Sicherheit und Stabilität zu gewährleisten, können DDL- und DML-Berechtigungen strikt getrennt werden:
-
db_ddl
führt ausschließlich Schema- und Berechtigungsänderungen aus (DDL). -
app_dml
führt ausschließlich Datenmanipulationen aus (DML).
Dadurch gilt: - Die Anwendung selbst hat niemals DDL-Rechte. - Schlägt ein Deployment fehl, kann durch ein Rollback-Deployment sichergestellt werden, dass Datenkonsistenz erhalten bleibt (z. B. durch Trigger, die Daten zurückschreiben).
Rolle | Rechte-Profil | Verwendung |
---|---|---|
|
|
Liquibase Runner wird ausschließlich in CI/CD-Jobs (z. B. Puppet-Job, Init-Container) eingesetzt, um Schema- und Berechtigungsänderungen durchzuführen. |
|
|
Runtime-User der Anwendung führt ausschließlich Datenmanipulationen aus. |
Offizielle Dokumentation von Liquibase: |
2. Erstellung einer Baseline
Die folgenden Schritte beschreiben die Erstellung einer Baseline zur Verwaltung eines bestehenden Schemas.
- 1. Erstellung der
db.changelog-master.xml
-
Zuerst muss das Root Changelog
db.changelog-master.xml
erzeugt werden.Diese Datei dient als Einstiegspunkt für alle Schemaänderungen und stellt sicher, dass die Baseline zuerst und dann alle weiteren Release-Changelogs in der richtigen Reihenfolge angewendet werden.
Bei Verwendung von Spring Boot sollte sich diese Datei im Ordner
src/main/resources/db/changelog
befinden:Listing 5. Beispiel für das Root Changelog in XML<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.8.xsd"> <include file="baseline.xml" relativeToChangelogFile="true"/> </databaseChangeLog>
Listing 6. Beispiel für das Root Changelog in YAMLdatabaseChangeLog: - include: file: baseline.yml relativeToChangelogFile: true
- 2. Generierung der
baseline.xml
-
mvn liquibase:generateChangeLog -Dliquibase.outputChangeLogFile=src/main/resources/db/changelog/baseline.xml
Nach der Generierung sollte die Datei manuell überprüft und bereinigt werden.
- 3. Markierung der Baseline
-
Damit Liquibase die bestehende Datenbank nicht erneut verändert, aber die bereits vorhandenen Strukturen als Referenz speichert, muss die Baseline als ausgeführt markiert werden.
Listing 7. Markierung der Baseline als ausgeführtmvn liquibase:changelogSync
Dadurch werden alle im Changelog definierten Änderungen als bereits angewendet registriert, ohne tatsächlich Änderungen an der Datenbank vorzunehmen.
Listing 8. Optionale Vorabprüfung ohne Ausführungmvn liquibase:changelogSyncSQL
Dieser Befehl zeigt die SQL-Befehle an, die Liquibase ausführen würde, ohne sie tatsächlich anzuwenden.
Offizielle Dokumentation von Liquibase:
- 4. Tabellen
DATABASECHANGELOG
- undDATABASECHANGELOGLOCK
anlegen -
Beim ersten Liquibase-Update oder Sync-Vorgang legt Liquibase automatisch zwei Tabellen an:
-
DATABASECHANGELOG
: Speichert ausgeführte Changesets. -
DATABASECHANGELOGLOCK
: Sperrt die Datenbank, um gleichzeitige Änderungen zu verhindern.
Falls die Sperre aus einem vorherigen Prozess hängen geblieben ist, kann sie mit folgendem Befehl entfernt werden:
Listing 9. Entfernung der Sperremvn liquibase:releaseLocks
Offizielle Dokumentation von Liquibase: Tracking Tables
-
- 5. Integration der Baseline in das Versionskontrollsystem
-
Nachdem die Baseline erstellt wurde, sollten sowohl diese als auch das Root Changelog
db.changelog-master.xml
in das Versionskontrollsystem aufgenommen werden. Dies gewährleistet eine nachvollziehbare Historie der Datenbankänderungen.
2.1. Gefahr von Schema-Drift und semantische Versionskontrolle mit isy-persistence
Liquibase gewährleistet die Nachvollziehbarkeit aller Datenbankänderungen über die Tabellen DATABASECHANGELOG
und DATABASECHANGELOGLOCK
.
Damit wird sichergestellt, dass jede definierte Änderung genau einmal und in der richtigen Reihenfolge ausgeführt wird.
Liquibase prüft nicht, ob die tatsächliche Datenbankversion mit der vom Anwendungscode erwarteten Release-Version übereinstimmt. Dadurch kann es trotz konsistentem Changelog zum Schema-Drift kommen.
Schema-Drift kann folgende Ursachen haben:
-
manuelle Hotfixes oder Änderungen, ohne dass diese in Liquibase berücksichtigt werden (z. B.
ALTER TABLE
ohne Changelog), -
Wiederherstellung eines Snapshots, ohne erneute Anwendung aller Changesets,
-
abgebrochene Deployments, bei denen einzelne Changesets übersprungen wurden.
Solche Abweichungen führen dazu, dass die Anwendung gegen ein nicht kompatibles Schema startet. Dies birgt Risiken wie Laufzeitfehler oder Datenverlust.
2.1.1. Enforcement mit isy-persistence
Um zu verhindern, dass unbemerkte Schema-Abweichungen in Produktion zu Herausforderungen und Aufwand führen, und die Integrität zwischen Anwendung und Datenbank über alle Deployments hinweg gewährleistet ist oder die Durchsetzung von semantischen Schema-Versionen eine Anforderung ist, kann über die Funktionalität der Eigenentwicklung zur Versionierung eine Schema-Versionskontrolle auf Anwendungsebene eingebunden werden.
2.1.2. Bewährte Praktiken
Die Kombination aus Liquibase Changelogs und Version Enforcement bietet für großskalige Umgebungen Vorteile:
-
Deterministische Kompatibilität: Die Anwendung läuft nur gegen ein getestetes, freigegebenes Schema.
-
Früherkennung: Fehlkonfigurationen oder manuelle Eingriffe werden beim Start erkannt.
3. Release-orientierte Umsetzung von Schemaänderungen
In den folgenden Schritten werden Changelogs in XML als Beispiel verwendet.
Es wird eine Baseline als Ausgangspunkt für die Datenbankstruktur genutzt, auf die alle nachfolgenden Release-Changelogs aufbauen.
Für eine genauere Steuerung, beispielsweise bei Stored Procedures, Triggern oder komplexen Optimierungen, können Changelogs auch als SQL-Dateien integriert werden.
3.1. Verzeichnisstruktur
Die Verzeichnisstruktur ist so aufgebaut, dass die Baseline als erster Schritt dient und alle nachfolgenden Releases über Changelogs (changelog-X.Y.xml
) angewendet werden.
Sie definiert die grundlegende Datenbankstruktur und bleibt nach der ersten Anwendung unverändert.
/db/changelog/
├── db.changelog-master.xml
├── baseline.xml
├── changelog-1.0.xml
├── changelog-1.1.xml
└── changelog-1.2.xml
Offizielle Dokumentation von Liquibase: |
3.2. Root Changelog
Das Root Changelog ist die db.changelog-master.xml
und stellt sicher, dass die Baseline zuerst und dann alle weiteren Release-Changelogs in der richtigen Reihenfolge angewendet werden.
<databaseChangeLog>
<include file="db/changelog/baseline.xml"/>
<include file="db/changelog/changelog-1.0.xml"/>
<include file="db/changelog/changelog-1.1.xml"/>
<include file="db/changelog/changelog-1.2.xml"/>
</databaseChangeLog>
3.3. Tagging von Baseline und Releases
Vor jedem neuen Release sollte ein Tag gesetzt werden, um ein sauberes Rollback des gesamten Releases zu erleichtern. Der erste Tag definiert die Baseline und markiert diesen Zustand als Referenzpunkt. Dadurch kann bei Bedarf gezielt auf die Baseline zurückgerollt werden. Eine einheitliche sinnvolle Namenskonvention ist empfehlenswert.
mvn liquibase:tag -Dliquibase.tag=v1.0-baseline
mvn liquibase:rollback -Dliquibase.rollbackTag=v1.0-baseline
Offizielle Dokumentation von Liquibase: Maven tag |
3.4. Erste Änderungen nach der Baseline
Das erste Update nach der Baseline ist Release-Changelog 1.0. Alle nachfolgenden Release-Changelogs enthalten ausschließlich inkrementelle Änderungen zur Baseline.
<databaseChangeLog>
<changeSet id="1.0-001" author="dev1">
<addColumn tableName="customers">
<column name="email" type="varchar(255)"/>
</addColumn>
</changeSet>
<changeSet id="1.0-002" author="dev2">
<createIndex indexName="idx_orders_date" tableName="orders">
<column name="order_date"/>
</createIndex>
</changeSet>
</databaseChangeLog>
3.5. Preconditions
Preconditions in Liquibase prüfen vor der Ausführung eines Changesets definierte Bedingungen. Wird eine Bedingung nicht erfüllt, kann die Migration abgebrochen, übersprungen oder mit einer Warnung fortgesetzt werden. Dadurch werden Fehler frühzeitig erkannt und nur zulässige Änderungen ausgeführt.
<databaseChangeLog>
<changeSet id="1.0-001" author="dev1">
<preConditions onFail="HALT">
<not>
<columnExists tableName="customers" columnName="email"/>
</not>
</preConditions>
<addColumn tableName="customers">
<column name="email" type="varchar(255)"/>
</addColumn>
</changeSet>
<changeSet id="1.0-002" author="dev2">
<preConditions onFail="HALT">
<not>
<indexExists tableName="orders" indexName="idx_orders_date"/>
</not>
</preConditions>
<createIndex indexName="idx_orders_date" tableName="orders">
<column name="order_date"/>
</createIndex>
</changeSet>
</databaseChangeLog>
Offizielle Dokumentation von Liquibase: Preconditions |
3.6. Manuelles Rollback im Changelog
Rollback-Anweisungen in der changelog.xml
ermöglichen Rollbacks einzelner Changesets für mehr Kontrolle.
<databaseChangeLog>
<changeSet id="1.0-001" author="dev1">
<addColumn tableName="customers">
<column name="email" type="varchar(255)"/>
</addColumn>
<rollback>
<dropColumn tableName="customers" columnName="email"/>
</rollback>
</changeSet>
<changeSet id="1.0-002" author="dev2">
<createIndex indexName="idx_orders_date" tableName="orders">
<column name="order_date"/>
</createIndex>
<rollback>
<dropIndex indexName="idx_orders_date" tableName="orders"/>
</rollback>
</changeSet>
</databaseChangeLog>
3.7. Befehle zum Rollback
Befehl | Bedeutung |
---|---|
|
Rollback auf einen zuvor gesetzten Tag ( |
|
Rollback um eine bestimmte Anzahl an Changesets (hier: 1 Changeset). |
|
Rollback auf einen spezifischen Zeitpunkt im Format |
|
Rollback auf ein bestimmtes Datum ( |
Offizielle Dokumentation von Liquibase: Liquibase Rollback Workflow |
4. Einsatz von CI/CD-Pipelines für automatisierte Migrationen
Liquibase kann nahtlos in GitLab CI/CD-Pipelines integriert werden, um Datenbankmigrationen automatisiert, sicher und kontrolliert auszuführen.
Die erforderlichen Konfigurationen werden in der Datei .gitlab-ci.yml
definiert.
- 1. Validierung der Changelogs
-
Vor dem Deployment sollte sichergestellt werden, dass alle Changelogs validiert und fehlerfrei sind. Tritt ein Fehler auf, wird der Prozess abgebrochen, um fehlerhafte Migrationen zu verhindern.
Listing 16. Beispiel für ein Validierungsskriptliquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASSWORD validate
- 2. Deployment auf Staging
-
Die Datenbankmigration wird durchgeführt und das Release mit einem Tag versehen, um Rollbacks zu ermöglichen. Mit
liquibase history
werden die durchgeführten Änderungen angezeigt. Falls alle Tests erfolgreich sind, wird das Deployment für die Produktion freigegeben.Listing 17. Beispiel für ein Deployment-Skriptliquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASSWORD update liquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASSWORD tag $RELEASE_VERSION liquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASSWORD history
- 3. Deployment in Produktion
-
Die gleichen Deployment-Befehle wie für Staging werden auf der Produktionsdatenbank ausgeführt.
- 4. Automatisches Rollback bei Fehlern
-
Falls die Pipeline einen Fehler erkennt, kann ein automatisches Rollback erfolgen:
Listing 18. Beispiel für ein Rollback-Skriptliquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASSWORD rollbackToTag $RELEASE_VERSION liquibase --url=$DB_URL --username=$DB_USER --password=$DB_PASSWORD history
Ähnliche Konfigurationen können zum Beispiel für GitHub Actions erstellt werden.
Offizielle Dokumentation von Liquibase: Using Liquibase with GitLab CI/CD
5. DB Update Management: Drei-Phasen-Strategie mit Liquibase
Dieses Verfahren wird angewendet, wenn Daten in einem vorhandenen Datenbankfeld verändert werden müssen. Dabei wird die Anwendung so erweitert, dass sie auf dem neuen Feld arbeitet. Gleichzeitig bleibt die Datenbank in einem Zustand, in dem ältere Anwendungsversionen weiterhin ohne Anpassungen auf der Datenbank arbeiten können. Sobald alle Altversionen abgeschaltet sind, wird dann in einer dritten Phase das alte Datenbankfeld aus der Datenbank entfernt.
- 1. Phase
-
Schema- und Migration-Changeset mit Label
pre/db-deploy
nutzen (z.B. <addColumn> und Trigger oder Batch-SQL nutzen um Bestandsdaten zu kopieren. Hier sollte die Rollback-SQL bereitgehalten werden. - 2. Phase
-
Das Feature-Rollout findet in der Anwendung statt. Hier wird auf neues Feld umgestellt. Falls nötig, ist ein Changeset für zusätzliche Indizes mit dem Label
app-deploy
zu nutzen. - 3. Phase
-
Laufen alle Instanzen der Anwendung auf der neuen Version der Anwendung ist mit einem Changeset mit dem Label
clean-up
das Datenbankschema zu bereinigen (z.B. <dropColumn>).
Für CI-Pipelines sind diese Tätigkeiten am besten in einzelnen Jobs auszuführen. Mindestens jedoch in zwei, Phase 1 und Phase 2 können zusammen ausgeführt werden. |
6. Nützliche Liquibase-Befehle zur Schema-Verwaltung
Liquibase bietet verschiedene Befehle zur Überprüfung der Aktualität des Schemas und zur Verwaltung von Änderungen. Hier sind einige der wichtigsten Befehle:
Befehl | Bedeutung |
---|---|
|
Überprüft, ob alle Changesets korrekt formatiert und konsistent sind. Falls Probleme auftreten, gibt Liquibase eine Fehlermeldung aus, die auf fehlerhafte oder fehlende Changesets hinweist. |
|
Überträgt alle noch nicht angewendeten Änderungen aus den Changelogs auf die Datenbank. Für Datenbanken in der Produktion ist ein Ad-hoc-Snapshot vor dem Liquibase-Update zu erstellen. |
|
Zeigt an, welche Änderungen noch nicht auf die Datenbank angewendet wurden. Liquibase gibt eine Liste der ausstehenden Changesets zurück. |
|
Vergleicht zwei Datenbanken und zeigt Unterschiede an. Dies ist unter anderem nützlich, um zu überprüfen, ob und wie sich die Datenbanken in verschiedenen Umgebungen unterscheiden. Oder um zu überprüfen, ob alle Objekte in einer Baseline berücksichtigt sind. Hierfür können die Unterschiede zwischen dem IST-Zustand und einer leeren Datenbank erzeugt werden. |
|
Labels können genutzt werden, um Datenbankänderungen, die ein Feature betreffen, zu kennzeichnen und zum gewünschten Zeitpunkt auszuführen. (Vgl. Liquibase-Doku-Labels). Dies kann in Verbindung mit Feature-Toggles genutzt werden. |
|
Erstellt eine Momentaufnahme der aktuellen Datenbankstruktur, die zum Beispiel für Analysen oder den Vergleich mit späteren Versionen verwendet werden kann. |
|
Führt ein Rollback aus. Dies stellt den Zustand der Datenbank auf einen definierten Punkt zurück und entfernt alle nachfolgenden Änderungen. |
Offizielle Dokumentation von Liquibase: Liquibase-Befehlen. |