Używasz programu db2look do naśladowania bazy danych?
On 14 lutego, 2021 by adminJestem w trakcie migracji niektórych baz danych z iso8859-1 do utf-8. Jedna z baz zawiera ponad 1000 tabel, mnóstwo procedur, funkcje, wyzwalacze, ograniczenia itp. Chciałbym zautomatyzować ten proces tak bardzo, jak to możliwe, ponieważ będzie się to działo kilka razy i dla kilku wystąpień systemu. Idealnie chciałbym przekazać pracę moim przyjaciołom Jenkinsowi i Ansible 😉
Mój plan polegał na wygenerowaniu ddl za pomocą db2look, ale nie udało się wygenerować „obiektów” w poprawnej kolejności. Próbowałem uruchomić zarówno z flagą -ct, jak i bez niej (niepowodzenie z różnych powodów) . Na przykład:
connect to <db>; create table t1 (x int not null); create unique index t1pk on t1 (x); alter table t1 add constraint t1pk primary key (x); connect reset;
Running
db2look -d <db> -e -td @ -ct
generuje nieprawidłową kolejność indeksu i ograniczenie.
CREATE TABLE "DB2INST1"."T1" ( "X" INTEGER NOT NULL ) IN "USERSPACE1" ORGANIZE BY ROW@ ALTER TABLE "DB2INST1"."T1" ADD CONSTRAINT "T1PK" PRIMARY KEY ("X")@ CREATE UNIQUE INDEX "DB2INST1"."T1PK" ON "DB2INST1"."T1" ("X" ASC) COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS@
Usunięcie -ct działa w tym trywialnym przykładzie, ale kończy się niepowodzeniem w rzeczywistej bazie danych z powodu innych zależności.
Wyobrażam sobie, że migracja do UTF-8 jest dość powszechnym zadaniem, więc ciekawi mnie, co str eople zrobili. Dwa możliwe rozwiązania, które widzę, to:
a) write a parser that inspects the catalog for database objects and sort them topologically using dependency tables. b) write a parser that reads the output from db2look, identify each object and sort them topologically using dependency tables.
Oczywiście oba mają wady, czy przeoczę jakiś trywialny sposób migracji baz danych?
EDYCJA: Dodatkową obserwacją jest to, że tak długo, jak indeks nie zawiera żadnych dodatkowych atrybutów w porównaniu z niejawnie utworzonym indeksem za pomocą ograniczenia unikalnego / klucza podstawowego, QL0605W
ostrzeżenie jest podniesiony. Jeśli z drugiej strony zostaną określone dodatkowe atrybuty, zostanie zgłoszony błąd SQL0601N
. Przykład:
CREATE UNIQUE INDEX X1 ON T1 (C1, C2) COMPRESS NO INCLUDE NULL KEYS DISALLOW REVERSE SCANS
generuje ostrzeżenie SQL0605W
, jeśli podobny indeks zostanie utworzony za pomocą instrukcji klucza podstawowego.
Z drugiej strony indeks taki jak:
CREATE UNIQUE INDEX X2 ON T2 (C1, C2) INCLUDE (C3) CLUSTER COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS
generuje błąd SQL0601N
. Zakładam, że jest to spowodowane klauzulą INCLUDE, ale być może klauzula CLUSTER również spowoduje takie zachowanie.
Komentarze
Odpowiedź
Kilka lat temu przeprowadziliśmy migrację naszej bazy danych DB2 z wersji 9.7 do 10.5. Przenieśliśmy również bazę danych na nowy sprzęt i wdrożyliśmy kompresję danych. Z tego powodu postanowiliśmy stworzyć bazę danych od podstaw oraz wyeksportować i zaimportować dane.
Użyliśmy do tego db2look i db2move. Jednak nie było sposobu, aby uzyskać plik DDL utworzony przez db2look we właściwej kolejności. Musieliśmy podzielić wygenerowany skrypt na różne części do tworzenia tabel, tworzenia wyzwalaczy, tworzenia indeksów itp.
Na koniec wykonujemy następujące kroki:
- wyeksportuj istniejące dane
- utwórz nową bazę danych
- utwórz pule buforów i obszary tabel
- utwórz tabele
- utwórz klucze podstawowe
- utwórz indeksy
- utwórz widoki
- załaduj dane
- uruchom reorganizacje i uruchom statystyki
- utwórz procedury składowane, funkcje zdefiniowane przez użytkownika i wyzwalacze
Mam nadzieję, że to pomoże, nawet jeśli nie jest to odpowiedź, której można się spodziewać.
Komentarze
- Dziękuję za odpowiedź. Sytuacja jest nieco bardziej skomplikowana (są np. Tabele zależne od funkcji i odwrotnie). I ' prawdopodobnie utworzę parser, który sortuje dane wyjściowe z db2look w porządku topologicznym za pomocą tabel zależności.
Odpowiedź
Ogólny pomysł, jak uzyskać obiekty bazy danych we właściwej kolejności. Wykres zależności nie jest kompletny, ale wydaje się, że spełnia moje potrzeby.
#!/usr/bin/python3 import ibm_db import ibm_db_dbi from toposort import toposort, toposort_flatten cfg = ... conn = ibm_db.connect("DATABASE=%s;HOSTNAME=%s;PORT=50000;PROTOCOL=TCPIP;UID=%s; PWD=%s" % cfg,"","") find_edges = """ select * from ( SELECT "CONSTRAINT" as type, CONSTNAME, TABSCHEMA, TABNAME, BTYPE, "N/A", BSCHEMA, BNAME FROM SYSCAT.CONSTDEP WHERE TABSCHEMA NOT LIKE "SYS%" AND BSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "I", "N/A", D.INDSCHEMA, D.INDNAME, D.BTYPE, "N/A", D.BSCHEMA, D.BNAME FROM SYSCAT.INDEXDEP D JOIN SYSCAT.INDEXES I ON D.INDSCHEMA = I.INDSCHEMA AND D.INDNAME = I.INDNAME WHERE I.TABSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "I", "N/A", I.INDSCHEMA, I.INDNAME, "T", "N/A", I.TABSCHEMA, I.TABNAME FROM SYSCAT.INDEXES I WHERE I.TABSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "F", "N/A", R1.ROUTINESCHEMA, R1.ROUTINENAME, D.BTYPE, "N/A" , COALESCE(R2.ROUTINESCHEMA, D.BSCHEMA), COALESCE(R2.ROUTINENAME, D.BNAME) FROM SYSCAT.ROUTINEDEP D JOIN SYSCAT.ROUTINES R1 ON D.ROUTINESCHEMA = R1.ROUTINESCHEMA AND D.SPECIFICNAME = R1.SPECIFICNAME LEFT JOIN SYSCAT.ROUTINES R2 ON D.BSCHEMA = R2.ROUTINESCHEMA AND D.BNAME = R2.SPECIFICNAME AND D.BTYPE = "F" WHERE D.ROUTINESCHEMA NOT LIKE "SYS%" AND D.BSCHEMA NOT LIKE "SYS%" AND D.BTYPE <> "K" UNION ALL SELECT "T", "N/A", TABSCHEMA, TABNAME, BTYPE, "N/A", BSCHEMA, BNAME FROM SYSCAT.TABDEP WHERE TABSCHEMA NOT LIKE "SYS%" AND BSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "X", "N/A", TRIGSCHEMA, TRIGNAME, BTYPE, "N/A", BSCHEMA, BNAME FROM SYSCAT.TRIGDEP WHERE TRIGSCHEMA NOT LIKE "SYS%" AND BSCHEMA NOT LIKE "SYS%" UNION ALL SELECT "T", "N/A", TABSCHEMA, TABNAME, "T", "N/A", REFTABSCHEMA, REFTABNAME FROM SYSCAT.REFERENCES WHERE TABSCHEMA NOT LIKE "SYS%" ORDER BY 3,4 ) """ sedges = ibm_db.prepare(conn, find_edges) edges = {} ibm_db.execute(sedges, ()) lastnode = None tpl = ibm_db.fetch_tuple(sedges) while tpl: n1 = (tpl[0], tpl[1], tpl[2], tpl[3]) n2 = (tpl[4], tpl[5], tpl[6], tpl[7]) if lastnode == n1: edges[n1].add(n2) else: # print("new") edges[n1] = set() edges[n1].add(n2) lastnode = n1 tpl = ibm_db.fetch_tuple(sedges) x = list(toposort_flatten(edges))
Teraz x może służyć do wybierania rzeczy we właściwej kolejności z db2look. Użyłem prostego parsera, który wczytuje dane wyjściowe z db2look
na listy. Przechodząc w pętlę nad x i wybierając definicję z odpowiedniego segmentu, można uzyskać posortowane dane wyjściowe. Sam parser jest tylko zbiorem wyrażeń regularnych i nie jest szczególnie interesujący, ale ponieważ instrukcje są rozłożone w kilku wierszach, dobrze jest mieć czytnik stmt:
# helper for reading stmt by stmt def myreadlines(f, newline): buf = "" while True: while newline in buf: pos = buf.index(newline) yield buf[:pos] buf = buf[pos + len(newline):] chunk = f.read(4096) if not chunk: yield buf break buf += chunk
EDYCJA: Mam parser pod adresem https://github.com/lelle1234/Db2Utils . W żadnym wypadku nie jest kompletny, ale działał na moje potrzeby.
Jest tam również doradca indeksowy, który stara się znaleźć optymalny zestaw indeksów dla danego zapytania i bazy danych.
Odpowiedź
Użyłem następującego podejścia do kopiowania schematu produkcyjnego, aby przetestować serwery dla programistów. Sztuczka polega na zignorowaniu błędów podczas poszczególnych przebiegów i na końcu porównania schematu, aby upewnić się, że wszystko zostało poprawnie odtworzone. Wiem, że to nie jest czyste podejście. Zrobiłem to w odniesieniu do schematu z ponad 1000 tabelami, 500 funkcjami z zależnościami, ograniczeniami itp. Możesz zautomatyzować kroki za pomocą skryptu powłoki. Wymaga o wiele mniej wysiłku w porównaniu z pisaniem parsera i testowaniem go.
Krok 1 Użyj skryptu, aby wyodrębnić schemat z serwera produkcyjnego w określonej kolejności (najpierw db2look dla obszarów tabel, następnie db2look dla tabel itp.). Użycie określonej kolejności zmniejsza liczbę iteracji w kroku 2 & 3.
Krok 2 Uruchom wyniki z kroku 1 na serwerze testowym
Krok 3 Wyodrębnij schemat z serwera testowego i porównaj go ze schematem z serwera produkcyjnego
Krok 4 Powtarzaj kroki 2 i 3, aż oba schematy będą zsynchronizowane.
AUTO_REVAL
abyDEFERRED_FORCE
rozwiązać problem?db2 +c -s ...
), abym mógł śmiało powiedzieć, że wszystko zostało odtworzone poprawnie.