Brug af db2look til at efterligne en database?
On februar 14, 2021 by adminJeg er i gang med at migrere nogle databaser fra iso8859-1 til utf-8. En af databaserne indeholder 1000+ tabeller, masser af procedurer, funktioner, udløsere, begrænsninger osv. Jeg vil gerne automatisere processen så meget som muligt, da dette kommer til at ske flere gange, og i flere tilfælde af systemet. Ideelt set vil jeg gerne aflevere arbejdet til mine venner Jenkins og Ansible 😉
Min plan var at generere ddl med db2look, men det kan ikke generere “objekterne” i den rigtige rækkefølge. Jeg prøvede at køre både med og uden flag -ct (mislykkes af forskellige årsager) F.eks .:
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;
kører
db2look -d <db> -e -td @ -ct
genererer den forkerte rækkefølge for indekset og begrænsningen.
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@
Fjernelse af -ct fungerer i dette trivielle eksempel, men mislykkes i den faktiske database på grund af andre afhængigheder.
Jeg forestiller mig at migrere til UTF-8 er en ganske almindelig opgave, så jeg er nysgerrig efter, hvad s folk har gjort. De to mulige løsninger, jeg ser, er at:
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.
Der er helt klart ulemper ved begge disse, overser jeg en eller anden triviel måde at migrere databaserne på?
REDIGERING: En yderligere observation er, at så længe et indeks ikke indeholder nogen ekstra attributter sammenlignet med det implicit oprettede indeks gennem en unik / primær nøglebegrænsning en advarsel QL0605W
er rejst. Hvis der på den anden side er angivet yderligere attributter, hæves en SQL0601N
-fejl. Eksempel:
CREATE UNIQUE INDEX X1 ON T1 (C1, C2) COMPRESS NO INCLUDE NULL KEYS DISALLOW REVERSE SCANS
genererer en advarsel SQL0605W
, hvis et lignende indeks oprettes via primær nøgleerklæring.
På den anden side genererer et indeks som:
CREATE UNIQUE INDEX X2 ON T2 (C1, C2) INCLUDE (C3) CLUSTER COMPRESS NO INCLUDE NULL KEYS ALLOW REVERSE SCANS
en fejl SQL0601N
. Jeg antager, at dette skyldes INCLUDE-klausulen, men måske vil CLUSTER-klausulen også medføre denne opførsel.
Kommentarer
Svar
For få år siden migrerede vi vores DB2 fra 9.7 til 10.5. Vi flyttede også databasen til ny hardware og implementerede datakomprimering. På grund af dette besluttede vi at oprette databasen fra bunden og eksportere og importere dataene.
Vi brugte db2look og db2move til at udføre jobbet. Der var dog ingen måde at få DDL oprettet af db2look i den rigtige rækkefølge. Vi var nødt til at opdele det genererede script i forskellige dele til oprettelse af tabeller, oprettelse af udløsere, oprettelse af indekser osv.
Endelig ender vi med følgende trin:
- eksportere eksisterende data
- opret den nye database
- opret bufferpools og tabelrum
- opret tabeller
- opret primære nøgler
- oprette indekser
- oprette visninger
- indlæse data
- køre reorgs og runstats
- oprette lagrede procedurer, brugerdefinerede funktioner og udløsere
Jeg håber, det hjælper, selv det er ikke det svar, du måske havde forventet.
Kommentarer
- Tak for dit svar. Situationen er lidt mere kompliceret (der er for eksempel tabeller, der afhænger af funktioner og omvendt). Jeg ‘ Jeg opretter sandsynligvis en parser, der sorterer output fra db2look i topologisk rækkefølge ved hjælp af afhængighedstabeller.
Svar
En grov idé om, hvordan man får databaseobjekter i den rigtige rækkefølge. Afhængighedsgrafen er ikke komplet, men synes at opfylde mine behov.
#!/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))
Nu kan x bruges til at vælge ting i den rigtige rækkefølge fra db2look. Jeg brugte en trivial parser, der læser output fra db2look
i lister. Ved at sløjfe over x og vælge definitionen fra den rigtige skovl kan en sorteret output opnås. Selve parseren er bare en flok regelmæssige udtryk og ikke særlig interessant, men da udsagnene er spredt over flere linjer, er en stmt-læser rart at have:
# 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
EDIT: Jeg har en parser ved https://github.com/lelle1234/Db2Utils . Det er på ingen måde komplet, men fungerede til mine behov.
Der er også en indeksrådgiver der, der forsøger at komme med et optimalt sæt indekser til en given forespørgsel og en database.
Svar
Jeg har brugt følgende tilgang til at kopiere produktionsskema til testservere til udviklere. Trick er at ignorere fejl under individuelle kørsler og sammenligne skema i slutningen for at sikre, at alt genskabes korrekt. Jeg ved, det er ikke en ren tilgang. Jeg har gjort dette mod skema med over 1000 tabeller, 500 funktioner med afhængigheder, begrænsninger osv. Du kan automatisere trinnene ved hjælp af shell-script. Det kræver meget mindre indsats i forhold til at skrive parser og teste den.
Trin 1 Brug script til at udtrække skema fra produktionsserveren i en bestemt rækkefølge (db2look for tablespaces først, db2look for tables next osv.) Brug af en bestemt rækkefølge reducerer antallet af iterationer for trin 2 & 3.
Trin 2 Kør output fra trin 1 på testserver
Trin 3 Uddrag skema fra testserver, og sammenlign det med skema fra produktionsserver
Trin 4 Gentag trin 2 og 3, indtil begge skemaer er synkroniseret.
AUTO_REVAL
databasekonfigurationsparameter for atDEFERRED_FORCE
løse dit problem?db2 +c -s ...
), så jeg med sikkerhed kan sige, at alt er genskabt korrekt.