Erleben Sie 1440 Datenbankmigrationen



Stellen Sie sich Oracle DBA vor. Er ist bereits über dreißig, er ist etwas übergewichtig, trägt eine Weste, er hat einen geheimen Zugang zu allen Basen, die an seinem Hals hängen, und in einer Zusammenfassung von einer halben Seite der Zertifizierungen, die er bestanden hat. Samstag Großer Veröffentlichungstag. Höhepunkt. Zeit, Änderungen an der Datenbank vorzunehmen. Er gibt sqlplus ein, drückt die EINGABETASTE und irgendwo auf dem schwarzen Bildschirm in die Leere rasen kilometerlange SQL-Befehle. Genau wie in Star Wars. Fünf Minuten später ist alles fertig. Eine Stunde später ist die Veröffentlichung abgeschlossen. Die Arbeit ist erledigt, der Tag war ein Erfolg. Jetzt können Sie ein paar Biere trinken.

Eine andere Sache ist Montag. Es stellt sich heraus, dass einige Befehle aufgrund von Fehlern nicht ausgeführt wurden, was das Skript jedoch in seinem ungezügelten Streben nach schwarzer Leere nicht aufhielt. Die ohnehin schwierige Aufgabe, herauszufinden, was kaputt ist, wird durch den Druck der Führung erschwert. Im Allgemeinen hat der Montag nicht geklappt.

Dies ist natürlich eine fiktive Geschichte. Das ist noch nie jemandem passiert. Zumindest wäre es nicht passiert, wenn die Arbeit an der Änderung des Datenbankschemas durch Migrationen organisiert worden wäre.

Was ist ein Datenbankmigrationstool?


Die Idee, Datenbankschemaänderungen durch Migrationen zu verwalten, ist äußerst einfach:

  1. Jede Änderung wird als separate Migrationsdatei ausgegeben.
  2. Die Migrationsdatei enthält sowohl direkte als auch umgekehrte Änderungen.
  3. Die Anwendung von Migrationen in die Datenbank wird von einem speziellen Dienstprogramm ausgeführt.

Das einfachste Migrationsbeispiel:
-- 20180618152059: create sequence for some_table CREATE SEQUENCE some_table_seq; --//@UNDO DROP SEQUENCE some_table_seq; 

Dieser Ansatz bietet viele Vorteile gegenüber dem Organisieren von Änderungen in einer gemeinsamen SQL-Datei. Das bloße Fehlen von Zusammenführungskonflikten lohnt sich.

Umso überraschender ist es, dass der Ansatz selbst in jüngster Zeit an Popularität gewonnen hat. Es scheint, dass das Ruby on Rails-Framework, in das das Migrationstool ursprünglich integriert war, der Hauptruhm des Ansatzes war, es war Ende 2005. Ein wenig früher schrieb Martin Fowler, 2003 , über den Ansatz. Wahrscheinlich geht es darum, dass die Entwicklung die Verwendung des Versionskontrollsystems erst zu Beginn dieses Jahrhunderts aktiv anpasste. Im Jahr 2000 lautete der erste Absatz des Joel-Spolsky-Tests „Verwenden Sie die Quellcodeverwaltung?“. - Dies deutet darauf hin, dass zu diesem Zeitpunkt nicht jeder Versionskontrollsysteme verwendete. Aber wir waren abgelenkt.

Acht Jahre bei MyBatis Migrations


Wir bei Wrike haben 2010, am 29. März, um halb eins begonnen, den Ansatz der Datenbankänderung durch Migrationen anzuwenden . Seitdem haben wir 1.440 Migrationen implementiert, die 6.436 direkte Änderungen und 5.015 Umkehrungen enthalten. Im Allgemeinen haben wir einige Erfahrungen mit dem MyBatis Migrations- Tool in Verbindung mit PostgreSQL gesammelt.

Kurz gesagt, wir haben es nie bereut. Wenn Sie Migrationen oder ähnliches nicht verwenden, ist es Zeit zu beginnen. Ja, Pentium 4 ist ebenfalls veraltet.

Aber es ist langweilig, über die Vorzüge von irgendetwas zu sprechen. Gehen wir gleich zu den Schwierigkeiten.

PostgreSQL-Besonderheiten


Es gibt vielleicht keine Schwierigkeiten beim Schreiben von Migrationen für Postgres, außer für zwei:
  • Sie können keine Indizes erstellen.
  • Sie können keine NOT NULL-Spalten hinzufügen.

Nein, eigentlich ist es möglich, nur nicht auf ganz offensichtliche Weise. Wenn Sie einen Index erstellen, sollten Sie CURATE INDEX CONCURRENTLY immer angeben, da sonst die Produktion unterbrochen wird, da Postgres die Tabelle für die Dauer der Indexerstellung sperrt. Dies kann sehr lange dauern. Natürlich vergessen Entwickler es einmal, man muss diese Subtilität immer im Auge behalten. Hier könnte man einen Test schreiben. Dies ist jedoch nur eine geringfügige Unannehmlichkeit.

Das Erstellen von NOT NULL-Spalten ist schwieriger. Hier müssen Sie eine Änderung in vier Schritten vornehmen:
  1. Erstellen Sie eine NULL-Spalte (in Postgres ist es kostenlos).
  2. Setzen Sie die Spalte DEFAULT auf einen Wert.
  3. Aktualisieren Sie in einer Schleife schrittweise die NULL-Werte in DEFAULT.
  4. Setze SET NOT NULL.

Der größte Haken hier ist im dritten Absatz. NULL-Werte müssen in Teilen aktualisiert werden, da UPDATE some_table SET some_column='' WHERE some_column IS NULL ; blockiert die Tabelle, wie es beim Index der Fall ist, mit den gleichen Konsequenzen. Und Migrationen können nur SQL-Befehle ausführen, sodass solche Skripte von Hand in die Produktion gerollt werden müssen. Vergnügen unterdurchschnittlich. Wenn nun ein Zyklus in Migrationen geschrieben werden könnte, gäbe es kein Problem. Vielleicht wird dies durch Hooks implementiert.

Das Erstellen eines UNIQUE Index und das Ändern eines PRIMARY KEY erfordert ebenfalls einige Kenntnisse, aber diese Operationen sind relativ selten.

Cluster-Besonderheiten


Das Tool zur Verwaltung der Datenbankmigration ist gut, solange Sie über eine Datenbank verfügen. Umso mehr Spaß, wenn Sie mehrere Basen haben. Insbesondere, wenn Sie mehrere Arten von Datenbanken haben, von denen jede mehrere Instanzen hat.

Infolgedessen muss git pull Entwickler nach dem git pull die Änderungen auf die erste Instanz der ersten Datenbank, dann auf die zweite Instanz, dann auf die erste Instanz der zweiten Datenbank usw. übertragen - ein solches Prinzip. Hier ist es genau richtig, ein Dienstprogramm zum Verwalten des Datenbankmigrationsverwaltungsdienstprogramms zu schreiben. Totale Automatisierung.

Rollenjonglieren


Es ist kein Geheimnis, dass Rollen als Entitäten nicht auf der Ebene einer separaten Datenbank, sondern auf der Ebene des gesamten Datenbankservers leben, zumindest in Postgres. In diesem Fall müssen Sie möglicherweise REVOKE INSERT ON some_table FROM some_role angeben. Es ist immer noch zu erwarten, dass Rollen in der Produktion vorkonfiguriert werden, aber für Entwickler oder Staging ist dies bereits schwierig. Gleichzeitig befinden sich in der Entwicklung natürlich alle Datenbanken auf demselben lokalen Server, sodass Sie bei der Migration einfach keine CREATE ROLE schreiben können und IF NOT EXISTS nicht unterstützt wird. Alles ist einfach gelöst:
 DO $$ BEGIN IF NOT EXISTS (SELECT * FROM pg_roles WHERE rolname = 'some_role') THEN CREATE ROLE "some_role" NOLOGIN; END IF; END; $$; 

Schau es dir an! Ich fange und werfe sie, fange und werfe, es ist so einfach.

Ein bisschen Entwicklungsrealität


Entwickler machen Fehler, und selbst bei SQL-Migrationen passiert dies. Normalerweise können Fehler in der Überprüfung festgestellt werden, aber es kann auch ungewöhnlich sein. Wenn wir über direkte Änderungen sprechen, erreichen die Pfosten dort immer noch nicht die Produktion - es gibt zu viele Überprüfungsstufen. Bei den umgekehrten Änderungen können jedoch Vorfälle auftreten. Um Fehler bei der UNDO-Migration zu vermeiden, müssen Sie beim Testen der Migration nicht nur ./migrate up , sondern ./migrate up , dann ./migrate down , dann erneut ./migrate up . Dies ist nicht kompliziert, Sie müssen nur sicherstellen, dass immer vierzig Entwickler dies tun. In guter Weise könnte das Dienstprogramm eine solche Kombination für die Entwicklerumgebung automatisch ausführen.

Testumgebungen


Wenn die Testumgebung nur von kurzer Dauer ist: Angenommen, Sie erstellen einen Container, initialisieren die Datenbank und führen Integrationstests aus. Es sollten keine Probleme auftreten. Wir ./migrate bootstrap , dann ./migrate up , und Sie sind fertig. Wenn die Anzahl der Migrationen eintausend überschreitet, kann sich dieser Vorgang verzögern. Es ist eine Schande, wenn die Datenbank länger initialisiert wird als die Tests. Wir müssen ausweichen.

In langlebigen Umgebungen ist es noch schwieriger. QA, wissen Sie, sie möchten keine makellos saubere Datenbank sehen, wenn sie zur Arbeit kommen. Ich weiß nicht, warum das so ist, aber Tatsache ist Tatsache. Daher muss der Zustand der bei manuellen Tests verwendeten Basen integer gehalten werden. Und das ist nicht immer einfach.

Die Subtilität besteht darin, dass bei Anwendung der Migration auf die Datenbank die Migrationskennung in diese geschrieben wird. Wenn der Migrationscode später geändert wurde, ist die Datenbank nicht betroffen. Wenn die Änderungen nicht kritisch sind, kann der Code erfolgreich in die Produktion gelangen. Rssynchron. Das ist natürlich eine Schande. Das erste Prinzip bei der Arbeit mit Migrationen besteht darin, schriftliche Migrationen niemals zu ändern, sondern immer neue zu erstellen. Aber manchmal habe ich Lust zu fummeln - ich werde mich hier ein wenig ändern, nichts wird kaputt gehen, denn die Wahrheit ist. Natürlich! Mach weiter!

Wenn Migrationen nach der Überprüfung unterzeichnet würden, wäre es möglich, die Anwendung von Entwürfen für die Bereitstellung zu verbieten. Und es wäre möglich, nicht nur die Migrationskennung im changelog zu speichern, sondern auch die checksum - ebenfalls nützlich.

Kehre zurück wie es war


Eine besonders heimtückische Wendung passiert, wenn eine Aufgabe abgesagt wird: Sie haben es getan, getan und ihre Meinung geändert. Es ist eine normale Situation. Sobald der Code nicht mehr benötigt wird, sollte der Zweig gelöscht werden. Und es gab Migration ... und sie ist bereits in der Inszenierung ... ah, ... oops. Ein guter Grund zu prüfen, ob Sie die Sicherung des Repositorys wiederherstellen können. Obwohl ich mich daran erinnere, dass es vielleicht einfacher war.

Gleichzeitig ist Migration Text. Und es wäre möglich, diesen Text dort im changelog zu speichern. Wenn die Migration vom Code dann weg ist, spielt es keine Rolle, aus welchen Gründen, sie kann jederzeit zurückgesetzt werden. Und sogar automatisch.

Wieder rückgängig machen


Der UNDO-Bereich wird definitiv benötigt. Aber warum es schreiben? Natürlich gibt es eingängige Fälle, aber die meisten Änderungen sind CREATE TABLE oder ADD COLUMN oder CREATE INDEX . Für sie könnte das Dienstprogramm automatisch umgekehrte Operationen direkt unter Verwendung von SQL-Code generieren. Natürlich gibt es eine Besonderheit. CREATE TABLE ${name} - Dies ist ein so besonderes Team, das plötzlich nicht mehr dem Standard entspricht. Ja, und um DROP TABLE ${name} zu generieren, müssen Sie in der Lage sein, den Ausdruck bis zum dritten Wort zu analysieren. Dies ist jedoch im Allgemeinen eine vollständig realisierbare technische Aufgabe. Könnte out of the box sein.

Fazit


Natürlich finde ich Fehler. MyBatis Migrations wurde als einfaches und universelles Dienstprogramm konzipiert, das nur minimal an die Besonderheiten von Datenbanken gebunden ist. Und sie ist mehr als nur eine Rechtfertigung. Aber es scheint, dass ein paar kleine Verbesserungen es viel besser machen würden, besonders wenn es über große Entfernungen verwendet wird.
- -
Dmitry Mamonov / Wrike

Source: https://habr.com/ru/post/de414441/


All Articles