Datenbank-Migrationen sind bei Server-Wechseln, Upgrades oder Systemumstellungen unvermeidlich. Die richtige Strategie minimiert Ausfallzeiten und Datenverlust.
Migration planen
Vorüberlegungen
1. Quell- und Zielsystem identifizieren
2. Datenbankgröße ermitteln
3. Ausfallzeit-Fenster planen
4. Backup erstellen
5. Testmigration durchführen
6. Rollback-Plan vorbereitenCheckliste
| Schritt | Erledigt | |---------|----------| | Backup der Quelldatenbank | ☐ | | Zieldatenbank vorbereitet | ☐ | | Anwendung informiert | ☐ | | Testmigration erfolgreich | ☐ | | Rollback-Plan dokumentiert | ☐ |
MySQL zu MySQL
Einfache Migration (mysqldump)
# Export von Quelldatenbank
mysqldump -h source-server -u root -p --all-databases \
--single-transaction \
--routines \
--triggers \
--events > all_databases.sql
# Import auf Zieldatenbank
mysql -h target-server -u root -p < all_databases.sqlEinzelne Datenbank
# Export
mysqldump -h source -u root -p mydb > mydb.sql
# Import
mysql -h target -u root -p mydb < mydb.sqlMit Komprimierung
# Export komprimiert
mysqldump -u root -p mydb | gzip > mydb.sql.gz
# Import komprimiert
gunzip < mydb.sql.gz | mysql -u root -p mydbÜber SSH-Tunnel
# Direkte Pipe über SSH
mysqldump -u root -p mydb | ssh user@target "mysql -u root -p mydb"
# Mit Komprimierung
mysqldump -u root -p mydb | gzip | ssh user@target "gunzip | mysql -u root -p mydb"Große Datenbanken
# Mit Progress-Anzeige
apt install pv
mysqldump -u root -p mydb | pv | gzip > mydb.sql.gz
# Parallel mit mydumper
apt install mydumper
mydumper -u root -p password -B mydb -o /backup/mydb
myloader -u root -p password -B mydb -d /backup/mydbMySQL zu MariaDB
Kompatibilität
MySQL 5.7 → MariaDB 10.2+
MySQL 8.0 → MariaDB 10.3+ (mit Einschränkungen)
Inkompatibilitäten:
- JSON-Datentyp (MariaDB: LONGTEXT)
- Window Functions (unterschiedliche Syntax)
- CTEs (teilweise)
- System-Tabellen (mysql.*)In-Place-Upgrade (gleicher Server)
# MySQL stoppen
systemctl stop mysql
# MySQL deinstallieren (Daten bleiben erhalten)
apt remove mysql-server mysql-client
# MariaDB Repository hinzufügen
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash
# MariaDB installieren
apt install mariadb-server mariadb-client
# Upgrade durchführen
mysql_upgrade -u root -p
# Service starten
systemctl start mariadbDump-basierte Migration
# Von MySQL exportieren
mysqldump -u root -p --all-databases \
--routines \
--triggers \
--single-transaction > mysql_dump.sql
# Inkompatible Syntax anpassen (optional)
sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' mysql_dump.sql
# In MariaDB importieren
mysql -u root -p < mysql_dump.sqlAuthentifizierung anpassen
-- MySQL 8.0 verwendet caching_sha2_password
-- MariaDB verwendet mysql_native_password
-- Benutzer nach Migration aktualisieren
ALTER USER 'user'@'host' IDENTIFIED WITH mysql_native_password BY 'password';MySQL zu PostgreSQL
Unterschiede beachten
| MySQL | PostgreSQL | |-------|------------| | AUTO_INCREMENT | SERIAL | | TINYINT(1) | BOOLEAN | | DATETIME | TIMESTAMP | | DOUBLE | DOUBLE PRECISION | | TEXT | TEXT | | ENUM | CREATE TYPE | | Backticks ` | Anführungszeichen " |
Mit pgloader
# Installation
apt install pgloader
# Migrations-Konfiguration
cat > mysql2pg.load << 'EOF'
LOAD DATABASE
FROM mysql://user:password@localhost/mydb
INTO postgresql://user:password@localhost/mydb
WITH include drop, create tables, create indexes, reset sequences
SET maintenance_work_mem to '512MB',
work_mem to '48MB'
CAST type datetime to timestamp using zero-dates-to-null,
type tinyint to boolean using tinyint-to-boolean;
EOF
# Migration ausführen
pgloader mysql2pg.loadManuelle Migration
# 1. Schema exportieren
mysqldump -u root -p --no-data mydb > schema.sql
# 2. Schema konvertieren (manuell oder mit Tool)
# Anpassungen:
# - AUTO_INCREMENT → SERIAL
# - Engine-Definitionen entfernen
# - Backticks → Anführungszeichen
# 3. In PostgreSQL importieren
psql -U postgres -d mydb -f schema_converted.sql
# 4. Daten als CSV exportieren
mysql -u root -p -N -e "SELECT * FROM users" mydb > users.csv
# 5. Daten in PostgreSQL importieren
psql -U postgres -d mydb -c "\COPY users FROM 'users.csv' CSV"Schema-Konvertierung
-- MySQL
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
active TINYINT(1) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- PostgreSQL
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);PostgreSQL zu MySQL
Mit pgloader (umgekehrt)
# pgloader unterstützt auch PG → MySQL
cat > pg2mysql.load << 'EOF'
LOAD DATABASE
FROM postgresql://user:password@localhost/mydb
INTO mysql://user:password@localhost/mydb
WITH include drop, create tables
CAST type boolean to tinyint using boolean-to-tinyint;
EOF
pgloader pg2mysql.loadManuelle Migration
# Schema exportieren
pg_dump -U postgres -s mydb > schema.sql
# Daten als CSV
psql -U postgres -d mydb -c "\COPY users TO 'users.csv' CSV HEADER"
# In MySQL importieren
mysql -u root -p mydb -e "LOAD DATA INFILE 'users.csv' INTO TABLE users FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\n' IGNORE 1 ROWS"Online-Migration (Minimale Ausfallzeit)
Mit Replikation
# 1. Slave auf Zielserver einrichten
# 2. Replikation laufen lassen
# 3. Anwendung stoppen
# 4. Replikation abschließen
# 5. Anwendung auf neuen Server umstellenMit gh-ost (Schema-Änderungen)
# Installation
wget https://github.com/github/gh-ost/releases/download/v1.1.5/gh-ost-binary-linux-amd64.tar.gz
tar xzf gh-ost-binary-linux-amd64.tar.gz
# Online Schema-Migration
gh-ost \
--host=localhost \
--database=mydb \
--table=users \
--user=root \
--password=secret \
--alter="ADD COLUMN email VARCHAR(255)" \
--executeMit pt-online-schema-change
# Installation
apt install percona-toolkit
# Online-Migration
pt-online-schema-change \
--alter "ADD COLUMN email VARCHAR(255)" \
D=mydb,t=users \
--executeAWS DMS (Database Migration Service)
Konzept
Source DB → DMS Replication Instance → Target DB
Unterstützt:
- MySQL → Aurora/RDS
- Oracle → PostgreSQL
- On-Premise → CloudKonfiguration
{
"rules": [
{
"rule-type": "selection",
"rule-id": "1",
"rule-name": "migrate-all",
"object-locator": {
"schema-name": "mydb",
"table-name": "%"
},
"rule-action": "include"
}
]
}Datenvalidierung
Zeilenanzahl prüfen
# MySQL
mysql -u root -p -N -e "SELECT COUNT(*) FROM users" mydb
# PostgreSQL
psql -U postgres -t -c "SELECT COUNT(*) FROM users" mydbChecksummen vergleichen
# pt-table-checksum
pt-table-checksum --databases=mydb h=source,u=root,p=secret
# Manuell
mysql -N -e "SELECT MD5(GROUP_CONCAT(CONCAT_WS(',',*) ORDER BY id)) FROM users" mydbDatenintegrität
-- Primary Keys prüfen
SELECT COUNT(*) FROM users;
SELECT COUNT(DISTINCT id) FROM users;
-- NULL-Werte
SELECT COUNT(*) FROM users WHERE email IS NULL;
-- Fremdschlüssel
SELECT u.id FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;Rollback-Plan
Backup-Strategie
# Vor Migration: Vollständiges Backup
mysqldump -u root -p --all-databases > pre_migration_backup.sql
# Zeitstempel
mysqldump -u root -p mydb > mydb_$(date +%Y%m%d_%H%M%S).sqlSchneller Rollback
# Anwendung auf alten Server zurückstellen
# DNS-TTL vorher auf niedrigen Wert setzen
# Datenänderungen seit Migration übertragen
mysqldump -u root -p --where="updated_at > '2026-01-26 10:00:00'" mydb > changes.sqlSkript-Beispiel
Automatisierte Migration
#!/bin/bash
# migrate-db.sh
set -e
SOURCE_HOST="source.example.com"
TARGET_HOST="target.example.com"
DB_USER="root"
DB_PASS="password"
DATABASE="mydb"
# Backup erstellen
echo "Creating backup..."
mysqldump -h $SOURCE_HOST -u $DB_USER -p$DB_PASS \
--single-transaction \
--routines \
--triggers \
$DATABASE > /tmp/${DATABASE}_backup.sql
# Datenbank auf Ziel erstellen
echo "Creating target database..."
mysql -h $TARGET_HOST -u $DB_USER -p$DB_PASS \
-e "CREATE DATABASE IF NOT EXISTS $DATABASE"
# Daten importieren
echo "Importing data..."
mysql -h $TARGET_HOST -u $DB_USER -p$DB_PASS $DATABASE < /tmp/${DATABASE}_backup.sql
# Validierung
echo "Validating..."
SOURCE_COUNT=$(mysql -h $SOURCE_HOST -u $DB_USER -p$DB_PASS -N \
-e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DATABASE'")
TARGET_COUNT=$(mysql -h $TARGET_HOST -u $DB_USER -p$DB_PASS -N \
-e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$DATABASE'")
if [ "$SOURCE_COUNT" -eq "$TARGET_COUNT" ]; then
echo "Migration successful! Tables: $TARGET_COUNT"
else
echo "ERROR: Table count mismatch ($SOURCE_COUNT vs $TARGET_COUNT)"
exit 1
fi
# Cleanup
rm /tmp/${DATABASE}_backup.sql
echo "Done!"Zusammenfassung
| Migration | Tool | |-----------|------| | MySQL → MySQL | mysqldump | | MySQL → MariaDB | mysqldump + mysql_upgrade | | MySQL → PostgreSQL | pgloader | | PostgreSQL → MySQL | pgloader / manuell |
| Strategie | Ausfallzeit | |-----------|-------------| | Dump/Restore | Minuten bis Stunden | | Replikation | Sekunden | | Online-Tools | Minimal |
| Validierung | Methode | |-------------|---------| | Zeilenanzahl | COUNT(*) vergleichen | | Checksumme | MD5/SHA vergleichen | | Stichproben | SELECT WHERE vergleichen |
Fazit
Datenbank-Migrationen erfordern sorgfältige Planung und Tests. Für MySQL zu MariaDB sind In-Place-Upgrades oft möglich. Bei Wechsel zu PostgreSQL ist pgloader das beste Tool. Online-Migrationstools wie gh-ost minimieren Ausfallzeiten bei großen Datenbanken. Ein Rollback-Plan ist unverzichtbar. Validierung nach der Migration stellt Datenintegrität sicher.