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

  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.

  7. #7
    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
    Re,

    donc je confirme, c'est un StringBuilder.
    La variable ligneInsertion c'est un traitement particulier pour les colonnes blob, en fait, pour les blobs je suis obligé d'inserer les lignes au fil de l'eau pour les update juste après pour insérer le fichier blob.
    Le += ne devrait pas poser soucis car cette variable est vidée à chaque ligne blob.


    Là je trouve choquant le temps mis pour parcourir une seule ligne de mon RS, surtout qu'il n'y a pas de traitement lourd, à part le parcours des colonne p-e.


    Là j'ai un test en cours, j'ai mis un filtre rownum à 10000 et un ResultSet.setFetchSize(10000):
    Pour ma table, le select total arrive à 7000, mon RS contient donc 7000 lignes, et ça met environ 10mn à le parcourir pour générer le fichier .sql via BufferedWriter.


    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
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    				BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File("a_traiter/"+nomFichierComplet)));
    				// creation du script d'insert
    				bufferedWriter.write(DEBUT_COMMENTAIRE);
    				bufferedWriter.write(COMMENTAIRE_INSERTION);
    				bufferedWriter.write(table.getNom());
    				bufferedWriter.write(FIN_COMMENTAIRE);
    				Blob blob = null;
    				String ligneInsertion = "";
     
    				Lanceur.tableLogger.log(
    						Level.INFO,
    						"\t[ DEBUT ] Creation des insert | "
    								+ DateUtils.getFormattedDate());
    				int nbLignes = 0;
    				donnees.setFetchSize(fetchSize);
    				donnees.setFetchDirection(ResultSet.FETCH_FORWARD);
     
    				while (donnees.next()) {
    					bufferedWriter.write(INSERT_INTO);
    					bufferedWriter.write(table.getNom());
    					bufferedWriter.write(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) {
    							bufferedWriter.write(SEPARATEUR_PARAM);
    							ligneInsertion += SEPARATEUR_PARAM;
    						}
    						bufferedWriter.write(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);
     
    					bufferedWriter.write(SEPARATEUR_PARAM);
    					bufferedWriter.write(table.getTypes().get(j)
    							.mettreEnForme(donnees.getString(j + 1)));
    					bufferedWriter.write(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++;
    				}
    				bufferedWriter.close();


    J'ai bien l'impression que sans les thread ça va pas être possible :p

  8. #8
    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
    Quand je te parle de supprimer "+=" ce n'est pas pour la consommation mémoire mais pour des raisons de performances. Tu ne devrais pas utiliser un += car tu crées pleins de String à chaque fois. Passe par un StringBuilder.

    Un String est un objet immutable, ça veut dire que tu ne peux pas le modifier/maj, tu ne peux qu'en recréer un nouveau à chaque fois. Quand tu fais ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ligneInsertion += INSERT_INTO + table.getNom() + VALUES;
    Tu crées d'abord un nouveau String pour le résultat de "INSERT_INTO + table.getNom() + VALUES" (le compilateur va créer un StringBuilder pour faire la concaténation de ces 3 variables), puis un nouveau String pour effectuer la concaténation de ligneInsertion avec le résultat précédent.

    Alors que quand tu passes par un StringBuilder, tu ne crées jamais un nouveau StringBuilder, tu le mets à jour. Tu gagneras du temps (théoriquement ^^) en passant pas un StringBuilder au lieu de "+=". Maintenant soyons clair c'est pas ça qui prends 10min...

    Est-ce que tu peux essayer de mettre des petits compteurs de temps un peu partout pour qu'on voit ce qui prend vraiment du temps. Je te propose d'utiliser la librairie Guava : http://unstuck.grabyourfreedom.net/2...precision.html de Google qui te permet de faire ça assez proprement sans trop polluer ton code.

    Est ce que ça ne serait pas cette requête qui prend de plus en plus de temps au fur à mesure que tu remplis tes tables :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "select max(rownum) from"+ maTable.getNom() + ")
    Combien de lignes contiennent ces tables ? Quand tu utilises un max dans un select même si tu as définit un index sur rownum il ne sera pas utilisé. Si la table est grosse tu pourrais utiliser un index sur rownum (un index descendant : CREATE INDEX nom_index ON nom_table (rownum DESC) ) et écrire la requête de cette façon pour quelle puisse s'appuyer sur un index :
    SELECT rownum FROM "+ maTable.getNom() +" ORDER BY rownum DESC LIMIT 1
    A adapter selon la base car c'est pas du standard SQL il me semble. Ce SELECT est OK sur une base postgresql.


    Romain.

  9. #9
    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
    Ok,
    j'ai effectivement fait une erreur d'algo avec ligneInsertion, elle n'était pas reset comme il fallait, maintenant c'est bon.

    Je viens de monter le filtre ROWNUM = 100.000 , avec un fetchSize de 20.000, les perfs sont deja bien meilleures :

    - select total = 172000 lignes
    => il met 1mn15 à générer tous les fichiers sql
    => mais il met forcement longtemps ensuite à jouer ligne par ligne mes inserts.

    Va donc falloir trouver le juste milieu ou opti la partie d'insertion des données; c'est vrai que ligne par ligne c'est pas top non plus




    Je continuerais à vérifier les opti dans 1 semaine , merci en tout cas .

  10. #10
    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
    Pense qu'en SQL tu peux optimiser les INSERT de la sorte :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    INSERT INTO ma_table VALUES (..., ..., ...) (..., ..., ...) (..., ..., ...) ...
    Au lieu de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    INSERT INTO ma_table VALUES (..., ..., ...)
    INSERT INTO ma_table VALUES (..., ..., ...)
    INSERT INTO ma_table VALUES (..., ..., ...)
    ...
    Bon week-end !

    Romain.

  11. #11
    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
    Hop, de retour.

    Bon du coup je suis en train de mettre en place le multithreading comme suit :

    generation des scripts d'insert :
    -> 5 threads qui gèrent chacun 20000 lignes.

    execution des scripts:
    -> 2/3 threads qui attendent l'arrivée de fichiers sql dans un repertoire précis.


    Le problème là, c'est au niveau de l'import, la lecture des scripts ligne par ligne est plutot lente je trouve; 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
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    	public void lancerImport() {
     
    		File repertoire = new File("a_traiter");
    		FilenameFilter extension = GestionFichiers.avecExtension(".sql");
    		List<File> fichiers = GestionFichiers.listeRepertoire(repertoire,
    				extension);
     
    		try {
     
    			for (File f : fichiers) {
    				FileHandler handler = new FileHandler("logs/import/"+f.getName().replace("script_insertion_", "import_").replace(".sql", "")+".log");
    				handler.setFormatter(new SimpleFormatter());
    				logger.addHandler(handler);
     
    				logger.log(Level.INFO, "MOVE du fichier de a_traiter --> en_cours\t\t"+DateUtils.getFormattedDateToLog());
    				GestionFichiers.move("a_traiter/" + f.getName(), "en_cours/" + f.getName());
     
    				if (f.toString().contains("DTM_IAMBE")) {
    					connexionCible = new ConnexionOracleViaJdbc(
    							"HDD_DTM_IAMBE", "HDD_DTM_IAMBE",
    							Lanceur.prop.getProperty("URL_ORACLE_CIBLE"));
    				} else if (f.toString().contains("DWH")) {
    					connexionCible = new ConnexionOracleViaJdbc("HDD_DWH_PR",
    							"HDD_DWH_PR",
    							Lanceur.prop.getProperty("URL_ORACLE_CIBLE"));
    				} else if (f.toString().contains("ODS")) {
    					connexionCible = new ConnexionOracleViaJdbc("HDD_ODS_PR",
    							"HDD_ODS_PR",
    							Lanceur.prop.getProperty("URL_ORACLE_CIBLE"));
    				} else {
    					connexionCible = new ConnexionOracleViaJdbc(
    							Lanceur.prop.getProperty("USER_CIBLE"),
    							Lanceur.prop.getProperty("PASSWORD_CIBLE"),
    							Lanceur.prop.getProperty("URL_ORACLE_CIBLE"));
    				}
    				connexionCible.ouvrir();
     
     
    				int cp = 0;
    				logger.log(Level.INFO, "DEBUT\tparcours du fichier sql\t\t"+DateUtils.getFormattedDateToLog());
    				System.out.println("debut "+DateUtils.getFormattedDateToLog());
    				for (String s : GestionFichiers.getInstance().lireFichierScanner(
    						"en_cours/" + f.getName())) {
    					if (!s.startsWith("/*")) {
    						String insert = s.replaceAll(";$", "");
    						try {
    							Statement pstmt = connexionCible.getConnexion()
    									.createStatement();
    							pstmt.setFetchSize(10000);
    							pstmt.executeUpdate(insert);
    							pstmt.getConnection().commit();
    							pstmt.close();
    						} catch (Exception e) {
    							logger.log(Level.SEVERE, "Ligne " + cp
    									+ "  " + e.toString() + "\n" + insert);
    							e.printStackTrace();
    						}
    					}
    					cp++;
    				}
    				System.out.println("fin "+DateUtils.getFormattedDateToLog());
     
    				System.out.println("fin "+DateUtils.getFormattedDateToLog());
     
    				logger.log(Level.INFO, "FIN\tparcours du fichier sql\t\t"+DateUtils.getFormattedDateToLog());
    				logger.log(Level.INFO, "MOVE du fichier de en_cours --> traite\t\t"+DateUtils.getFormattedDateToLog());
    				GestionFichiers.move("en_cours/" + f.getName(),
    						"traite/" + f.getName());
    				connexionCible.fermer();
    			}
    		} catch (FichierException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
     
    	}
    Les deux méthodes de lecture de fichiers dispo :
    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
    	public List<String> lireFichier(String nomFichier) throws FichierException {
    		BufferedReader fluxEntree = null;
    		String ligneLue;
    		List<String> lignes = new ArrayList<String>();
    		try {
    			fluxEntree = new BufferedReader(new FileReader(nomFichier));
    			ligneLue = fluxEntree.readLine();
    			while (ligneLue != null) {
    				lignes.add(ligneLue);
    				ligneLue = fluxEntree.readLine();
    			}
    		} catch (IOException exc) {
    			throw new FichierException("Impossible de lire le fichier "
    					+ nomFichier, exc);
    		}
    		try {
    			fluxEntree.close();
    		} catch (IOException e) {
    			throw new FichierException("Impossible de lire le fichier "
    					+ nomFichier, e);
    		}
    		return lignes;
    	}
     
     
    	public List<String> lireFichierScanner(String nomFichier) throws FichierException, FileNotFoundException {
     
    		Scanner scanner=new Scanner(new File(nomFichier));
    		List<String> lignes = new ArrayList<String>();
     
    		// On boucle sur chaque champ detecté
    		while (scanner.hasNextLine()) {
    		    String line = scanner.nextLine();
    		    lignes.add(line);
    		}
     
    		scanner.close();
    		return lignes;
    	}
    Pour l'instant donc, mes méthodes de lecture, parcourent mon fichier sql, et retournent un ArrayList<String>, celui-ci est ensuite parcouru à son tour puis exécuté ligne par ligne sur la base cible.

  12. #12
    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
    Autant pour moi, après test, la lecture ligne par ligne ne met que 1s pour 20000 lignes, c'est donc l’exécution du code qui est ultra lent

    Apparement, lorsqu'on a un grand nombre de lignes à gérer, il faut utiliser addBatch() comme suit :

    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
    				Statement pstmt = connexionCible.getConnexion().createStatement();
    				pstmt.setFetchSize(1000);
    				for (String s : GestionFichiers.getInstance().lireFichierScanner(
    						"en_cours/" + f.getName())) {
    					if (!s.startsWith("/*")) {
    							pstmt.addBatch(s.substring(0, s.length()-1));
    					}
    					cp++;
    			        if (cp % 1000 == 0) {
    			        	pstmt.executeBatch(); // Execute every 1000 items.
    			        	pstmt.getConnection().commit();
    			        	System.out.println("exec "+cp+"\t"+DateUtils.getFormattedDateToLog());
    			        }
    				}
    				System.out.println("execBatch "+DateUtils.getFormattedDateToLog());
    				pstmt.executeBatch();
    				pstmt.close();
    là, ca met 30s pour inserer 1000 lignes.

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