
In diesem Artikel wird ausführlich erklärt, wie man Probleme mit der Datenbankkompatibilität beim Deployment löst. Wir werden erläutern, was mit Ihren Anwendungen in der Produktion passieren kann, wenn Sie versuchen, ein Deployment ohne vorherige Vorbereitung durchzuführen. Anschließend durchlaufen wir die Phasen des Anwendungslebenszyklus, die erforderlich sind, um eine null Downtime zu erreichen (Anmerkung: im Folgenden — zero downtime). Das Ergebnis unserer Maßnahmen wird die Anwendung einer nicht rückwärtskompatiblen Datenbankänderung auf rückwärtskompatible Weise sein.
Wenn Sie die Codebeispiele aus dem Artikel durchgehen möchten, finden Sie diese unter .
Einführung
Zero downtime deployment
Was steckt hinter dem mysteriösen zero downtime deployment? Можно сказать, это когда ваше приложение развернуто так, что вы можете успешно вводить новую версию приложения на продакшн, в то время как пользователь не замечает его недоступности. С точки зрения пользователя и компании, это наилучший из возможных сценариев деплоя, поскольку таким образом можно вводить новые функции и устранять ошибки без перебоев в работе.
Wie erreichen wir das? Es gibt mehrere Möglichkeiten, hier ist eine davon:
- Implementieren Sie Version Nr. 1 Ihres Dienstes
- Führen Sie die DB-Migration durch
- Implementieren Sie parallel Version Nr. 2 Ihres Dienstes zu Version Nr. 1
- Sobald Sie sehen, dass Version Nr. 2 wie gewünscht funktioniert, entfernen Sie Version Nr. 1
- Fertig!
Einfach, oder? Leider ist das nicht so einfach, und wir werden das später ausführlicher besprechen. Aber jetzt schauen wir uns einen weiteren recht verbreiteten Deployment-Prozess an – den Blue-Green-Deployment.
Haben Sie schon von ? С Cloud Foundry это чрезвычайно легко сделать. Просто гляньте на gehört? Hier beschreiben wir das genauer. Um es kurz zusammenzufassen, erinnern wir uns daran, wie man ein Blue-Green-Deployment durchführt:
- stellen Sie sicher, dass zwei Kopien Ihres Produktionscodes („blue“ und „green“) vorhanden sind;
- leiten Sie den gesamten Verkehr zur blauen Umgebung, d.h. die Produktions-URLs sollten dorthin zeigen;
- stellen Sie alle Änderungen der Anwendung in der grünen Umgebung bereit und testen Sie sie;
- wechseln Sie die URLs von der blauen auf die grüne Umgebung.
Das Blue-Green-Deployment ist ein Ansatz, der es Ihnen ermöglicht, neue Funktionen problemlos einzuführen, ohne sich Sorgen machen zu müssen, dass der Produktionsbetrieb ausfällt. Dies liegt daran, dass selbst wenn etwas schiefgeht, Sie einfach zur vorherigen Umgebung zurückkehren können, indem Sie einfach den ‚Schalter umlegen‘.
Nachdem Sie all dies gelesen haben, könnten Sie sich fragen: Wie hängt Zero Downtime mit dem Blue-Green-Deployment zusammen?
Nun, sie haben viel gemeinsam, da die Unterstützung von zwei Kopien derselben Umgebung doppelte Anstrengungen für deren Wartung erfordert. Aus diesem Grund halten einige Teams, wie von , behauptet wird, an einer Variante dieses Ansatzes fest:
Eine weitere Möglichkeit besteht darin, dieselbe Datenbank zu verwenden und Blau-Grün-Schalter für die Web- und Domain-Ebenen zu erstellen. In diesem Ansatz können Datenbanken oft problematisch sein, insbesondere wenn Sie das Schema ändern müssen, um eine neue Softwareversion zu unterstützen.
Hier kommen wir zum Hauptproblem dieses Artikels. DatenbankSchauen wir uns diesen Satz noch einmal an.
Führen Sie die Datenbankmigration durch.
Jetzt sollten Sie sich die Frage stellen – was passiert, wenn die Änderung der Datenbank nicht abwärtskompatibel ist? Wird meine erste Version der Anwendung kaputtgehen? Tatsächlich wird genau das passieren...
Trotz der enormen Vorteile von Zero Downtime / Blue-Green-Deployment neigen Unternehmen dazu, dem folgenden sichereren Prozess bei der Bereitstellung ihrer Anwendungen zu folgen:
- Ein Paket mit der neuen Version der Anwendung vorbereiten
- Die laufende Anwendung stoppen
- Skripte zur Migration der Datenbank ausführen
- Die neue Version der Anwendung bereitstellen und starten
In diesem Artikel werden wir detailliert beschreiben, wie Sie mit der Datenbank und dem Code arbeiten können, um von den Vorteilen des Zero Downtime Deployments zu profitieren.
Probleme mit der Datenbank
Wenn Sie eine zustandslose Anwendung haben, die keine Daten in einer Datenbank speichert, können Sie sofort ein Zero-Downtime-Deployment erreichen. Leider muss die meiste Software irgendwo Daten speichern. Deshalb sollten Sie sorgfältig überlegen, bevor Sie Änderungen am Schema vornehmen. Bevor wir uns näher mit den Details befassen, wie das Schema so geändert werden kann, dass ein Deployment ohne Ausfallzeit möglich wird, konzentrieren wir uns zunächst auf das Versionsmanagementschema.
Versionsmanagementschema
In diesem Artikel verwenden wir als Werkzeug für das Versionsmanagement (Anm. d. Red.: es geht um Datenbankmigrationen). Natürlich werden wir auch eine Spring-Boot-Anwendung schreiben, die Flyway integriert und das Schema während der Initialisierung des Anwendungskontexts migriert. Mit Flyway können Sie Migrationsskripte im Ordner Ihrer Projekte speichern (standardmäßig in classpath:db/migration). Hier sehen Sie ein Beispiel für solche Migrationsdateien
└── db
└── migration
├── V1__init.sql
├── V2__Add_surname.sql
├── V3__Final_migration.sql
└── V4__Remove_lastname.sqlIn diesem Beispiel sehen wir 4 Migrationsszenarien, die, falls sie zuvor noch nicht durchgeführt wurden, nacheinander beim Starten der Anwendung ausgeführt werden. Lassen Sie uns eine der Dateien (V1__init.sql) als Beispiel betrachten.
CREATE TABLE PERSON (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
first_name varchar(255) NOT NULL,
last_name varchar(255) NOT NULL
);
INSERT INTO PERSON (first_name, last_name) VALUES ('Dave', 'Syer');Alles spricht für sich selbst: Sie können SQL nutzen, um festzulegen, wie Ihre Datenbank verändert werden soll. Für weitere Informationen zu Spring Boot und Flyway lesen Sie die .
Durch die Verwendung eines Versionsverwaltungstools mit Spring Boot erhalten Sie 2 große Vorteile:
- Sie trennen Datenbankänderungen von Codeänderungen
- die Migration der Datenbank erfolgt zusammen mit der Bereitstellung Ihrer Anwendung, d.h. Ihr Deploy-Prozess wird vereinfacht
Datenbankprobleme lösen
Im nächsten Abschnitt des Artikels konzentrieren wir uns auf zwei Ansätze zur Datenbankänderung.
- Rückwärtsinkompatibilität
- Rückwärtskompatibilität
Der erste Punkt dient als Warnung, dass man ohne vorherige Vorbereitung keine Zero Downtime-Bereitstellungen durchführen sollte. Der zweite Punkt bietet eine Lösung, wie man Bereitstellungen ohne Ausfallzeiten durchführen und gleichzeitig die Abwärtskompatibilität wahren kann.
Unser Projekt, an dem wir arbeiten werden, ist eine einfache Spring Boot Flyway-Anwendung mit Person mit first_name und last_name in der Datenbank (Anm. d. Ü.: Person ist die Tabelle und first_name und last_name — das Feld darin). Wir möchten last_name in surname.
Annahmen
Bevor wir in die Details eintauchen, müssen wir einige Annahmen bezüglich unserer Anwendungen festlegen. Das Hauptziel, das wir erreichen möchten, ist ein recht einfacher Prozess.
Hinweis. Business PRO-TIPP: Die Vereinfachung von Prozessen kann Ihnen viel Geld bei der Unterstützung sparen (je mehr Personen in Ihrem Unternehmen tätig sind, desto mehr Geld können Sie sparen)!
Ein Rollback der Datenbank ist nicht erforderlich.
Dies vereinfacht den Deployment-Prozess (manche Datenbank-Rollbacks sind praktisch unmöglich, zum Beispiel das Zurücksetzen einer Löschung). Wir ziehen es vor, nur Anwendungen zurückzusetzen. So sieht Ihr Deployment-Pipeline selbst mit unterschiedlichen Datenbanken (wie SQL und NoSQL) gleich aus.
Es muss IMMER die Möglichkeit bestehen, die Anwendung um eine Version zurückzusetzen (nicht mehr).
Ein Rollback sollte nur bei Bedarf erfolgen. Wenn die aktuelle Version einen Fehler enthält, der schwer zu beheben ist, müssen wir in der Lage sein, die letzte funktionierende Version zurückzubringen. Wir gehen davon aus, dass diese letzte funktionierende Version die vorherige ist. Die Unterstützung von Code- und Datenbankkompatibilität für mehr als ein Release wäre äußerst schwierig und kostspielig.
Hinweis. Zur besseren Lesbarkeit werden wir in diesem Artikel die Hauptversion der Anwendung ändern.
Schritt 1: Ausgangszustand
Anwendungsversion: 1.0.0
Datenbankversion: v1
Kommentar
Dies wird der Ausgangszustand der Anwendung sein.
Datenbankänderungen
Die Datenbank enthält last_name.
CREATE TABLE PERSON (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
first_name varchar(255) not null,
last_name varchar(255) not null
);
insert into PERSON (first_name, last_name) values ('Dave', 'Syer');Änderungen am Code
Die Anwendung speichert die Personendaten in last_name:
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.flyway;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastname) {
this.lastName = lastname;
}
@Override
public String toString() {
return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName
+ "]";
}
}Nicht zurückwärtskompatible Umbenennung der Spalte
Lassen Sie uns ein Beispiel betrachten, wie man den Spaltennamen ändert:
Achtung. Das folgende Beispiel wird absichtlich zu einem Fehler führen. Wir zeigen dies, um das Kompatibilitätsproblem der Datenbank zu demonstrieren.
Anwendungsversion: 2.0.0.BAD
DB-Version: v2bad
Kommentar
Die aktuellen Änderungen erlauben es uns NICHT, zwei Instanzen (alte und neue) gleichzeitig auszuführen. Daher wird ein Zero-Downtime-Deployment schwer erreichbar sein (wenn man die Annahmen berücksichtigt, ist es tatsächlich unmöglich).
A/B-Testing
Die aktuelle Situation ist so, dass wir eine Anwendung der Version 1.0.0, in der Produktion haben, und die DB v1. Wir müssen eine zweite Instanz der Anwendung, Version 2.0.0.BAD, ausrollen und die Datenbank auf v2bad.
Schritte:
- eine neue Instanz der Anwendung der Version
2.0.0.BAD, die die Datenbank aufv2bad - in der Datenbank
v2badSpaltelast_nameexistiert nicht mehr – sie wurde geändert insurname - das Update der Datenbank und der Anwendung war erfolgreich, und einige Instanzen laufen in
1.0.0, andere in2.0.0.BAD. Alle sind mit der DBv2bad - alle Instanzen der Version
1.0.0werden Fehler ausgeben, weil sie versuchen, Daten in die Spaltelast_name, die es nicht mehr gibt, einzufügen - alle Instanzen der Version
2.0.0.BADwerden problemlos funktionieren
Wie Sie sehen, ist A/B-Testing nicht möglich, wenn wir nicht abwärtskompatible Änderungen an der Datenbank und der Anwendung vornehmen.
Anwendungs-Rollback
Angenommen, wir haben nach dem Versuch eines A/B-Deployments (Anmerkung: Hier meinte der Autor wahrscheinlich A/B-Testing) entschieden, dass wir die Anwendung auf die Version zurücksetzen müssen 1.0.0. Nehmen wir an, wir möchten kein Datenbank-Rollback durchführen.
Schritte:
- wir stoppen die Instanz der Anwendung in Version
2.0.0.BAD - die Datenbank ist weiterhin
v2bad - da die Version
1.0.0nicht versteht, was das istsurname, werden wir Fehler sehen - die Hölle ist losgebrochen, wir können nicht mehr zurück
Wie Sie sehen, können wir nicht mehr zur vorherigen Version zurückkehren, wenn wir nicht abwärtskompatible Änderungen an der Datenbank und der Anwendung durchführen.
Ausführungsprotokoll des Skripts
Szenario mit fehlender Rückwärtskompatibilität:
01) Führen Sie 1.0.0 aus
02) Warten Sie, bis die Anwendung (1.0.0) gestartet ist
03) Erstellen Sie eine Person, indem Sie POST localhost:9991/person für die Version 1.0.0 aufrufen
04) Führen Sie 2.0.0.BAD aus
05) Warten Sie, bis die Anwendung (2.0.0.BAD) gestartet ist
06) Erstellen Sie eine Person, indem Sie POST localhost:9991/person für die Version 1.0.0 aufrufen <-- dies sollte fehlschlagen
07) Erstellen Sie eine Person, indem Sie POST localhost:9992/person für die Version 2.0.0.BAD aufrufen <-- dies sollte erfolgreich sein
Starte die Anwendung in Version 1.0.0
Erstelle eine Person in Version 1.0.0
Sende einen POST an 127.0.0.1:9991/person. Dies ist die Antwort:
{"firstName":"b73f639f-e176-4463-bf26-1135aace2f57","lastName":"b73f639f-e176-4463-bf26-1135aace2f57"}
Starte die Anwendung in Version 2.0.0.BAD
Erstelle eine Person in Version 1.0.0
Sende einen POST an 127.0.0.1:9991/person. Dies ist die Antwort:
curl: (22) Die angeforderte URL gab den Fehler zurück: 500 Interner Serverfehler
Erstelle eine Person in Version 2.0.0.BAD
Sende einen POST an 127.0.0.1:9995/person. Dies ist die Antwort:
{"firstName":"e156be2e-06b6-4730-9c43-6e14cfcda125","surname":"e156be2e-06b6-4730-9c43-6e14cfcda125"}Datenbankänderungen
Migrationsskript, das umbenennt last_name in surname
Ursprüngliches Flyway-Skript:
CREATE TABLE PERSON (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
first_name varchar(255) not null,
last_name varchar(255) not null
);
insert into PERSON (first_name, last_name) values ('Dave', 'Syer');Skript, das umbenennt last_name.
-- Diese Änderung ist nicht rückwärtskompatibel - A/B-Tests sind nicht möglich
ALTER TABLE PERSON CHANGE last_name surname VARCHAR;Änderungen am Code
Wir haben den Namen des Feldes geändert lastName findet man surname.
Umbenennung der Spalte auf rückwärtskompatible Weise
Dies ist die häufigste Situation, auf die wir stoßen können. Wir müssen rückwärts inkompatible Änderungen vornehmen. Wir haben bereits bewiesen, dass wir für bereichsübergreifende Deployment ohne Ausfallzeiten nicht einfach die Datenbankmigration anwenden sollten, ohne zusätzliche Maßnahmen zu ergreifen. In diesem Abschnitt des Artikels werden wir 3 App-Deployments zusammen mit den Datenbankmigrationen durchführen, um das gewünschte Ergebnis zu erzielen und gleichzeitig die Abwärtskompatibilität zu wahren.
Hinweis. Erinnern wir uns, dass wir eine DB-Version haben,
v1. Diese enthält die Spaltenfirst_nameundlast_name. Wir müssen ändernlast_namefindet mansurname. Wir haben auch eine App-Version,1.0.0,die bisher noch nicht nutztsurname.
Schritt 2: Fügen Sie den Nachnamen hinzu.
Anwendungsversion: 2.0.0
Datenbankversion: v2
Kommentar
Indem wir eine neue Spalte hinzufügen und deren Inhalte kopieren, schaffen wir rückwärtskompatible Änderungen in der Db. Gleichzeitig, falls wir ein JAR zurücksetzen oder ein funktionierendes altes JAR haben, wird es während der Ausführung nicht beschädigt.
Wir bringen die neue Version heraus.
Schritte:
- Migrieren Sie die DB, um die neue Spalte zu erstellen,
surname. Jetzt ist Ihre DB-Versionv2 - Kopieren Sie die Daten von
last_nameinsurname. Bitte beachten Sie, was bedeutet, wenn Sie viele dieser Daten haben, sollten Sie über eine batchweise Migration nachdenken! - Schreiben Sie den Code, wo die verwendet werden BOTH und neu, und alt Spalte. Jetzt hat Ihre App-Version
2.0.0 - lesen Sie den Wert aus der Spalte
surname, wenn er nichtnull, oder aus lNachname, wennsurnamenicht angegeben. Sie könnengetLastName()aus dem Code entfernen, da esnullbei der Rücksetzung Ihrer Anwendung mit3.0.0bis zu2.0.0.
Wenn Sie Spring Boot Flyway verwenden, werden diese beiden Schritte beim Starten der Version 2.0.0 der Anwendung ausgeführt. Wenn Sie das Datenbank-Versionierungstool manuell ausführen, müssen Sie hierfür zwei verschiedene Schritte ausführen (aktualisieren Sie zuerst die DB-Version manuell und implementieren Sie dann die neue Anwendung).
Wichtig. Denken Sie daran, dass die neu erstellte Spalte NICHT DÜRFE Teil NOT NULL. Wenn Sie zurücksetzen, weiß die alte Anwendung nichts von der neuen Spalte und setzt diese während des
Insert nicht fest.Aber wenn Sie diese Einschränkung hinzufügen und Ihre DBv2, erfordert dies die Festlegung eines Wertes für die neue Spalte. Was zu Einschränkungsverstößen führen wird.Wichtig. Sie sollten die Methode
getLastName()entfernen, da in der Version3.0.0das Konzept der Spalte im Code fehltlast_name. Das bedeutet, dass dort null gesetzt werden. Sie können die Methode beibehalten und Überprüfungen hinzufügen fürnull, aber eine viel bessere Lösung wäre, sicherzustellen, dass in der LogikgetSurname()Sie den richtigen nicht-null Wert gewählt haben.
A/B-Testing
Die aktuelle Situation ist so, dass wir eine Anwendung der Version 1.0.0, bereitgestellt in der Produktion, und die DB in v1. Wir müssen eine zweite Instanz der Version der Anwendung bereitstellen 2.0.0, die die Datenbank auf den Stand von v2.
Schritte:
- eine neue Instanz der Anwendung der Version
2.0.0, die die Datenbank aufv2 - aktualisiert; währenddessen wurden einige Anfragen von Instanzen der Version
1.0.0 - bearbeitet. Das Update war erfolgreich, und Sie haben mehrere funktionierende Instanzen der Version
1.0.0und die anderen Versionen.2.0.0.Alle kommunizieren mit der Datenbank inv2 - Version
1.0.0verwendet in der Datenbank nicht die Spalte surname, während die Version2.0.0dies tut. Sie stören sich nicht gegenseitig, und es sollten keine Fehler auftreten. - Version
2.0.0speichert Daten sowohl in der alten als auch in der neuen Spalte, was Rückwärtskompatibilität gewährleistet.
Wichtig. Wenn Sie Anfragen haben, die Elemente basierend auf Werten aus der alten / neuen Spalte zählen, sollten Sie beachten, dass jetzt eine Duplizierung der Werte vorliegt (höchstwahrscheinlich befinden sich diese immer noch in der Migration). Wenn Sie beispielsweise die Anzahl der Benutzer zählen möchten, deren Nachname (wie auch immer die Spalte genannt wird) mit dem Buchstaben
Abeginnt, könnten Sie bis zum Abschluss der Datenmigration (alt→neuSpalte) inkonsistente Daten erhalten, wenn Sie eine Abfrage an die neue Spalte ausführen.
Anwendungs-Rollback
Jetzt haben wir die Anwendung der Version 2.0.0 und die Datenbank in v2.
Schritte:
- setzen Sie Ihre Anwendung auf die Version zurück
1.0.0. - Version
1.0.0verwendet in der Datenbank nicht die Spaltesurname, daher sollte das Rollback erfolgreich sein
Änderungen an der DB
Die DB enthält eine Spalte mit dem Namen last_name.
Ursprüngliches Flyway-Skript:
CREATE TABLE PERSON (
id BIGINT GENERATED BY DEFAULT AS IDENTITY,
first_name varchar(255) not null,
last_name varchar(255) not null
);
insert into PERSON (first_name, last_name) values ('Dave', 'Syer');Skript zum Hinzufügen surname.
Achtung. Bitte beachten Sie, dass keine NOT NULL-Einschränkungen in die hinzuzufügende Spalte eingefügt werden dürfen. Wenn Sie das JAR zurücksetzen, wird die alte Version keine Kenntnis von der hinzugefügten Spalte haben und ihr automatisch den Wert NULL zuweisen. Bei Vorhandensein einer solchen Einschränkung würde die alte Anwendung einfach nicht mehr funktionieren.
-- HINWEIS: Dieses Feld kann nicht die NOT NULL-Bedingung haben, da die alte Version bei einem Rollback nichts über dieses Feld weiß
-- und es immer auf NULL setzen wird
ALTER TABLE PERSON ADD surname varchar(255);
-- WIR GEHEN DAVON AUS, DASS ES SICH UM EINEN SCHNELLEN MIGRATIONSPROZESS HANDELT - ANDERNFALLS MÜSSTEN WIR IN BATCHES MIGRIEREN
UPDATE PERSON SET PERSON.surname = PERSON.last_nameÄnderungen am Code
Wir speichern die Daten sowohl in last_nameals auch in surname. Dabei lesen wir aus last_name, da diese Spalte am relevantesten ist. Während des Deployments könnten einige Anfragen von einer Instanz der Anwendung bearbeitet worden sein, die noch nicht aktualisiert wurde.
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.flyway;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
private String surname;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* Reading from the new column if it's set. If not the from the old one.
*
* When migrating from version 1.0.0 -> 2.0.0 this can lead to a possibility that some data in
* the surname column is not up to date (during the migration process lastName could have been updated).
* In this case one can run yet another migration script after all applications have been deployed in the
* new version to ensure that the surname field is updated.
*
* However it makes sense since when looking at the migration from 2.0.0 -> 3.0.0. In 3.0.0 we no longer
* have a notion of lastName at all - so we don't update that column. If we rollback from 3.0.0 -> 2.0.0 if we
* would be reading from lastName, then we would have very old data (since not a single datum was inserted
* to lastName in version 3.0.0).
*/
public String getSurname() {
return this.surname != null ? this.surname : this.lastName;
}
/**
* Storing both FIRST_NAME and SURNAME entries
*/
public void setSurname(String surname) {
this.lastName = surname;
this.surname = surname;
}
@Override
public String toString() {
return "Person [firstName=" + this.firstName + ", lastName=" + this.lastName + ", surname=" + this.surname
+ "]";
}
}Schritt 3: Entfernen des last_name aus dem Code
Anwendungsversion: 3.0.0
DB-Version:v3
Kommentar
Anm. d. Übs.: Offensichtlich wurde in dem ursprünglichen Artikel der Text dieses Abschnitts fälschlicherweise aus Schritt 2 kopiert. In diesem Schritt sollten Änderungen am Anwendungscode vorgenommen werden, die darauf abzielen, die Funktionalität, die die Spalte verwendet, zu entfernen. last_name.
Durch das Hinzufügen einer neuen Spalte und das Kopieren ihrer Inhalte haben wir rückwärtskompatible Datenbankänderungen erstellt. Sollte es notwendig sein, ein JAR zurückzusetzen oder wir einen funktionierenden alten JAR haben, bricht es während der Ausführung nicht.
Anwendungs-Rollback
Momentan haben wir die App-Version 3.0.0 und die Datenbank v3. Die Version 3.0.0 speichert keine Daten in last_name. Das bedeutet, dass in surname die aktuellsten Informationen gespeichert sind.
Schritte:
- setzen Sie Ihre Anwendung auf die Version zurück
2.0.0. - Version
2.0.0verwendet undlast_nameundsurname. - Version
2.0.0nimmt auf,surnamewenn es nicht null ist, andernfalls —last_name
Datenbankänderungen
Es gibt keine strukturellen Änderungen in der Datenbank. Das folgende Skript führt die endgültige Migration der alten Daten durch:
-- WIR GEHEN DAVON AUS, DASS ES SICH UM EINE SCHNELLE MIGRATION HANDELT - ANDERSON WÜRDE MAN IN BATCHES MIGRIEREN MÜSSEN
-- WIR ÜBERPRÜFEN AUCH NICHT, OB WIR EXISTIERENDE EINTRÄGE ÜBERSCHREIBEN. WIR MÜSSTEN EINTRAGSVERSIONEN VERGLEICHEN,
-- UM SICHERZUSTELLEN, DASS WIR WENN SCHON EINEN EINTRAG MIT EINER HÖHEREN VERSION NUMMER HABEN,
-- DIESEN NICHT ÜBERSCHREIBEN.
UPDATE PERSON SET PERSON.nachname = PERSON.vorname;
-- DAS NICHT-NULL-ALTER WIRD HIER GELÖSCHT; ANSONSTEN WERDEN SIE VERSUCHEN, WERT NULL FÜR DEN VORNAMEN EINZUFÜGEN,
-- WENN EIN NOT_NULL-ALTER BESTEHT.
ALTER TABLE PERSON MODIFY COLUMN vorname varchar(255) NULL DEFAULT NULL;Änderungen am Code
Anmerkung: Die Beschreibung dieses Blocks wurde ebenfalls irrtümlich vom Autor aus Schritt 2 kopiert. Entsprechend der Erzähllogik des Artikels sollten die Änderungen am Code in diesem Schritt darauf abzielen, Elemente zu entfernen, die mit der Spalte arbeiten. last_name.
Wir speichern Daten sowohl in last_nameals auch in Nachname. Darüber hinaus lesen wir aus der Spalte last_name, da dies am relevantesten ist. Während des Deployments können einige Anfragen von einer Instanz bearbeitet werden, die noch nicht aktualisiert wurde.
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.flyway;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String surname;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSurname() {
return this.surname;
}
public void setSurname(String lastname) {
this.surname = lastname;
}
@Override
public String toString() {
return "Person [firstName=" + this.firstName + ", surname=" + this.surname
+ "]";
}
}Schritt 4: Entfernen von last_name aus der DB
Anwendungsversion: 4.0.0
Datenbankversion: v4
Kommentar
Da der Versionscode 3.0.0 die Spalte nicht verwendet hat, last_name, wird während der Ausführung nichts Schlimmes passieren, wenn wir auf 3.0.0 nach dem Löschen der Spalte aus der Datenbank zurückkehren.
Ausführungsprotokoll des Skripts
Wir werden es folgendermaßen durchführen:
01) Starte 1.0.0
02) Warte, bis die App (1.0.0) hochgefahren ist
03) Erstelle eine Person, indem du POST localhost:9991/person für Version 1.0.0 aufrufst
04) Starte 2.0.0
05) Warte, bis die App (2.0.0) hochgefahren ist
06) Erstelle eine Person, indem du POST localhost:9991/person für Version 1.0.0 aufrufst
07) Erstelle eine Person, indem du POST localhost:9992/person für Version 2.0.0 aufrufst
08) Beende die App (1.0.0)
09) Starte 3.0.0
10) Warte, bis die App (3.0.0) hochgefahren ist
11) Erstelle eine Person, indem du POST localhost:9992/person für Version 2.0.0 aufrufst
12) Erstelle eine Person, indem du POST localhost:9993/person für Version 3.0.0 aufrufst
13) Beende die App (3.0.0)
14) Starte 4.0.0
15) Warte, bis die App (4.0.0) hochgefahren ist
16) Erstelle eine Person, indem du POST localhost:9993/person für Version 3.0.0 aufrufst
17) Erstelle eine Person, indem du POST localhost:9994/person für Version 4.0.0 aufrufst
Starte die App in Version 1.0.0
Erstelle eine Person in Version 1.0.0
Sende einen POST an 127.0.0.1:9991/person. Dies ist die Antwort:
{"firstName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2","lastName":"52b6e125-4a5c-429b-a47a-ef18bbc639d2"}
Starte die App in Version 2.0.0
Erstelle eine Person in Version 1.0.0
Sende einen POST an 127.0.0.1:9991/person. Dies ist die Antwort:
{"firstName":"e41ee756-4fa7-4737-b832-e28827a00deb","lastName":"e41ee756-4fa7-4737-b832-e28827a00deb"}
Erstelle eine Person in Version 2.0.0
Sende einen POST an 127.0.0.1:9992/person. Dies ist die Antwort:
{"firstName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","lastName":"0c1240f5-649a-4bc5-8aa9-cff855f3927f","surname":"0c1240f5-649a-4bc5-8aa9-cff855f3927f"}
Beende die App 1.0.0
Starte die App in Version 3.0.0
Erstelle eine Person in Version 2.0.0
Sende einen POST an 127.0.0.1:9992/person. Dies ist die Antwort:
{"firstName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","lastName":"74d84a9e-5f44-43b8-907c-148c6d26a71b","surname":"74d84a9e-5f44-43b8-907c-148c6d26a71b"}
Erstelle eine Person in Version 3.0.0
Sende einen POST an 127.0.0.1:9993/person. Dies ist die Antwort:
{"firstName":"c6564dbe-9ab5-40ae-9077-8ae6668d5862","surname":"c6564dbe-9ab5-40ae-9077-8ae6668d5862"}
Beende die App 2.0.0
Starte die App in Version 4.0.0
Erstelle eine Person in Version 3.0.0
Sende einen POST an 127.0.0.1:9993/person. Dies ist die Antwort:
{"firstName":"cbe942fc-832e-45e9-a838-0fae25c10a51","surname":"cbe942fc-832e-45e9-a838-0fae25c10a51"}
Erstelle eine Person in Version 4.0.0
Sende einen POST an 127.0.0.1:9994/person. Dies ist die Antwort:
{"firstName":"ff6857ce-9c41-413a-863e-358e2719bf88","surname":"ff6857ce-9c41-413a-863e-358e2719bf88"}Datenbankänderungen
Bezüglich v3 wir entfernen einfach die Spalte last_name und fügen fehlende Einschränkungen hinzu.
-- SPALTE ENTFERNEN
ALTER TABLE PERSON DROP last_name;
-- EINSCHRÄNKUNGEN HINZUFÜGEN
UPDATE PERSON SET surname='' WHERE surname IS NULL;
ALTER TABLE PERSON ALTER COLUMN surname VARCHAR NOT NULL;Änderungen am Code
Es gibt keine Änderungen im Code.
Fazit
Wir haben erfolgreich eine inkompatible Änderung des Spaltennamens zurückgesetzt, indem wir mehrere abwärtskompatible Deployments durchgeführt haben. Hier ist eine Zusammenfassung der durchgeführten Maßnahmen:
- Deployment der Anwendung Version
1.0.0mitv1Datenbankschema (Spaltenname =last_name) - Deployment der Anwendung Version
2.0.0,der Daten speichert inlast_nameundsurname. Die Anwendung liest auslast_name. Die Datenbank befindet sich in Versionv2, die Spalten wielast_name, als auchsurname enthält. surnameist eine Kopie von lNachname. (HINWEIS: Diese Spalte sollte keine Not-Null-Einschränkung haben) - Deployment der Anwendung Version
3.0.0, die Daten nur insurnamespeichert und aus surname liest. Was die Datenbank betrifft, erfolgt die letzte Migrationlast_nameinsurname. Auch die Einschränkung NOT NULL wird vonlast_name. Die Datenbank ist jetzt in Versionv3 - Deployment der Anwendung Version
4.0.0— im Code werden keine Änderungen vorgenommen. Deployment der Datenbankv4, die entferntlast_name. Hier können Sie alle fehlenden Einschränkungen in der Datenbank hinzufügen.
Mit diesem Ansatz können Sie immer eine Version zurückrollen, ohne die Kompatibilität der Datenbank / Anwendung zu brechen.
Code
Der gesamte in diesem Artikel verwendete Code ist verfügbar auf . Hier ist eine zusätzliche Beschreibung.
Projekte
Nach dem Klonen des Repositories sehen Sie die folgende Ordnerstruktur.
├── boot-flyway-v1 - Version 1.0.0 der App mit v1 des Schemas
├── boot-flyway-v2 - Version 2.0.0 der App mit v2 des Schemas (rückwärtskompatibel - die App kann zurückgesetzt werden)
├── boot-flyway-v2-bad - Version 2.0.0.BAD der App mit v2bad des Schemas (nicht rückwärtskompatibel - die App kann nicht zurückgesetzt werden)
├── boot-flyway-v3 - Version 3.0.0 der App mit v3 des Schemas (die App kann zurückgesetzt werden)
└── boot-flyway-v4 - Version 4.0.0 der App mit v4 des Schemas (die App kann zurückgesetzt werden)Skripte
Sie können die Skripte aus den folgenden Beispielskripten ausführen, die rückwärtskompatible und nicht rückwärtskompatible Änderungen in der Datenbank demonstrieren.
Um zu sehen ein Beispiel für rückwärtskompatible Änderungen, führen Sie aus:
./scripts/scenario_backward_compatible.shUm zu sehen ein Beispiel für nicht rückwärtskompatible Änderungen, führen Sie aus:
./scripts/scenario_backward_incompatible.shSpring Boot Sample Flyway
Alle Beispiele stammen von Spring Boot Sample Flyway.
Sie können einen Blick darauf werfen http://localhost:8080/flyway, dort ist die Liste der Skripte.
Dieses Beispiel enthält auch die H2-Konsole (unter http://localhost:8080/h2-console), damit Sie den Zustand der Datenbank ansehen können (Standard-URL jdbc — jdbc:h2:mem:testdb).
Zusätzlich
Lesen Sie auch andere Artikel in unserem Blog:
Quelle: habr.com
