Migration d'une base de donnée (execution trop longue)

Migration d'une base de donnée (execution trop longue) - PHP - Programmation

Marsh Posté le 11-01-2007 à 17:28:18    

Bonsoir,
 
Je travaille actuellement sur une migration de base de données de type "uzine à gaz".
Migration de 74 tables vers une nouvelle base de données.
Le problème réside sur le fait qu'il y a une quantité assez importante à migrer et le script que je suis en train de concevoir ne peut pas supporter toute la charge que je lui est donné.
A titre d'exemple, une table qui contient 1800 enregistrement met environ 70 secondes à s'executer. Et les 73 autres tables contiennent plus ou moins la même quantité d'information.
 
PS : J'utilise 2 fichiers (un fichier php pour appeller chaques fonctions et afficher leur "etat" et un fichier pour les différentes fonctions. Chacune d'elle représente une table à migrer).
 
J'ai essayer par mal de chose :
- un "set_time_limit(300);"
- un "set_time_limit(300);" dans une boucle
...
 
Lors de l'execution de mon script, je suis obliger de limiter le nombre de table à migrer mais même cela, le navigateur ne recoit pas la page html pour dire que la migration s'est bien déroulé ou pas.
Je ne peux pas non plus faire table par table cela va à l'encontre de l'automatisation...
 
Que me conseillez-vous pour optimiser le code ?
Connaissez-vous des scripts qui ressemble plus ou moins à ce que je veux faire ?
Flush() est-il utile pour mon cas ?
Comment faire pour que le serveur m'envoie régulièrement "un état d'avancement" ?
 
Merci pour votre aide si bien sur vous souhaitez m'aider :)


Message édité par cvex le 11-01-2007 à 17:29:59
Reply

Marsh Posté le 11-01-2007 à 17:28:18   

Reply

Marsh Posté le 12-01-2007 à 12:20:14    

Tes tables elles font quel poid? moi je trouve ça trés long 70s pour 1800 enregistrements s'ils ne sont pas énorme.
Pour le fait que le navigateur ne recoit pas la page, c'est normal : un navigateur n'attend une page que pendant un temps déterminé avant de considéré que le serveur est innaccéssible. A ma conaissance aucun navigateur récent n'attendra pendant 5 minutes d'affilé sans rien recevoir.
Le Flush poura servir à envoyer un etat d'avancement au navigateur. Celà dit, le navigateur attendra d'avoir reçu sufisament de texte (dans la limite d'un temps donnée) avant de t'afficher l'avancement.
 
Si ton systéme est vraiment trop trop long, alors tu devras faire table par table, mais tu n'es pas obligé de demander au client de cliquer à chaque fois dans son navigateur pour lancer l'exportation suivante : un navigateur est capable d'appeller une autre page quand le serveur lui dit d'aller voir ailleur. Utilise donc les redirections pour que le navigateur appelle succéssivement les pages servant à exporter toutes les tables.
 
Une autre solution mais qui ne marche que si tu as accés au serveur consiste à lancer des programmes de gestion de base de donnée tels que ceux fournis avec mysql et de leurs faire faire l'exportation comme il faut : ca ira surement plus vite qu'une page php contenant des boucles et tu n'auras plus de probléme de timeout. Sinon, essaye de voir si ton hébergeur ne propose pas ce genre de service : ca sert à rien de se prendre la tête si c'est déjà en place. ;)

Reply

Marsh Posté le 12-01-2007 à 13:49:36    

moi, je trouve ça aussi très long. Pour une table de 11000 enregistrements, ça me prend 10s environ sous mysql 3

Reply

Marsh Posté le 12-01-2007 à 13:56:52    

+1 pour le très (trop) long!!!
tu pourrais montrer de script? Surtout les requetes d'alimentation en fait...

Reply

Marsh Posté le 12-01-2007 à 14:40:17    

Oui c'est bien ce que je pensais (lol).
Heuresement que j'ai pas précisé que c'était en local ! (Pour l'instant, juste le temps du développement).
 
omega2 > "Si ton systéme est vraiment trop trop long, alors tu devras faire table par table, mais tu n'es pas obligé de demander au client de cliquer à chaque fois dans son navigateur pour lancer l'exportation suivante : un navigateur est capable d'appeller une autre page quand le serveur lui dit d'aller voir ailleur. Utilise donc les redirections pour que le navigateur appelle succéssivement les pages servant à exporter toutes les tables."
=> Oui, c'est ce que j'ai finalement fait mais encore ce problème de temps d'execution beaucoup trop long pour les tables qui on plus de 10 000 enregistrements.
 
Voici un exemple de ce que j'utilise pour réaliser l'exportation entre 2 bases MySQL :
 
transfere_db.php

Code :
  1. <?php
  2. include("maj_tables.php" );
  3. if(empty($_POST['etape']))
  4. {
  5. $_POST['etape'] = 1;
  6. //Vidage des tables
  7. vidage_table();
  8. }
  9. $execution_tot = 0;
  10. ?>
  11. <html>
  12. ...
  13. ...
  14. ...
  15. <table align="center">
  16. <?php
  17. switch($_POST['etape'])
  18. {
  19. case 1 :
  20. //Table "absencespersonnel"
  21. $tab_abspersonnel = table_absencespersonnel();
  22. if($tab_abspersonnel[1] == "ok" )
  23. {
  24. echo "<tr><td>".$tab_abspersonnel[0]." -> Temps d'execution : <b>".$tab_abspersonnel[2]."</b> seconde(s).</td>";
  25. echo "<td align='center'>[ <a class='ok'>OK</a> ]</td></tr>";
  26. }
  27. else
  28. {
  29. echo "<tr><td>Une erreur est survenue sur la ".$tab_abspersonnel[0]."</td>";
  30. echo "<td rowspan=2 align='center'>[ <a class='echec'>Echec</a> ]</td></tr>";
  31. echo "<tr><td class='contour_erreur'><i><u>Détail de l'erreur :</u></i><p>".$tab_abspersonnel[3]."</p></td>";
  32. echo "<td></td></tr></table>";
  33. exit();
  34. }
  35. echo "<form action='' method='post'>
  36.   <input type='hidden' name='etape' value='2'>
  37.   <input type='hidden' name='tmp_exec' value='".$execution_tot = $execution_tot + $tab_abspersonnel[2]."'>
  38.   <input type='submit' name='etape_suiv' value='Etape 2 >'>
  39.   </form>";
  40.   break;
  41. case 2 :
  42. ...
  43. ...
  44. ...
  45. break;
  46. case 80 :
  47. ...
  48. ...
  49. ...
  50. break;
  51. }
  52. ?>
  53. ...
  54. ...
  55. ...
  56. </html>


 
maj_tables.php

Code :
  1. function table_absencespersonnel()
  2. {
  3. set_time_limit(300);
  4. //Démarrage du chrono
  5. $temps = microtime();
  6. $temps = explode(' ', $temps);
  7. $debut = $temps[1] + $temps[0];
  8. //Déclaration des variables de connexion
  9. $base1 = array();
  10. $base2 = array();
  11. $base1 = connexion_db1();
  12. $base2 = connexion_db2();
  13. //Connexion à l'ancienne base de données
  14. $linkdb1 = mysql_connect($base1['serv'], $base1['nomutil'], $base1['mdp']);
  15. if(!$linkdb1) { $erreur = "<b>Impossible de se connecter à l'ancienne base de donnée.</b>"; }
  16. if(!mysql_select_db($base1['nombase'], $linkdb1)) { $erreur = "<b>Impossible de selectionner l'ancienne base de donnée.</b>"; }
  17. $sql_absence_personnel = mysql_query("SELECT idAbsence, numeroMatricule, dateDebutPersonnel, dateFinPersonnel, nombreJour, nombreHeure, observation, motif, idAction FROM absence_personnel", $linkdb1);
  18. if(!$sql_absence_personnel) { $erreur = "<b>Erreur de lecture de la table \"absence_personnel\".</b><br />".$sql_absence_personnel."<br />".mysql_error(); }
  19. //Connexion à la nouvelle base de données
  20. $linkdb2 = mysql_connect($base2['serv'], $base2['nomutil'], $base2['mdp']);
  21. if(!$linkdb2) { $erreur = "<b>Impossible de se connecter à la nouvelle base de donnée.</b>"; }
  22. if(!mysql_select_db($base2['nombase'], $linkdb2)) { $erreur = "<b>Impossible de selectionner la nouvelle base de donnée.</b>"; }
  23. //Migration des données dans la nouvelle table
  24. while($absence_personnel = mysql_fetch_row($sql_absence_personnel))
  25. {
  26. $sql = "INSERT INTO absencespersonnel (IdAbsencesPersonnel, IdPersonnel, DateDebutAbsence, DateFinAbsence, NombreJoursAbsence, NombreHeuresAbsence, Motif, Observations, IdAction) VALUES ('".$absence_personnel[0]."', '".$absence_personnel[1]."', '".$absence_personnel[2]."', '".$absence_personnel[3]."', '".$absence_personnel[4]."', '".$absence_personnel[5]."', '".addslashes($absence_personnel[7])."', '".addslashes($absence_personnel[6])."', '')";
  27.     if(!mysql_query($sql, $linkdb2)) { $erreur = "<b>Erreur intégration de la donnée dans la table \"absencespersonnel\".</b><br />".$sql."<br />".mysql_error(); }
  28. }
  29. mysql_close($linkdb1);
  30. mysql_close($linkdb2);
  31. //Fin du chrono
  32. $temps = microtime();
  33. $temps = explode(' ', $temps);
  34. $fin = $temps[1] + $temps[0];
  35. $temps_exec = round(($fin - $debut),2);
  36. //Valeur à retourner
  37. if(empty($erreur))
  38. {
  39. return(array("Table \"<b>absencespersonnel\"</b>", "ok", $temps_exec));
  40. }
  41. else
  42. {
  43. return(array("Table \"<b>absencespersonnel\"</b>", "echec", $temps_exec, $erreur));
  44. }
  45. }
  46. function...
  47. ...
  48. ...
  49. ...


 
PS : Pour un soucis de clareté, je n'est pas inclus les autres fonctions et j'ai présenté la migration d'une seule table. ^^


Message édité par cvex le 12-01-2007 à 14:42:23

---------------
http://forum.hardware.fr/hardwaref [...] 1293-1.htm
Reply

Marsh Posté le 12-01-2007 à 14:45:55    

Plus fondamentalement : est-ce bien raisonable de faire ce genre de migration avec PHP server-side? [:pingouino]
 
C'est pour un hébergement mutualisé?

Reply

Marsh Posté le 12-01-2007 à 15:00:53    

non Intranet.
Je me suis posé la même question, mais on m'a imposé le langage :s
Je suis actuellement en train de voir pour migrer les plus grosses bases de données à l'aide de C#.
 
Donc d'après vous, il n'est pas possible de réaliser la migration avec PHP pour ce cas la ?

Reply

Marsh Posté le 12-01-2007 à 15:02:30    

D'accord,

Code :
  1. while($absence_personnel = mysql_fetch_row($sql_absence_personnel)){
  2.     $sql = "INSERT INTO absencespersonnel (IdAbsencesPersonnel, IdPersonnel, DateDebutAbsence, DateFinAbsence, NombreJoursAbsence, NombreHeuresAbsence, Motif, Observations, IdAction) VALUES ('".$absence_personnel[0]."', '".$absence_personnel[1]."', '".$absence_personnel[2]."', '".$absence_personnel[3]."', '".$absence_personnel[4]."', '".$absence_personnel[5]."', '".addslashes($absence_personnel[7])."', '".addslashes($absence_personnel[6])."', '')";
  3.     if(!mysql_query($sql, $linkdb2)) { $erreur = "<b>Erreur intégration de la donnée dans la table \"absencespersonnel\".</b><br />".$sql."<br />".mysql_error(); }
  4. }


pas besoin de chercher plus loin, une requette par ligne à insérer avec mysql, c'est normal que ca rame. Fait plustôt des requettes d'insertion correspondant à un lot de 10, 20 ou 50 lignes à insérer. Tu véras que ca ira beaucoup plus vite.
 
EDIT : PS : Fait attention aux hébergeurs, de nombreux hébergeurs bloquent les accés distant à la base SQL ce qui empécherait ton script de bosser comme il faut.

Message cité 1 fois
Message édité par omega2 le 12-01-2007 à 15:05:40
Reply

Marsh Posté le 12-01-2007 à 15:10:22    

omega2 a écrit :


pas besoin de chercher plus loin, une requette par ligne à insérer avec mysql, c'est normal que ca rame. Fait plustôt des requettes d'insertion correspondant à un lot de 10, 20 ou 50 lignes à insérer. Tu véras que ca ira beaucoup plus vite.


+1 sur la cause mais pas d'accord sur la solution qui n'est pas beaucoup mieux.

 

Mysql te propose un truc assez pratique c'est SELECT ... INTO FILE et LOAD DATA FILE. Du coup pour dumper ta table tu fais:

Code :
  1. $query      = "SELECT * INTO OUTFILE '".$tblName.".bck' FROM ".$tblName;
  2. $result = mysql_query($query);


et pour la upper

Code :
  1. $query      = "LOAD DATA INFILE '".$tblName.".bck' INTO TABLE ".$tblName";
  2. $result = mysql_query($query);
  



Message édité par anapajari le 12-01-2007 à 15:11:36
Reply

Marsh Posté le 12-01-2007 à 15:25:56    

anapajari > Pour ça, il faut que le serveur mysql soit sur la même machine que le serveur php ce qui est rarement le cas en dehors des hébergements dédiés.
Certe en local ca marcherait mais une fois en production, il y a peu de chance qu'il puisse utiliser cette méthode.
 
Cette méthode à en plus un défaut méconu avec mysql : mysql remplis la table sans mettre à jour les index puis exécute les fonctions de réparation de table pour que les index soient à jour. Pour de trés grosses tables, (plusieurs disaines ou centaines de millions de lignes par exemple) ca peut se réveller plus lent que des insertions classiques. Je sais, on est trés loin de ce volume de données dans le cas présent.

Reply

Marsh Posté le 12-01-2007 à 15:25:56   

Reply

Marsh Posté le 12-01-2007 à 15:36:09    

omega2 a écrit :

anapajari > Pour ça, il faut que le serveur mysql soit sur la même machine que le serveur php ce qui est rarement le cas en dehors des hébergements dédiés.
Certe en local ca marcherait mais une fois en production, il y a peu de chance qu'il puisse utiliser cette méthode.


Euh non  [:roane]  [:roane]  [:roane]
tu fais

  • un script de dump + sauvegarde des fichiers sur le serveur 1
  • un script d'upload fichier + load data sur le serveur 2
 
omega2 a écrit :

Cette méthode à en plus un défaut méconu avec mysql : mysql remplis la table sans mettre à jour les index puis exécute les fonctions de réparation de table pour que les index soient à jour. Pour de trés grosses tables, (plusieurs disaines ou centaines de millions de lignes par exemple) ca peut se réveller plus lent que des insertions classiques. Je sais, on est trés loin de ce volume de données dans le cas présent.


not quite true.
La "non mise à jour" des index n'est effectué que si l'opération a lieu dans une table vide, d'ailleurs dans la doc te donne la procédure pour toujours émuler ce comportement à toi de choisir ou non quel cas te convient le mieux.
Mais juste pour quoter cette page:

Citation :

When loading a table from a text file, use LOAD DATA INFILE. This is usually 20 times faster than using INSERT statements. See Section 13.2.5, %u201CLOAD DATA INFILE Syntax%u201D.


Et pour moi, dans ce cas précis on est exactement dans le "usually" ( comme tu l'indique également dans ton message;) )


Message édité par anapajari le 12-01-2007 à 15:36:52
Reply

Marsh Posté le 12-01-2007 à 15:44:08    

Merci beaucoup pour vos réponses, elle me sont précieuses !
Je vais tester toutes les solutions afin de trouver celle qui correspond au besoin de mon cas. :)

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed