[mysql] copie de tables d'un serveur distant.

ViPHP
ViPHP | 1380 Messages

03 sept. 2005, 10:21

Question récurrente: comment copier une table se trouvant sur un autre serveur MySQL que le sien.

Solutions possibles
  • mysldump: suppose la possiblité d'avoir accès à une session ssh sur le serveur cible ou de pouvoir faire un exec() sur le serveur (souvent désactivée en mutualisé). Cela suppose également... la connaissance de la syntaxe.
  • phpmyadmin: faire un export sql --> tranférer par ftp --> import en cible
  • MySQL federated table dangereux car le mot de passe apparaît en clair dans la structure de la table (voir documentation) De plus ce n'est possible que depuis la version 5.0.3.
  • recopier à la main #-o
J'ai fait ce petit script qui permet de faire tout cela sans trop de soucis. Cela présuppose que le serveur distant dispose d'une adresse IP et que le port de MySQL (défini dans my.cnf ou my.ini) soit ouvert sur le firewall, sinon, c'est sans espoir.

Ce script permet également de transférer une table d'un serveur A vers un serveur B tout en s'exécutant sur un serveur C. Seuls les serveurs A et B doivent avoir une adresse IP valable et un port MySQL ouvert.

Faire juste attention au paramètre $ecraser. S'il est à true, il peut faire des dégats si on se trompe de nom de cible (voir code).

Dites moi si c'est utile, sinon je le zappe.
// PARAMETRES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

  // paramètres de configuration
  $affichageResultats = true;             // pour afficher le nombre de lignes importées et le timing
  $timeLimit          = 3;                // en sec. Au cas où une connexion distante bloquerait...
  $ecraser            = false;            // true on écrase la table cible si elle existe (ATTENTION !
                                          // ne pas se tromper de nom de table cible sinon... zap!)
  
  // paramètres de connexion source
  $hostSource         = '***.***.***.***';// adresse IP du serveur Source
  $portSource         = '3306';           // port serveur MySql (3306 par défaut)
  $userSource         = 'root';           // utilisateur
  $mdpSource          = '*******' ;       // mot de passe
  $bddSource          = 'bdd_1';          // base de donnée Source
  $tableSource        = 'articles';       // table Source
  
  // paramètres de connexion cible
  $hostCible          = 'localhost';      // adr. IP du serveur Cible (ici localhost pour l'exemple)
  $portCible          = '3306';           // port serveur MySql (3306 par défaut)
  $userCible          = 'root';           // utilisateur
  $mdpCible           = '***********';    // mot de passe
  $bddCible           = 'bdd_2';          // base de donnée Cible
  $tableCible         = 'articles_2';     // table Cible

  // texte d'erreur de connexion
  $errConnexion = "Impossible d'établir une connexion au port <b>%1\$s</b> du host <u>%2\$s</u> <b>%3\$s</b> dans la limite du temps imparti (%4\$s secondes). <br />Vérifiez l'adresse du host et le numéro de port du serveur %2\$s MySQL.<br />S'ils vous semblent corrects, essayez en changeant la valeur de <b>\$timeLimit</b>.";

  // fonction timing (compatibilité arrière pour microtime(true) de PHP 5)
  function microtimeFloat(){
     list($usec, $sec) = explode(" ", microtime());
     return ((float)$usec + (float)$sec);
  }

// CONNEXION - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  // test d'ouverture du port MySQL sur les serveurs
  if (!@fsockopen($hostSource, $portSource, $errno, $errstr, $timeLimit)){
    printf($errConnexion, $portSource, 'source', $hostSource,  $timeLimit);
    exit;
  }
  
  if (!@fsockopen($hostCible, $portCible, $errno, $errstr, $timeLimit)){
    printf($errConnexion, $portCible, 'cible', $hostCible,  $timeLimit);
    exit;
  }

  // connexion aux deux serveurs MySql
  $time_start = microtimeFloat();
  $linkSource = mysql_connect($hostSource.':'.$portSource, $userSource, $mdpSource)
    or die (mysql_error());
  $time['connexion_serveur_source'] = microtimeFloat() - $time_start;
  
  $time_start = microtimeFloat();
  $linkCible = mysql_connect($hostCible.':'.$portCible, $userCible, $mdpCible)
    or die (mysql_error());
  $time['connexion_serveur_cible'] = microtimeFloat() - $time_start;


// SERVEUR SOURCE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  
  // sélection de la bdd source
  $time_start = microtimeFloat();
  mysql_select_db($bddSource, $linkSource)
    or die (mysql_error($linkSource));

  // importation de la structure de la table source
  $qry_import_structure = 'SHOW CREATE TABLE '.$tableSource;
  $result = mysql_query($qry_import_structure, $linkSource)
    or die (mysql_error($linkSource));
  $row = mysql_fetch_row($result);
  $qry_create = str_replace ('CREATE TABLE `'.$tableSource.'`', 'CREATE TABLE `'.$tableCible.'`', $row[1]);

  // importation des lignes de la table source et construction de l'INSERT
  $qry_import_lignes = 'SELECT * FROM '.$tableSource;
  $result = mysql_query($qry_import_lignes, $linkSource)
    or die (mysql_error($linkSource));

  while ($row = mysql_fetch_row($result)){
    $values = '(\''.implode("','", array_map('addslashes', $row)).'\')';
    $qry_insert[] = 'INSERT INTO '.$tableCible.' VALUES '.$values;
  }

  $time['lecture_source'] = microtimeFloat() - $time_start;


// SERVEUR CIBLE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  // sélection de la bdd cible
  $time_start = microtimeFloat();
  mysql_select_db($bddCible, $linkCible)
    or die (mysql_error($linkCible));
  
  // suppression de la table cible si elle existe déjà
  if ($ecraser){
    $qry_drop = 'DROP TABLE IF EXISTS '.$tableCible;
    mysql_query($qry_drop, $linkCible)
      or die (mysql_error($linkCible));
  }
  
  // création de la structure de la table cible
  mysql_query($qry_create, $linkCible)
    or die (mysql_error($linkCible));

  // insertion des lignes dans la table-cible
  foreach ($qry_insert as $v){
    mysql_query($v, $linkCible)
      or die (mysql_error($linkCible));
  }
  
  $time['ecriture_dans_cible'] = microtimeFloat() - $time_start;



// AFFICHAGE RESULTATS ET STAT - - - - - - - - - - - - - - - - - - - - - - - - - - -

  if ($affichageResultats){
    foreach ($time as $k=>$v){
      $totalTime += $v;
      $s_totalTime .= $k.' --> '.$v.'<br />';
    }
    
    echo '<h4>Importation de '.count($qry_insert).' lignes réussie en '.$totalTime.' secondes!</h4>'.$s_totalTime;
  }


// FERMETURE DES SESSIONS SERVEURS - - - - - - - - - - - - - - - - - - - - - - - - -

mysql_close($linkSource);
mysql_close($linkCible);

Edit 6-09-2005 : Test des connexions par sockets. Lorsqu'une connexion ne peut être établie par mysql_pconnect(), le max_execution_time est sans effet tant que le serveur MySL ne renvoie rien. Une erreur de numéro de port pouvait donc bloquer le script malgré un set_time_limit restrictif. L'utilisation de sockets, permet d'introduire un time out sur les flux, ce que ne permet pas mysql_pconnect() avec set_time_limit().
ripat