Migracja danych bez zatrzymywania systemu
Utworzono: luty 2026 Zaktualizowano: 1 października 2026
z pracy
Python, Oracle, ETL, ładowanie przyrostowe
Przeniesienie dużej ilości danych do nowego schematu bazy, kiedy system musi cały czas działać.
Dane trzeba przenieść do nowego schematu w bazie Oracle, a schematy mocno się różnią: pola z jednej starej tabeli rozchodzą się po kilku nowych i odwrotnie. System przez cały czas działa, więc nie ma mowy o zatrzymaniu wszystkiego na weekend, a dane zmieniają się w trakcie, to, co przeniosłem rano, wieczorem może być już nieaktualne.
Czemu nie jednym skryptem
Jeden skrypt w oknie serwisowym odpada, bo okna nie ma, a jak padnie w połowie, zaczyna się od nowa. Replikacja zmian gotowymi narzędziami byłaby dobra przy podobnych schematach, a tu dane trzeba po drodze mocno przekształcać. Więc piszemy własne ETL z ładowaniem przyrostowym.
Małe paczki
Skrypty w Pythonie, małe kroki: pobranie paczki rekordów, mapowanie na nowy schemat, zapis, sprawdzenie. Każdy przebieg bierze tylko to, co się zmieniło od poprzedniego, paczki wybieramy po dacie ostatniej zmiany, więc przerwany przebieg po prostu zaczyna od tego samego miejsca.
Najtrudniejsze, czyli dopasowanie
Dane, które już przenieśliśmy, dalej zmieniają się w starym systemie i trzeba je ponownie połączyć z tym, co jest w nowym. Tylko że nowy system nie trzyma identyfikatorów ze starego, więc rekordy dopasowujemy po kilku kolumnach naraz. Napisanie tych dopasowań to najtrudniejsza część całej migracji, a ich testowanie wcale nie łatwiejsze.
Dopasowanie siedzi w warunku MERGE, po kilku kolumnach zamiast po identyfikatorze, więc powtórzony przebieg aktualizuje to, co już jest, zamiast robić duplikaty:
MERGE INTO records_new t USING (SELECT :owner_code AS owner_code, :doc_number AS doc_number, :doc_date AS doc_date, :status AS status, :updated_at AS updated_at FROM dual) s ON (t.owner_code = s.owner_code AND t.doc_number = s.doc_number AND t.doc_date = s.doc_date) WHEN MATCHED THEN UPDATE SET t.status = s.status, t.updated_at = s.updated_at WHEN NOT MATCHED THEN INSERT (owner_code, doc_number, doc_date, status, updated_at) VALUES (s.owner_code, s.doc_number, s.doc_date, s.status, s.updated_at)
Po każdej paczce porównujemy liczby rekordów po obu stronach, a rekordy, których nie da się jednoznacznie dopasować, lądują w osobnej tabeli z opisem problemu, zamiast zatrzymywać całą migrację.