Jak zarządzasz bazami danych w fazie rozwoju, testowania i produkcji?

171

Trudno mi było znaleźć dobre przykłady zarządzania schematami bazy danych i danymi między serwerami programistycznymi, testowymi i produkcyjnymi.

Oto nasza konfiguracja. Każdy programista ma maszynę wirtualną, na której działa nasza aplikacja i baza danych MySQL. To ich osobista piaskownica, aby robić, co chcą. Obecnie programiści dokonają zmiany w schemacie SQL i wykonają zrzut bazy danych do pliku tekstowego, który zatwierdzą do SVN.

Chcemy wdrożyć serwer programistyczny z ciągłą integracją, na którym zawsze będzie działać najnowszy zatwierdzony kod. Jeśli zrobimy to teraz, baza danych zostanie ponownie załadowana z SVN dla każdej kompilacji.

Mamy serwer testowy (wirtualny), na którym działają „kandydaci do wydania”. Wdrażanie na serwer testowy jest obecnie bardzo ręcznym procesem i zwykle wymaga załadowania najnowszego kodu SQL z SVN i poprawienia go. Ponadto dane na serwerze testowym są niespójne. Otrzymujesz dane testowe, które ostatni programista zatwierdził na swoim serwerze piaskownicy.

Tam, gdzie wszystko się psuje, jest wdrożenie do produkcji. Ponieważ nie możemy zastąpić aktywnych danych danymi testowymi, wymaga to ręcznego ponownego utworzenia wszystkich zmian schematu. Gdyby było wiele zmian schematu lub skryptów konwersji w celu manipulowania danymi, może to być naprawdę skomplikowane.

Gdyby problem dotyczył tylko schematu, byłby to łatwiejszy problem, ale w bazie danych znajdują się „podstawowe” dane, które są również aktualizowane podczas opracowywania, takie jak metadane w tabelach zabezpieczeń i uprawnień.

To największa bariera, jaką widzę w dążeniu do ciągłej integracji i jednoetapowych kompilacji. Jak ty go rozwiązać?


Pytanie uzupełniające: jak śledzić wersje bazy danych, aby wiedzieć, które skrypty należy uruchomić, aby zaktualizować daną instancję bazy danych? Czy tabela wersji, o której wspomina Lance, znajduje się poniżej standardowej procedury?


Dzięki za odniesienie do Tarantino. Nie pracuję w środowisku .NET, ale ich strona wiki DataBaseChangeMangement okazała się bardzo pomocna. Szczególnie ta prezentacja Powerpoint (.ppt)

Mam zamiar napisać skrypt w Pythonie, który sprawdza nazwy *.sqlskryptów w danym katalogu w tabeli w bazie danych i uruchamia te, których nie ma, w kolejności na podstawie liczby całkowitej, która stanowi pierwszą część nazwy pliku. Jeśli jest to całkiem proste rozwiązanie, a podejrzewam, że będzie, to zamieszczę je tutaj.


Mam do tego działający skrypt. Obsługuje inicjalizację bazy danych, jeśli nie istnieje, i uruchamianie skryptów aktualizacji w razie potrzeby. Istnieją również przełączniki do czyszczenia istniejącej bazy danych i importowania danych testowych z pliku. Ma około 200 linii, więc nie opublikuję tego (chociaż mogę umieścić go na pastebinie, jeśli jest zainteresowanie).

Matt Miller
źródło
„Mam zamiar napisać skrypt w Pythonie, który sprawdza nazwy skryptów * .sql w danym katalogu w tabeli w bazie danych i uruchamia te, których nie ma, w kolejności na podstawie liczby całkowitej, która stanowi pierwszą część nazwę pliku. Jeśli jest to całkiem proste rozwiązanie, a podejrzewam, że będzie, opublikuję je tutaj ”. Wygląda na to, że wdrażasz flyway.
masterxilo

Odpowiedzi:

53

Jest kilka dobrych opcji. Nie użyłbym strategii „przywróć kopię zapasową”.

  1. Skryptuj wszystkie zmiany w schemacie i pozwól serwerowi CI uruchamiać te skrypty w bazie danych. Miej tabelę wersji, aby śledzić bieżącą wersję bazy danych i wykonywać skrypty tylko wtedy, gdy są przeznaczone dla nowszej wersji.

  2. Użyj rozwiązania do migracji. Te rozwiązania różnią się w zależności od języka, ale w przypadku platformy .NET używam Migrator.NET. Umożliwia to wersjonowanie bazy danych i przechodzenie między wersjami w górę iw dół. Twój schemat jest określony w kodzie C #.

Lance Fisher
źródło
28

Twoi programiści muszą napisać skrypty zmian (schemat i zmiana danych) dla każdego błędu / funkcji, nad którą pracują, a nie tylko zrzucić całą bazę danych do kontroli źródła. Te skrypty uaktualnią bieżącą produkcyjną bazę danych do nowej wersji w fazie rozwoju.

Twój proces budowania może przywrócić kopię produkcyjnej bazy danych do odpowiedniego środowiska i uruchomić w nim wszystkie skrypty z kontroli źródła, które zaktualizują bazę danych do aktualnej wersji. Robimy to codziennie, aby upewnić się, że wszystkie skrypty działają poprawnie.

tbreffni
źródło
13

Zobacz, jak robi to Ruby on Rails.

Po pierwsze, są to tzw. Pliki migracyjne, które w zasadzie przekształcają schemat bazy danych i dane z wersji N do wersji N + 1 (lub w przypadku przejścia z wersji N + 1 do N). Baza danych zawiera tabelę informującą o aktualnej wersji.

Testowe bazy danych są zawsze czyszczone przed testami jednostkowymi i wypełniane stałymi danymi z plików.

Juha Syrjälä
źródło
10

Książka Refactoring Databases: Evolutionary Database Design może dać ci kilka pomysłów, jak zarządzać bazą danych. Krótka wersja jest również dostępna pod adresem http://martinfowler.com/articles/evodb.html

W jednym projekcie PHP + MySQL miałem zapisany numer wersji bazy danych w bazie danych i kiedy program łączy się z bazą danych, najpierw sprawdzi wersję. Jeśli program wymaga innej wersji, otworzy stronę do aktualizacji bazy danych. Każda aktualizacja jest określona w kodzie PHP, co spowoduje zmianę schematu bazy danych i migrację wszystkich istniejących danych.

Esko Luontola
źródło
5
  • Nazwij swoje bazy danych w następujący sposób - dev_<<db>> , tst_<<db>> , stg_<<db>> , prd_<<db>>(Oczywiście nigdy nie należy zapisywać na stałe nazw baz danych
  • W ten sposób możesz wdrożyć nawet różne typy baz danych na tym samym serwerze fizycznym (nie polecam tego, ale być może będziesz musiał ... jeśli zasoby są ograniczone)
  • Upewnij się, że możesz automatycznie przenosić dane między nimi
  • Oddziel skrypty tworzenia bazy danych od populacji = Zawsze powinno być możliwe odtworzenie bazy danych od podstaw i wypełnienie jej (ze starej wersji bazy danych lub zewnętrznego źródła danych
  • nie używaj parametrów połączenia w kodzie (nawet nie w plikach konfiguracyjnych) - używaj w plikach konfiguracyjnych szablonów parametrów połączenia, które wypełniasz dynamicznie, każda rekonfiguracja application_layer, która wymaga ponownej kompilacji, jest ZŁA
  • korzystaj z wersjonowania baz danych i wersjonowania obiektów bazy danych - jeśli możesz sobie na to pozwolić, korzystaj z gotowych produktów, jeśli nie, stwórz coś samodzielnie
  • śledź każdą zmianę DDL i zapisz ją w jakiejś tabeli historii ( przykład tutaj )
  • CODZIENNE kopie zapasowe! Sprawdź, jak szybko byłbyś w stanie przywrócić coś utraconego z kopii zapasowej (użyj automatycznych skryptów przywracania
  • nawet twoja baza danych DEV i PROD mają dokładnie ten sam skrypt do tworzenia, będziesz miał problemy z danymi, więc pozwól programistom stworzyć dokładną kopię produktu i bawić się nią (wiem, że dostanę minusy za ten, ale zmiana w sposób myślenia i proces biznesowy będą Cię kosztować znacznie mniej, gdy gówno trafi do fanów - więc zmuś programistów do legalnego indeksowania indeksów niezależnie od tego, co robi, ale upewnij się, że to jedno
Yordan Georgiev
źródło
Ostatnią kwestią jest rzeczywiście nastrój. Jeśli to konieczne, pokazuje, że definicja projektu jest zepsuta. Rozwój musi prowadzić przed produkcją. Jeśli dane produkcyjne wywołują skutki uboczne, to pokazują większe problemy. Wyczyść dane produkcyjne. Wyjaśnij również ostatni krok z inspektorem ochrony danych, jeśli istnieje powód - jak sugerujesz - że dane w czasie rzeczywistym muszą znajdować się w systemach programistycznych, sprawdź, czy ma to prawne zastosowanie. Również dokładna kopia danych produkcyjnych w znacznym stopniu spowalnia rozwój i integrację. Rozważ mniej kosztowny proces, jeśli nie możesz sobie pozwolić na taki luksus.
hakre
Chodzi o to, że podczas rozwoju po prostu nie można nawet wyobrazić sobie wszystkich przypadków narożnych w przepływie sterowania i zmian w jakości danych, które będą miały miejsce w produkcji. Jeśli jesteś w tak dużej korporacji, aby mieć z tym problemy prawne, musisz zaimplementować jakieś rozwiązanie do szyfrowania i / lub maskowania danych, co dodaje dodatkową warstwę złożoności, ale nadal musi zachować aspekty jakości danych, które spowodowały błąd w każdym razie na pierwszym miejscu ...
Yordan Georgiev
4

To jest coś, z czego ciągle jestem niezadowolony - czyli nasze rozwiązanie tego problemu. Przez kilka lat utrzymywaliśmy osobny skrypt zmian dla każdego wydania. Ten skrypt zawierałby delty z ostatniej wersji produkcyjnej. Z każdym wydaniem aplikacji numer wersji będzie się zwiększał, dając coś w rodzaju:

  • dbChanges_1.sql
  • dbChanges_2.sql
  • ...
  • dbChanges_n.sql

Działało to wystarczająco dobrze, dopóki nie zaczęliśmy utrzymywać dwóch linii rozwoju: Trunk / Mainline dla nowego rozwoju i gałęzi konserwacji dla poprawek błędów, krótkoterminowych ulepszeń itp. Nieuchronnie pojawiła się potrzeba wprowadzenia zmian w schemacie w gałęzi. W tym momencie mieliśmy już dbChanges_n + 1.sql w pniu, więc skończyliśmy na schemacie takim jak poniżej:

  • dbChanges_n.1.sql
  • dbChanges_n.2.sql
  • ...
  • dbChanges_n.3.sql

Znowu to działało wystarczająco dobrze, aż pewnego dnia spojrzeliśmy w górę i zobaczyliśmy 42 skrypty delta w głównej linii i 10 w gałęzi. ARGH!

Obecnie po prostu utrzymujemy jeden skrypt delta i pozwalamy SVN na jego wersję - tj. Nadpisujemy skrypt przy każdym wydaniu. I unikamy zmian schematu w gałęziach.

Więc z tego też nie jestem zadowolony. Bardzo podoba mi się koncepcja migracji z Railsów. Zafascynowałem się LiquiBase . Obsługuje koncepcję przyrostowych refaktoryzacji baz danych. Warto zajrzeć i niedługo przyjrzę się temu szczegółowo. Czy ktoś ma z tym doświadczenie? Byłbym bardzo ciekawy twoich wyników.

Matt Stine
źródło
4

Możesz również przyjrzeć się użyciu narzędzia takiego jak porównanie SQL, aby skrypty różnic między różnymi wersjami bazy danych, umożliwiając szybką migrację między wersjami

Rad
źródło
3

Mamy bardzo podobną konfigurację do OP.

Deweloperzy tworzą maszyny wirtualne z prywatnymi bazami danych.

[Programiści wkrótce będą angażować się w prywatne oddziały]

Testowanie jest przeprowadzane na różnych maszynach (w rzeczywistości w maszynach wirtualnych hostowanych na serwerze) [Wkrótce zostanie uruchomione przez serwer Hudson CI]

Przetestuj, ładując zrzut odniesienia do bazy danych. Zastosuj poprawki schematów programistów, a następnie zastosuj poprawki danych programistów

Następnie uruchom testy jednostkowe i systemowe.

Produkcja jest wdrażana u klientów jako instalatorzy.

Co robimy:

Wykonujemy zrzut schematu naszej bazy danych piaskownicy. Następnie zrzut danych sql. Różnicujemy to z poprzednią linią bazową. ta para delt ma uaktualnić n-1 do n.

konfigurujemy zrzuty i delty.

Aby zainstalować wersję N CLEAN, uruchamiamy zrzut do pustej bazy danych. Aby założyć łatę, nałóż ją.

(Juha wspomniał, że pomysł Raila, aby mieć tabelę rejestrującą bieżącą wersję DB, jest dobry i powinien sprawić, że instalowanie aktualizacji będzie mniej obciążające).

Delty i zrzuty muszą zostać przejrzane przed testem beta. Nie widzę sposobu na obejście tego problemu, ponieważ widziałem, jak programiści wstawiali konta testowe do bazy danych dla siebie.

Tim Williscroft
źródło
3

Obawiam się, że zgadzam się z innymi plakatami. Programiści muszą zapisywać swoje zmiany.

W wielu przypadkach prosta ALTER TABELA nie zadziała, musisz też zmodyfikować istniejące dane - programiści muszą zastanowić się, jakie migracje są wymagane i upewnić się, że są poprawnie skrypty (oczywiście musisz to dokładnie przetestować w pewnym momencie cykl wydania).

Co więcej, jeśli masz jakiś sens, nakłonisz programistów do skryptu wycofywania zmian, aby można było je cofnąć w razie potrzeby. Należy to również przetestować, aby upewnić się, że ich wycofanie nie tylko zostanie wykonane bez błędów, ale pozostawi bazę danych w takim samym stanie, w jakim była poprzednio (nie zawsze jest to możliwe lub pożądane, ale jest to dobra zasada przez większość czasu) .

Nie wiem, jak podłączysz to do serwera CI. Być może Twój serwer CI musi mieć znaną migawkę kompilacji, którą przywraca każdej nocy, a następnie wprowadza wszystkie zmiany od tego czasu. To chyba najlepsze, w przeciwnym razie zepsuty skrypt migracji zepsuje nie tylko wersję tej nocy, ale wszystkie kolejne.

MarkR
źródło
1

Sprawdź dbdeploy , są już dostępne narzędzia Java i .net, możesz postępować zgodnie z ich standardami dotyczącymi układów plików SQL i tabeli wersji schematu oraz napisać swoją wersję Pythona.

Dave Marshall
źródło
1

Używamy mysql-diff z linii poleceń : wyświetla różnicę między dwoma schematami bazy danych (z live DB lub skryptu) jako skrypt ALTER. mysql-diff jest wykonywany przy starcie aplikacji, a jeśli schemat ulegnie zmianie, zgłasza się do programisty. Dlatego programiści nie muszą ręcznie pisać ALTER, aktualizacje schematów są półautomatyczne.

stepancheg
źródło
0

Napisałem narzędzie, które ( podłączając się do Open DBDiff ) porównuje schematy bazy danych i zasugeruje skrypty migracji. Jeśli wprowadzisz zmianę, która usunie lub zmodyfikuje dane, wyrzuci to błąd, ale dostarczy sugestię skryptu (np. Gdy brakuje kolumny w nowym schemacie, sprawdzi, czy została zmieniona nazwa kolumny i utworzy xx - wygenerowany script.sql.suggestion zawierający instrukcję zmiany nazwy).

http://code.google.com/p/migrationscriptgenerator/ Tylko SQL Server Obawiam się :( Jest to również dość alfa, ale jest BARDZO niskie tarcie (szczególnie jeśli połączysz to z Tarantino lub http://code.google .com / p / simplescriptrunner / )

Sposób, w jaki go używam, to mieć projekt skryptów SQL w twoim .sln. Masz również lokalną bazę danych db_next, w której dokonujesz zmian (za pomocą Management Studio lub NHibernate Schema Export lub LinqToSql CreateDatabase lub czegoś podobnego). Następnie wykonujesz migrationscriptgenerator z bazami danych _dev i _next, które tworzą pliki. skrypty aktualizacji SQL do migracji.

mcintyre321
źródło
0

W przypadku bazy danych Oracle używamy narzędzi oracle-ddl2svn .

To narzędzie zautomatyzowało następny proces

  1. dla każdego schematu bazy danych pobierz schemat ddls
  2. umieść to pod kontrolą wersji

zmiany między instancjami rozwiązane ręcznie

popalka
źródło