Sichere Datenbankmigrationen
Datenbankmigrationen auf einer Produktionsdatenbank mit laufendem Traffic sind die häufigste Ursache für ungeplante Downtimes. Die meisten Probleme sind vermeidbar.
Was gefährlich ist
-- Sperrt die gesamte Tabelle, blockiert alle Reads und Writes ALTER TABLE orders ADD COLUMN discount_pct DECIMAL(5,2) NOT NULL DEFAULT 0; -- Noch schlimmer auf großen Tabellen ALTER TABLE events ADD INDEX idx_created (created_at);
MySQL/MariaDB sperrt bei manchen ALTERs die Tabelle für die gesamte Dauer. Bei 50 Millionen Zeilen dauert das.
Online DDL prüfen
-- Prüfen ob die Operation Online geht (MySQL 5.6+)
ALTER TABLE orders
ADD COLUMN discount_pct DECIMAL(5,2) NOT NULL DEFAULT 0,
ALGORITHM=INPLACE, LOCK=NONE;
Schlägt fehl wenn Online nicht möglich ist statt einfach zu sperren.
Schrittweise vorgehen
Statt ALTER TABLE ... NOT NULL in einem Schritt:
- Spalte als nullable hinzufügen (schnell, kein Lock):
ALTER TABLE orders ADD COLUMN discount_pct DECIMAL(5,2) NULL DEFAULT NULL;
- Code deployen der beide Spalten befüllt.
- Daten befüllen (in Batches):
UPDATE orders SET discount_pct = 0 WHERE discount_pct IS NULL LIMIT 10000; -- Wiederholen bis kein NULL mehr vorhanden
- NOT NULL-Constraint setzen (jetzt schnell weil kein NULL mehr):
ALTER TABLE orders MODIFY COLUMN discount_pct DECIMAL(5,2) NOT NULL DEFAULT 0;
pt-online-schema-change
Für wirklich kritische Tabellen: Percona Toolkit's pt-osc.
pt-online-schema-change \
--alter "ADD COLUMN discount_pct DECIMAL(5,2) NOT NULL DEFAULT 0" \
--execute \
D=myapp,t=orders
Erstellt eine Schattentabelle, kopiert Daten in Batches, hält über Trigger synchron, benennt um.
Keine Downtime, aber Overhead durch Trigger.
Migrationen versionieren
migrations/ 001_initial_schema.sql 002_add_orders_table.sql 003_add_discount_column.sql
$applied = $pdo->fetchAll('SELECT version FROM schema_migrations ORDER BY version');
$appliedVersions = array_column($applied, 'version');
foreach (glob('migrations/*.sql') as $file) {
$version = basename($file, '.sql');
if (!in_array($version, $appliedVersions)) {
$pdo->exec(file_get_contents($file));
$pdo->prepare('INSERT INTO schema_migrations (version) VALUES (?)')->execute([$version]);
}
}
Migrationen nie rückwirkend ändern — immer neue hinzufügen.