Restauration de base mysql par portion (pour eviter les depassement de timelimit)

Contenu du snippet

Lorsque l'on effectue une sauvegarde d'une base mysql, le fichier peut etre tres gros. Et quand on le restaure on peut être confronté à la limitation de temps (timeout, timelimit) d'éxecution du script PHP.

Pour contrer cela, ce script de restauration s'arrete automatiquement avant un temps prédéfini, puis relance un nouveau script qui reprend la restauration à l'endroit où elle s'est arrétée dans le script précédent et ainsi de suite jusqu'à ce que le fichier .sql soit lu et restauré en entier.

Les paramètres sont classiques : serveur, nom de base, utilisateur, mot de passe
puis
fichier=nom du fichier sql à restaurer
duree=timeout à ne pas dépasser (5 secondes par défaut)

tout ceci se paramètre en fin de script

Source / Exemple :


<html>
<head><title>Restauration de base MySql</title></head>
<body>
<?
function td() //temp initial
{
  global $TPSDEB,$TPSCOUR;
  
  
  list ($usec,$sec)=explode(" ",microtime());
  $TPSDEB=$sec;
  $TPSCOUR=0;

}

function tf() //temp final ou intermediaire
{
  global $TPSDEB,$TPSCOUR;
  list ($usec,$sec)=explode(" ",microtime());
  $TPSFIN=$sec;
  if (round($TPSFIN-$TPSDEB,1)>=$TPSCOUR+1) //une seconde de plus
  {
    $TPSCOUR=round($TPSFIN-$TPSDEB,1);
    echo $TPSCOUR.".";
    flush();
  }

}

function restoreMySqlDump($dumpFile , $database , $mysqlUser , $mysqlPassword , $hostMySql, $duree)
{
 // $dumpFile, fichier source
 // $database, nom de la base de données cible
 // $mysqlUser, login pouyr la connexion au serveur MySql
 // $mysqlPassword, mot de passe
 // $histMySql, nom de la machine serveur MySQl
 // $duree=timeout pour changement de page (-1 = aucun)

global $TPSCOUR,$offset,$cpt;

 $mySqlHandle = mysql_connect($hostMySql, $mysqlUser, $mysqlPassword);
 if (!$mySqlHandle)
 {
   echo "Connexion impossible à $hostMySql pour $mysqlUser";
   return false;
 }

 if(!file_exists($dumpFile))
 {
   echo "$dumpFile non trouvé<br>";
   return FALSE;
 }
 $fileHandle = fopen($dumpFile, "rb");

 if(!$fileHandle)
 {
  echo "Ouverture de $dumpFile non trouvé<br>";
  return FALSE;
 }

 if ($offset!=0)
 {
   if (fseek($fileHandle,$offset,SEEK_SET)!=0) //erreur
   {
      echo "Impossible de trouver l'octet ".number_format($offset,0,""," ")."<br>";
      return FALSE;
   }
   else
     echo "Reprise à l'octet ".number_format($offset,0,""," ")."<br>";
  flush();
 }
 else
 {
   $query = "DROP DATABASE IF EXISTS " . $database;
   $result = mysql_query($query);
   $query = "CREATE DATABASE " . $database;
   $result = mysql_query($query);
  }
  
  $query = "USE " . $database;
  $result = mysql_query($query);

 $formattedQuery = "";

 while(!feof($fileHandle))
 {
    tf();
    if ($duree>0 and $TPSCOUR>=$duree) //on atteint la fin du temps imparti
      return TRUE;
    //echo $TPSCOUR."<br>";
    $buffer=fgets($fileHandle);
    if (substr($buffer,strlen($buffer),1)==0)
      $buffer=substr($buffer,0,strlen($buffer)-1);
    
    //echo $buffer."<br>";
    
    if(substr($buffer, 0, 1) != "#")
    {
      $formattedQuery .= $buffer;
       // echo $formattedQuery."<hr>";
      if ($formattedQuery)
        if (mysql_query($formattedQuery)) //réussie sinon continue à conca&téner
        {
          $offset=ftell($fileHandle);
          //echo $offset;
          $formattedQuery = "";
          $cpt++;
          //echo $cpt;
        }
  }
 }

 if (mysql_error())
   echo "<hr>ERREUR à partir de [$formattedQuery]<br>".mysql_error()."<hr>";

 fclose($fileHandle);
 mysql_close($mySqlHandle);
 $offset=0;
 return TRUE;
}

td(); //initialise le temps
if (!isset($offset)) $offset=0; //début de fichier
if (!isset($duree)) $duree=5; //timeout de 5 secondes par défaut, -1 pour utiliser sans timeout

if (!isset($fichier)) $fichier="sauvebase.sql"; //si le nom du fichier n'est pas en paramètre le mettre ici

echo "Restauration de $fichier.<br>Traitement en cours... ";

if ($duree>0) echo "timeout de $duree s.<br>";
flush();
//echo "$offset";
//exit;
//nom du fichier, nom de la base, nom d'utilisateur, mot de passe, serveur, duree
if (restoreMySqlDump($fichier , "base" , "root" , "pass", "localhost",$duree))
{
  if ($offset!=0)
  {
    echo "<br>Nombre de requêtes traitées à ce stade : $cpt<br>";
    echo "<br>mais il faut continuer à l'octet ".number_format($offset,0,""," ");
    echo "<br>Redirection automatique sinon cliquez <a href=\"restore.php?duree=$duree&offset=$offset&cpt=$cpt\">ici</a>";
    echo "<script>window.location=\"restore.php?duree=$duree&offset=$offset&cpt=$cpt\";</script>";
  }
  else
   echo "<br>Terminé. Nombre de requêtes totales traitées : $cpt<br>";

}

?>
</body>
</html>

Conclusion :


Le script de restauration est lui meme repris dans les scripts de ce site. Je n'ai fait qu'ajouter la gestion du timeout.

J'ai essayé de mettre un affichage de la progression, mais on ne la voit pas vraiment. Si le javascript est activé, la boucle se fait toute seule il n'y a rien à faire qu'attendre. Sinon en fin de chaque script il y a possibilité de cliquer pour activer le suivant.

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.