Migration d’une base DB2 8.2 sur AIX vers 9.5 sur Linux
|
Note Technique |
Date |
Classement
|
Page 1/6 |
||
|
Rédacteur : Fabrice SCEMAMA |
|||||
|
Historique |
|||||
|
Version |
Date |
Modifications |
|||
|
1.0 |
25/03/2009 |
Version Initiale |
|||
|
1.1 |
31/03/2009 |
Modification du script d'import / load des IXF, rapport listant les rejets |
|||
|
1.2 |
02/04/2009 |
Prise en charge des tables en rejet |
|||
|
1.3 |
17/04/2009 |
Ajout de scripts perl destinés à contrôler l'intégrité des tables réimportées. Précision du codeset sur create database, et intérêt. |
|||
|
1.4 |
21/04/2009 |
Amélioration des scripts de contrôle et de correction des imports , paramètres de tuning nécessaires à un import massif. |
|||
I. Objectifs et périmètre du document
Ce document couvre la migration d'une base de données située sur une instance DB2 8.2 fonctionnant sur AIX, vers une instance DB2 9.5 fonctionnant sur Linux. On se base sur la configuration obtenue à l'issue de l'application des étapes mentionnées dans les documents « Installation et Administration de Linux Fedora 10 (sur vm) » et « Installation de DB2 9.5 Workgroup Edition sur Linux Fedora 10 ».
La base de données de test utilisée est DBSIEB, située sur le serveur SRVDB1. C'est une base Siebel.
Ne sont pas couverts les aspects liés au tuning et au backup.
II. Export des données sur AIX (DB2 8.2)
|
telnet SRVDB1 login => sadmin ; password => db2 |
|
|
Le répertoire /backup/formation a été chmoddé 0777 par le root pour les besoins de cette migration pilote. Prévoir de l'espace ! |
|
|
Création d'un répertoire à la date du jour. |
|
nohup db2move DBSIEB EXPORT -u siebel -p xxx >db2move.log 2>&1 & |
Export des tables. |
|
nohup db2look -d DBSIEB -e -a -l -x -xd –i siebel –w xxx -o dbsieb.ddl >db2look.log 2>&1 & |
Export du schéma de la base (param. i pour le user, et w pour le mot de passe). |
Note : sur le serveur DEV, où DB2 est installé sur Windows 2000, la commande db2look existait, mais pas la commande db2move. Le script suivant, qui nécessite un compilateur Perl, a remédié à la situation :
| #!/usr/bin/perl -w use strict; my $DB = "HYPODEV"; system("db2 connect to $DB user $USER using $PASS"); my(@Tables) = (); open(DB, ">$REP_EXPORT/db2move.lst"); $REP_EXPORT\\$Tables[$i].msg select * from $Tables[$i]"); system("db2 connect reset"); |
Enregistrer ce fichier dans C:\temp\export.pl, puis exécuter C:\perl\bin\perl c:\temp\export.pl .
On obtient les mêmes fichiers IXF qu'avec db2move ; un fichier db2move.lst est créé, tel qu'attendu par les scripts d'import indiqués dans la suite de ce document. |
III. Import des données vers Linux (DB2 9.5 Workgroup Edition)
-
Préparation de l'environnement et création de la base
|
LIRE notes de droite avant exécution. useradd siebel –p xxx |
Il s'agit du user DB2 qui possèdera la base DBSIEB. Ce mot de passe devra être renforcé. Si le serveur a été connecté à l'AD à l'aide de Samba, il ne faut pas créer ce user local, mais utiliser celui de l'AD (OU MonDomaine\ADM). Créé le 17/4/9, siebel@mondomaine.fr a pour mot de passe 'xxx990'. |
|
mkdir /home/database |
En attendant de pouvoir monter des répertoires du NAS. |
|
ln -s /home /app |
L'habitude ayant été prise de trouver le répertoire database dans /app … cette habitude pourrait être changée. |
|
for i in 1 2 3 4 5 6; do mkdir /home/database/pgform0$i; done |
Ces répertoires contiendront les tablespaces créés par l'application du fichier dbsieb.ddl créé précédemment. |
|
chown -R db2inst1:db2grp1 /home/database |
Donner au user dba le contrôle des répertoires contenant les tablespaces. |
cat <<eot >>/home/siebel/.bashrc
mkdir /home/MONDOMAINE/siebel cat <<eot >>/home/siebel/.bashrc |
Variables d'environnement pour notre nouvel user. |
| su – db2inst1 cat <<eot |db2 create database DBSIEB using codeset 1252 territory fr COLLATE USING IDENTITY connect to DBSIEB grant dbadm on database to user siebel revoke connect on database from public connect reset quit eot |
Création de la DB DBSIEB, et grant de tous les droits au user siebel sur cette base.
/!\ Bien noter la syntaxe du CREATE DATABASE. En n'indiquant pas la spécificité française, on perd la gestion correcte des accents (qui occupent alors 3 chars au lieu d'un seul, compromettant par la suite l'insertion de nombreuses lignes). L'option COLLATE USING IDENTITY est une spécificité nécessaire à Siebel. |
2. Adaptation du schéma aux spécificités de DB2 9.5
Le fichier dbsieb.ddl obtenu précédemment ne peut être appliqué tel quel.
Modifications indispensables :
-
Requêtes « CREATE BUFFERPOOL » : l'attribut « NOT EXTENDED STORAGE » n'est plus supporté et doit être supprimé (détails : http://publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp?topic=/com.ibm.db2.luw.wn.doc/doc/i0051450.html).
-
Après le paragraphe relatif à la création des BUFFERPOOL, ajouter un db2stop force et un db2start (prise en compte des changements).
-
La requête « CREATE TEMPORARY TABLESPACE TEMPSPACE1 » va échouer, ce tablespace existant déjà. Il ne peut d'ailleurs être supprimé préalablement, étant unique. Pas d'action requise.
-
Requêtes « CREATE FUNCTION » : remplacer DECIMAL(x,y) par FLOAT (détails : https://publib.boulder.ibm.com/infocenter/db2luw/v9r5/index.jsp?topic=/com.ibm.db2.luw.messages.sql.doc/doc/msql00451n.html).
-
Requête « CREATE FUNCTION EXPLAIN_GET_MSGS » : remplacer les mentions de la table EXPLAIN_DIAGNOSTIC par UDBPGS.EXPLAIN_DIAGNOSTIC, et de la table EXPLAIN_DIAGNOSTIC_DATA par UDBPGS.EXPLAIN_DIAGNOSTIC_DATA.
-
Requêtes « CREATE TRIGGER » : la présence de caractères accentués pose problème. Remplacer par exemple la condition « (post.PAY_STAT_CD = 'payé') » par « (post.PAY_STAT_CD like 'pay_') ».
-
Certaines requêtes « GRANT » vont échouer. Cela concerne toutes les vues SYSCATV82.*, qui n'existent plus sous DB2 9.5, et certaines vues du schéma SYSTOOLS (détails : http://download.boulder.ibm.com/ibmdl/pub/ps/products/db2/fixes2/finnish/db2aix5v8/mfp/FP10_U803920/Release.Notes/doc/fr/db2ir/db2ir30.htm). Pas d'action requise, à moins que des besoins applicatifs ne nous obligent à créer des vues similaires à celles qui existaient sous DB2 8.2, ce qui est envisageable.
-
Certaines requêtes « GRANT » vont également échouer : GRANT EXECUTE ON FUNCTION "UDBPGS "."EXPLAIN_GET_MSGS", GRANT EXECUTE ON PROCEDURE ", GRANT EXECUTE ON PROCEDURE "SYSPROC "."LIST_COL_LONG_OPTS" et GRANT EXECUTE ON PROCEDURE "SYSPROC "."LIST_NN_LONG_OPTS". Pas d'action requise.
-
Requêtes « GRANT » sur « SIEBEL.SIEBDBX » : remplacer DECIMAL(x,y) par FLOAT.
-
Après le paragraphe consacré aux GRANT, on pourra ajouter de la compression pour les tables créées. Pour chaque table, on mettra alors une ligne comme celle-là : (liste des tables dans le fichier db2move.lst)
-
ALTER TABLE EIM_ACC_SRC_DTL compress yes ;
-
-
Voir enfin le /!\ du paragraphe Importation des tables, pour les modifications concernant les identities (séquences).
3. Création du schéma
|
su – db2inst1 |
|
|
On a choisi de monter un partage réseau, par manque d'espace local. |
| cd /mnt/smbfs/FScemama mkdir dbsieb.`date +%Y%m%d` cd dbsieb.`date +%Y%m%d` |
Création d'un répertoire à la date du jour. |
|
Récupération des fichiers d'export de SRVDB1, par FTP. |
|
db2 -vtf dbsieb.ddl >/tmp/importDDL.log 2>&1 |
Création du schéma. Vérifier le fichier de log. |
| cat <<eot |db2 connect to dbsieb get db cfg connect reset quit eot |
Permet de connaître les valeurs de tuning par défaut de la base. |
| cat <<eot |db2 update db cfg for DBSIEB using LOGPRIMARY 30 update db cfg for DBSIEB using LOGSECOND 100 update db cfg for DBSIEB using LOGFILSIZ 8192 update db cfg for DBSIEB using LOGBUFSZ 1024 quit eot |
Ajustements destinés à faciliter les insertions massives.
Requiert 1,7 Go dans /home/db2home/. |
|
Par défaut, DB2 alloue un nombre spécifique de pages de 4 Ko pour la mise en mémoire tampon et la mise en mémoire cache des données et des index. Cette valeur peut varier en fonction du système d'exploitation concerné, mais elle est en général égale à 1 000 pages (ou à 4 Mo d'espace de pool de mémoire tampon). Le paramètre bufferpool peut être utilisé pour le stockage de grandes quantités de données et d'index au sein de mémoires physiques, ce qui permet de réduire la quantité nécessaire de pagination de et vers l'espace de stockage. En règle générale, vous pouvez affecter un maximum de 80 pour cent de mémoire disponible à une utilisation au sein de pools de mémoire tampon.
Un bufferpool IBMDEFAULTBP de 128000 permet d'augmenter la taille du pool de mémoire tampon PAR DEFAUT à 512 Mo de mémoire. Nous mettons 32768. |
|
| cat <<eot |db2 connect to dbsieb SELECT * FROM SYSCAT.BUFFERPOOLS ALTER BUFFERPOOL IBMDEFAULTBP DEFERRED SIZE 32768 connect reset eot |
|
| su – root /etc/rc.d/init.d/db2 restart |
Applique les changements opérés précédemment sur la configuration de la DB. |
4. Importation des tables
Démarche : normalement, on aurait dû être en mesure d'importer toutes les tables, telles qu'indiquées dans le fichier « db2move.lst », par la seule commande « db2move DBSIEB IMPORT -io REPLACE_CREATE ».
Toutefois, cette commande échouerait pour toutes les tables contenant des colonnes avec identity (i.e. des séquences). Référence IBM : voir le Data Movement Utilities Guide and Reference.
Une première approche consiste à lancer, pour chaque table, une commande de type « db2 import from tab10.ixf of ixf modified by identityignore insert into eim_account ». Toutefois, cette méthode a pour effet d'ignorer le champ devant s'incrémenter automatiquement ; la DB le fait donc commencer à 1, et l'incrémente elle-même au fil de l'import. Cette méthode n'est pas utilisable : elle rendrait la base de données incohérente.
La seconde approche, telle que préconisée par IBM, consiste finalement, pour chaque table, à :
-
Tenter une commande de type « db2 import from tab10.ixf of ixf create into eim_account »
.
-
En cas d'échec, exécuter la commande « db2 load from tab10.ixf of ixf modified by IDENTITYOVERRIDE insert into eim_account ».
/!\ Ce n'est toutefois pas suffisant. Tel qu'énoncé, nous créerions par exemple une séquence commençant à 1, et chargerions des valeurs situées par exemple entre 10000 et 20000. Mais, toute future insertion verrait la valeur 1 affectée à la colonne identity, puis 2, etc. jusqu'à 10000 ! et nous aboutirions à des doublons. La solution coniste donc, en plus de ce qui précède, à modifier le schéma (dbsieb.ddl), de sorte qu'une query comportant par exemple.. :
| CREATE TABLE "SIEBEL "."EIM_ACC_SRC_DTL" ( "MS_IDENT" DECIMAL(15,0) NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH +1 INCREMENT BY +1 MINVALUE +1 MAXVALUE +999999999999999 NO CYCLE CACHE 20 NO ORDER ) , "ROW_ID" VARCHAR(15) NOT NULL , |
Soit rectifiée avec un START WITH commençant soit à MAX(MS_IDENT) + 1 (qui serait à récupérer au moment de l'import), soit plus simplement à une valeur suffisamment haute => par exemple « +199999999999999 ». On choisit cette dernière option.
Ccncrètement, il faut donc coder un script destiné à parser le fichier « db2move.lst », afin d'y récupérer chaque nom de table exportée, et le nom du fichier IXF correspondant. Puis, pour chaque table, exécuter les commandes précédemment exposées.
A noter que le script suivant peut être amélioré ; cela pourrait être une bonne idée de ne créer les indices qu'après l'import et la mise en production, au moins pour accélérer le temps d'import.
| cat <<"eot" >/tmp/load.pl #!/usr/bin/perl -w use strict; system("db2 connect to dbsieb user siebel using xxx990"); # For these commands, ["siebel "."table"] should be transformed print "————————————\n"; # Delete from the table my $cmd = "db2 delete from $F[1]"; Commit(); # CREATE TABLE "SIEBEL "."EIM_ACC_SRC_DTL" ( # Now, try to load data (schema not included; will fail if identity columns) unless($Ident) # This is a failure: Cleanup($F[1]); foreach(@ff) if($Ident || ($res{read} == $res{rejected} && $res{rejected} > 0 && $res{inserted} == 0)) Cleanup($F[1]); # Track partial rejections print "Date: [".Date()."]\n"; sub Date sub Commit sub Cleanup my $cmd = "db2 load from /dev/null of del terminate into $T"; $cmd = "db2 set integrity for $T immediate checked"; |
Création, toujours en tant que user siebel et depuis le répertoire contenant notre export, du script perl destiné à importer les tables.
Les tables dont l'import a engendré un rejet partiel sont mentionnées dans le fichier /tmp/reject . |
|
chmod 0755 /tmp/load.pl |
Droits d'exécution. |
|
/tmp/load.pl >/tmp/importTables.log 2>&1 |
Import. Vérifier le log. |
Dans notre cas, un fichier /tmp/reject a été créé, contenant les informations suivantes :
| Table: S_BITMAP_INTL Rejected: 472 Inserted: 9428 Table: S_BUSCOMP Rejected: 5 Inserted: 44915 Table: S_CHART_INTL Rejected: 32 Inserted: 3147 Table: S_CS_QUEST Rejected: 1041 Inserted: 2003 Table: S_DEFAULT_ADMIN Rejected: 71 Inserted: 1663 Table: S_DOC_PPSL Rejected: 4 Inserted: 259 Table: S_DOC_PPSL_BU Rejected: 4 Inserted: 41 Table: S_ESCL_GROUP Rejected: 1 Inserted: 26 |
De manière générale, on a pris soin d'écrire un autre script, destiné à comparer d'une part les tables présentes sur la base source et la base destination, et d'autre part, pour chaque table, le nombre de lignes :
| cat <<"eot" >/tmp/check_imp_exp.pl #!/usr/bin/perl -w use strict; use DBI; if(-e "$0.log") LOG("Connection to the Source DB"); LOG("Connection to the Target DB"); my(@Tables_S) = $dbh->tables(); LOG("Checking that all Source Tables are available on the Target DB"); my(@Tables_S_NoMissing) = (); LOG("Comparing each table for the number of rows"); foreach my $TS (@Tables_S_NoMissing) my $sth2 = $dbh2->prepare("SELECT COUNT(1) FROM $TS"); LOG(" ".($cnt != $cnt2 ? "!!!":"")." $TS $cnt $cnt2"); LOG("Disconnecting from the Target DB"); LOG("Disconnecting from the Source DB"); close(LOG); sub IsNum sub IsTime sub To_FR $T =~s/Ã\(c\)/é/g; $T =~s/Ã/à/g; return($T); sub LOG |
Logs : fichier check_imp_exp.pl.log . |
|
chmod 0755 /tmp/check_imp_exp.pl |
Droits d'exécution. |
|
cd /tmp ; ./check_imp_exp.pl & |
Vérifier le log. |
Corrections à appliquer :
-
Le script check_imp_exp.pl va lister des tables système inexistantes sur DB2 9.5, pour lesquelles il n'y a pas lieu d'agir.
-
Certaines tables particulièrement volumineuses apparaîtront éventuellement vides sur la base de destination. Procéder à leur import manuellement depuis l'interface db2. Exemple :
-
db2
-
connect to dbsieb user siebel using xxx990
-
load from /mnt/smbfs/Fscemama/DBSIEB.20090320/tab1057.ixf of ixf insert into S_CONTROL_INTL
-
connect reset
-
terminate
-
-
Certaines tables apparaîtront légèrement incomplètes.
Pour les tables incomplètes, il a été nécessaire d'écrire un script comparant, pour chacune d'entre elles, le contenu source et le contenu destination, et procédant aux insertions nécessaires.
Les erreurs renvoyées par le script ont permis de détecter deux types de problèmes :
-
Les accents. Leur présence dans les requêtes SQL est gérée, mais DB2 stocke un caractère accentué sur plusieurs bytes. Il en résulte que certaines requêtes ont échoué parce que dépassant la taille limite du champ ! => Problème réglé par un paramétrage adéquat de CREATE DATABASE.
-
Des incohérences dans certaines tables de la base source : dans certains cas, il est apparu que des champs de type DECIMAL ou TIMESTAMP ne contenaient pas de valeur. Cela peut se produire lorsque, par exemple, on altère une table pour lui rajouter un champ de ce type, sans lui affecter ensuite de valeur.
Le script suivant doit donc…
-
Remplacer à la volée les champs vides de type DECIMAL par 0.
-
Remplacer à la volée les champs vides de type TIMESTAMP par 1911-11-11 11:11:11.
Moyennant quoi, l'intégralité des rejets est traitée avec succès.
Notes :
-
Cette opération suppose que la base source n'a pas évolué ! sans quoi, des différences seront forcément détectées…
-
Dans notre cas, seule une grosse table n'a contenu aucune ligne en destination, et n'a pu être chargée manuellement par un load, nécessitant une longue phase d'insertions (table S_COLUMN). C'est une anomalie : un champ NOT NULLABLE était systématiquement nulle en source. Une solution aurait pu être de modifier le schéma, mais on a préféré recourir au script sync_reject.pl.
| cat <<"eot" >/tmp/sync_rejects.pl #!/usr/bin/perl -w use strict; use DBI; if(-e "$0.log") my($Reject_File) = @ARGV; my(@Tables) = (); my $dbh = DBI->connect('dbi:DB2:DATABASE=dbsieb;HOSTNAME=SRVDB1;' my $dbh2 = DBI->connect("dbi:DB2:dbsieb", "siebel", "xxx990", foreach my $Table (@Tables) # Getting field names and types # Querying the source table, my $intQ = 0; # Clean up problematic fields. In the source DBSIEB's BUSCOMP # Find out if the same row exists in the target table. # Not found in the target table, then insert. if(($intI / 1000) == int($intI / 1000)) $dbh2->commit(); $dbh2->commit(); LOG("Date: ".Date()); sub Date sub IsNum sub IsTime sub To_FR $T =~s/Ã\(c\)/é/g; $T =~s/Ã/à/g; return($T); sub LOG __END__ * SQL_CHAR 1 |
|
|
chmod 0755 /tmp/sync_rejects.pl |
Droits d'exécution. |
|
cd /tmp; ./sync_rejects.pl & |
Vérifier le log, dans sync_rejects.pl.log. |
5. Divers
-
En cas d'erreur SQL1076C, ou après un redémarrage du système à chaud, exécuter la commande ipclean en tant que db2inst1.
-
Après des load interrompus, on peut arriver à la situation suivante :
-
"select count(*) from TABLE"
-
1
-
———–
-
SQL0668N Operation not allowed for reason code "3" on table "TABLE".
-
SQLSTATE=57016
Solution :
-
load from /dev/null of del terminate into TABLE
-
-
Après un crash du système, la db peut être inaccessible (SQL1042C). Solution :
-
db2
-
restart db DBSIEB write resume
-
(Syntaxe complète :
-
RESTART [DATABASE | DB]
-
[DatabaseName]
-
USER [UserName] < USING
-
[Password] > >
-
< DROP PENDING TABLESPACES
-
( [TS_Name] , … ) >
-
< WRITE RESUME >
-
-
In: AIX, DB2 · Tagged with: AIX, DB2, Linux, migration, perl, script, tutoriel, virtualiser
