IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JDBC Java Discussion :

Création outil d'export base à base


Sujet :

JDBC Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2007
    Messages
    500
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2007
    Messages : 500
    Par défaut Création outil d'export base à base
    Hello,
    je dois créer un outil afin d'exporter les données d'environ 400 tables sur un autre schéma, ces données sont filtrées et je ne prend disons que celles récente d'une semaine.

    Pour l'instant le prog fonctionne comme suit:

    - j'ai une classe Lanceur, qui me lance ma premiere méthode qui va parcourir une à une les tables, pour chacune, on fait un select, on chope le resultset ( qui peut être énorme 200k lignes) , on le parcoure, et on stocke l'insert généré poru chaque ligne dans un fichier .sql

    - une fois une table traitée, on lance une 2e méthode qui va lire le fichier .sql généré ligne par ligne pour le jouer sur le schéma CIBLE.


    => pour éviter d'avoir des resultset trop volumineux à parcourir, je scinde mes requetes select pour n'en remonter que 1000 à la fois (rownum).
    => j'ai beau avoir défini un setFetchSize(1000) , ça prend énormément de temps à parcourir mon resultset.

    Actuellement, je pense partir sur du thread:
    - un thread qui parcoure mes tables une à une et qui crée le .sql dans un dossier "a_traiter"
    - un thread qui vérifie la présence de fichier .sql dans le dossier "a_traiter", qui le joue sur le schéma cible, puis qui move ce fichier sql dans "traite".


    Qu'en pensez-vous? Y-a t-il une autre solution?

  2. #2
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 394
    Par défaut
    Bonjour,

    Je pense que tu peux monter à plus 1000 résultats pour ton ResultSet (ça dépend évidemment de ce qu'il te retourne), mais tu dois pouvoir monter à 20000 assez facilement. Tu ferais moins d'aller retour avec la base de données. (Attention si tu crées des thread chacun ne devra pas remonter 20000 résultats évidemment)

    Est-ce que ça ne serait pas l'écriture dans le fichier qui est longue plutôt que le parcours du ResultSet ? Tu utilises bien un BufferedWriter pour écrire ton fichier ? Cette classe permet de ne pas faire un accès disque à chaque "writer.write("monSTring")", elle met données en mémoire jusqu'à atteindre la taille max de son buffer puis après elle écrit d'un coup tout ce qu'elle a mis en mémoire. Tu peux aussi paramétrer la taille de son buffer via le constructeur (buffer de 8ko par défaut c'est pas énorme).

    L'idée de créer des thread pour traiter les fichiers en parallèle de leur création me semblent bonne. Tu peux aussi créer plusieurs fichiers sql en parallèle non ?

    Romain.

  3. #3
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2007
    Messages
    500
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2007
    Messages : 500
    Par défaut
    Hello,
    en fait, pour l'instant, je parcoure donc mes resultset, et je génère le code des insert dans une StringBuilder, une fois mon parcours fini, j'utilise cette méthode :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    	public void ecrireFichier(String nomFichier, String lignes)
    			throws FichierException {
    		PrintWriter out = null;
    		try {
    			out = new PrintWriter(
    					new BufferedWriter(new FileWriter(nomFichier)));
    			out.print(lignes);
     
    		} catch (IOException exc) {
    			throw new FichierException("Impossible d'écrire dans le fichier "
    					+ nomFichier, exc);
    		}
    		out.close();
    	}
    Puis je lance l'import du fichier généré via :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    	public void lancerImport() {
    		ConnexionOracleViaJdbc connexionCible = new ConnexionOracleViaJdbc(
    				prop.getProperty("USER_CIBLE"),
    				prop.getProperty("PASSWORD_CIBLE"),
    				prop.getProperty("URL_ORACLE_CIBLE"));
     
    			File repertoire = new File("a_traiter");
    			FilenameFilter extension = GestionFichiers.avecExtension(".sql");
    			List<File> fichiers = GestionFichiers.listeRepertoire(repertoire,
    					extension);
     
    			try {
     
    				for (File f : fichiers) {
    					connexionCible.ouvrir();
     
    					Statement pstmt = connexionCible.getConnexion()
    							.createStatement();
    					pstmt.setFetchSize(fetchSize);
    					logger.log(Level.INFO,
    							"[ DEBUT ] Import fichier : " + f.toString() + " "
    									+ DateUtils.getFormattedDate());
     
    					int cp=0;
    					for (String s : GestionFichiers.getInstance().lireFichier(
    							f.toString())) {
    						if (!s.startsWith("/*")) {
    							String insert = s.replaceAll(";$", "");
    							try{
    							pstmt.executeUpdate(insert);
    							}catch (Exception e) {
    								logger.log(Level.SEVERE, "Ligne "+cp+"  "+e.toString()+"\n"+insert);
    							}
    						}
    						cp++;
    					}
    					logger.log(Level.INFO,
    							"[ FIN ] Import fichier : " + f.toString() + " "
    									+ DateUtils.getFormattedDate());
    					try {
    						GestionFichiers.move(f.toString(),
    								"en_cours/" + f.getName());
    					} catch (IOException e) {
    						e.printStackTrace();
    					}
    					pstmt.close();
    					connexionCible.fermer();
    				}
    			} catch (FichierException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
     
    	}
    	}

    Le soucis là, c'est que si j'augmente à disons 20000 le nb de lignes récupérées via le rownum, ça risque de mettre plus longtemps pour parcourir mon resultset non?
    D'autant que, pour chaque ligne du resultset, je parcoure toutes les colonnes une à une , et que certaines tables contiennent à la louche 50-60 colonnes

  4. #4
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 394
    Par défaut
    Est-tu sûr que c'est le parcours de ton resultSet qui est long ? ça me parait bizarre, un ResultSet c'est une liste il n'y a pas vraiment de raison que le parcours d'une liste de 1000 lignes soit long.

    Si tu dis que tu as dans les 200 000 entrées dans ta tables, et que tu y vas 1000 par 1000 ça fait quand même 2000 requêtes, et si l'exécution de ta requête est coûteuse je pense que la lenteur du traitement viendrai plutôt de là. Mais ce n'est qu'une supposition.

    Moi ce que je ferais à ta place ça serait de sélectionner dans les 20 000 résultats. Ensuite je créerai par exemple 5 threads à qui je filerais 4000 lignes chacun. Chacun des threads créerait des fichiers de 4000 lignes. En attendant qu'ils créent les fichiers tu re-sélectionne 20 000 résultat, puis dès qu'un thread est dispo tu lui refiles 4000 lignes à traiter etc...
    Puis de l'autre coté tu fais aussi 5 threads qui vérifient dès qu'un fichier est dispo, et qui le traite le cas échéant.

    La façon du tu écris tes fichiers n'est pas très optimale en terme de consomation mémoire. Tu crées un gros StringBuffer (donc tu montes toutes les données en mémoire), puis d'un coup tu écris le StringBuffer dans un fichier (ce qui libère ta mémoire). L'idée avec un BufferedWriter c'est plutôt d’envoyer les données à ce buffer et qu'il écrive (libère donc la mémoire) dès qu'il a atteint la taille max de son buffer. :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File("tot")));
    while(/* lecture du ResultSet ici*/){
         // le buffer n'écrira réellement que lorsqu'il aura atteint sa taille max
         bufferedWriter.write(/* création du INSERT ici*/);
    }
    bufferedWriter.close();
    Ainsi tu minimises à la fois la consommation mémoire, et l'accès disque.

    Optique complètement différente as-tu penser à te tourner vers un logiciel type ETL ? Je n'ai aucune connaissance dans le domaine mais c'est exactement le but de ces logiciels de transférer des données d'une base à une autre.

    Romain.

  5. #5
    Membre éclairé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2007
    Messages
    500
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Avril 2007
    Messages : 500
    Par défaut
    Pour le parcours du RS, ca ressemble à ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    				donnees.setFetchSize(fetchSize);
    				donnees.setFetchDirection(ResultSet.FETCH_FORWARD);
    				while (donnees.next()) {
    					sb.append(INSERT_INTO);
    					sb.append(table.getNom());
    					sb.append(VALUES);
    					ligneInsertion += INSERT_INTO + table.getNom() + VALUES;
    					int j = 0;
    					// Mise en forme selon type de colonne
    					for (int i = 1; i < table.getNomsVariables().size(); i++) {
    						if (i > 1) {
    							sb.append(SEPARATEUR_PARAM);
    							ligneInsertion += SEPARATEUR_PARAM;
    						}
    						sb.append(table.getTypes().get(i - 1)
    								.mettreEnForme(donnees.getString(i)));
    						ligneInsertion += table.getTypes().get(i - 1)
    								.mettreEnForme(donnees.getString(i));
     
    						j++;
    					}
    					if (containsBlob)
    						blob = donnees.getBlob(blobNomColonne);
     
    					sb.append(SEPARATEUR_PARAM);
    					sb.append(table.getTypes().get(j)
    							.mettreEnForme(donnees.getString(j + 1)));
    					sb.append(FIN_REQUETE);
    					ligneInsertion += SEPARATEUR_PARAM
    							+ table.getTypes().get(j)
    									.mettreEnForme(donnees.getString(j + 1))
    							+ ")";
    					/*
    					 * CREATION DU FICHIER DU BLOB
    					 */
    					if (containsBlob && blob != null) {
     
    						// insertion du tuple en base
    						Lanceur.tableLogger.log(Level.INFO,
    								"\t\t[ DEBUT ]  Insertion+update ligne blob sur table CIBLE | "
    										+ DateUtils.getFormattedDate());
    						importer(ligneInsertion, maTable, true, true);
     
    						ligneInsertion = "";
     
    						// puis insertion blob via un update de la derniere
    						// ligne
    						PreparedStatement pstmt = connexionCible
    								.createStatement("update "
    										+ maTable.getNom()
    										+ " set "
    										+ blobNomColonne
    										+ "= ? where rownum = (select max(rownum) from "
    										+ maTable.getNom() + ")");
    						pstmt.setBlob(1, blob);
    						pstmt.executeUpdate();
    						pstmt.close();
    						Lanceur.tableLogger.log(Level.INFO,
    								"\t\t[ FIN ]  Insertion+update ligne blob sur table CIBLE | "
    										+ DateUtils.getFormattedDate());
    					}
    					nbLignes++;
    				}

    Pas de traitement particulier, mis à part que pour chaque ligne, je boucle aussi sur une liste qui contient les noms des colonnes.

    Je vais deja modifier la système de la StringBuffer voir si ça change les temps de traitement .


    EDIT : pour infos, pour un select qui contient au total disons 80000 lignes, pour chaque parcours de resultset de 1000 lignes, au départ ça prend 10s, et vers la fin ça monte à 1mn30, y'a donc un truc qui doit pas être top.


    Merci en tout cas, je te tiens au courant..

  6. #6
    Membre chevronné
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 394
    Par défaut
    Pourquoi tu as fais tout en double entre ton buffer et ta ligneInsertion :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    sb.append(INSERT_INTO);
    sb.append(table.getNom());
    sb.append(VALUES);
    ligneInsertion += INSERT_INTO + table.getNom() + VALUES;
    D'autre part je ne sais pas si tu utilises un StringBuffer ou un StringBuilder, mais si ton buffer n'est pas partagé entre plusieurs thread tu devrais utiliser un StringBuilder qui n'est pas threadsafe (le StringBuffer est threadsafe mais plus lent).

    De plus il faut vraiment éviter le += dans une boucle comme ça, ça te plombe les perfs.

    Le fait que ça prend plus de temps vers la fin est peut-être que tu ne libère pas bien la mémoire. Si tu remontes 80 000 lignes, tu crées un buffer qui contient les 80 000 insert, ou tu crées un fichier tous les 1 000 INSERT ? Car à mon avis ton problème est qu'à la fin ta mémoire est saturée car tu as un buffer qui est trop gros. D'où l'intérêt d'utiliser un BufferedWriter pour écrire régulièrement dans un fichier.

    D'autre part est-ce que tu ne pourrais pas optimiser ce bout de code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    PreparedStatement pstmt = connexionCible
    								.createStatement("update "
    										+ maTable.getNom()
    										+ " set "
    										+ blobNomColonne
    										+ "= ? where rownum = (select max(rownum) from "
    										+ maTable.getNom() + ")");
    C'est dommage de recréer un PreparedStatement à chaque fois. J'imagine que tu dois recréer plusieurs fois le même statement ? Tu ne pourrais pas faire une Map qui associerait un PreparedStatement à un couple de valeur (maTable.getNom(), blobNomColonne, maTable.getNom()) ? Puis à chaque fois récupérer le Statement te ta Map au lieu de le recréer.

    Bon courage !

    Romain.

Discussions similaires

  1. [exporter] Exporter la base vers lecteur réseau..
    Par terziann dans le forum Administration
    Réponses: 3
    Dernier message: 15/11/2005, 11h02
  2. aide pour exporter une base de donnée
    Par matt55 dans le forum PostgreSQL
    Réponses: 8
    Dernier message: 06/04/2004, 14h28
  3. [msde]Exportation de base avec msde..vers Hébergeur.
    Par didoboy dans le forum MS SQL Server
    Réponses: 5
    Dernier message: 30/03/2004, 17h11
  4. Exportation de base avec ASP sous OUTLOOK
    Par M1000 dans le forum ASP
    Réponses: 6
    Dernier message: 04/03/2004, 09h52
  5. [phpPgAdmin] Exporter une base entière ?
    Par ovh dans le forum PostgreSQL
    Réponses: 2
    Dernier message: 01/01/2004, 18h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo