开发教程

thinphp5 备份数据库、优化数据库、修复数据库

  • 1199 Views

在实际项目中,可能用到使用php来备份数据库的功能。当然我们还是建议通过其他工具来备份,单从实现功能角度出发,可以这样干。


1、备份数据库

备份数据库,是指通过读取表信息,来生成创建表、视图和插入数据的sql语句。下面是完整的备份数据库的类库文件,修复了网上流传的备份数据库因为null值引发的在还原时出错的问题。

<?php

namespace zhuopro;
use think\db;

//数据导出模型
class Database{
   /**
    * 文件指针
    * @var resource
    */
   private $fp;

   /**
    * 备份文件信息 part - 卷号,name - 文件名
    * @var array
    */
   private $file;

   /**
    * 当前打开文件大小
    * @var integer
    */
   private $size = 0;

   /**
    * 备份配置
    * @var integer
    */
   private $config;

   /**
    * 数据库备份构造方法
    * @param array  $file   备份或还原的文件信息
    * @param array  $config 备份配置信息
    * @param string $type   执行类型,export - 备份数据, import - 还原数据
    */
   public function __construct($file, $config, $type = 'export'){
       $this->file   = $file;
       $this->config = $config;
   }

   /**
    * 打开一个卷,用于写入数据
    * @param  integer $size 写入数据的大小
    */
   private function open($size){
       if($this->fp){
           $this->size += $size;
           if($this->size > $this->config['part']){
               $this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
               $this->fp = null;
               $this->file['part']++;
               session('backup_file', $this->file);
               $this->create();
           }
       } else {
           $backuppath = $this->config['path'];
           $filename   = "{$backuppath}{$this->file['name']}-{$this->file['part']}.sql";
           if($this->config['compress']){
               $filename = "{$filename}.gz";
               $this->fp = @gzopen($filename, "a{$this->config['level']}");
           } else {
               $this->fp = @fopen($filename, 'a');
           }
           $this->size = filesize($filename) + $size;
       }
   }

   /**
    * 写入初始数据
    * @return boolean true - 写入成功,false - 写入失败
    */
   public function create(){
       $sql  = "-- -----------------------------\n";
       $sql .= "-- Think MySQL Data Transfer \n";
       $sql .= "-- \n";
       $sql .= "-- Host     : " . config('DB_HOST') . "\n";
       $sql .= "-- Port     : " . config('DB_PORT') . "\n";
       $sql .= "-- Database : " . config('DB_NAME') . "\n";
       $sql .= "-- \n";
       $sql .= "-- Part : #{$this->file['part']}\n";
       $sql .= "-- Date : " . date("Y-m-d H:i:s") . "\n";
       $sql .= "-- -----------------------------\n\n";
       $sql .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
       return $this->write($sql);
   }

   /**
    * 写入SQL语句
    * @param  string $sql 要写入的SQL语句
    * @return boolean     true - 写入成功,false - 写入失败!
    */
   private function write($sql){
       $size = strlen($sql);
       
       //由于压缩原因,无法计算出压缩后的长度,这里假设压缩率为50%
       //一般情况压缩率都会高于50%
       $size = $this->config['compress'] ? $size / 2 : $size;
       
       $this->open($size);
       return $this->config['compress'] ? @gzwrite($this->fp, $sql) : @fwrite($this->fp, $sql);
   }

   /**
    * 备份表结构
    * @param  string  $table 表名
    * @param  integer $start 起始行数
    * @return boolean        false - 备份失败
    */
   public function backup($table, $start){
       //创建DB对象
       //备份表结构
       if(0 == $start){
           $result = Db::query("SHOW CREATE TABLE `{$table}`");
           $sql  = "\n";
           $sql .= "-- -----------------------------\n";
           $sql .= "-- Table structure for `{$table}`\n";
           $sql .= "-- -----------------------------\n";
           $sql .= "DROP TABLE IF EXISTS `{$table}`;\n";
           $sql .= trim($result[0]['Create Table']) . ";\n\n";
           if(false === $this->write($sql)){
               return false;
           }
       }

       //数据总数
       $result = Db::query("SELECT COUNT(*) AS count FROM `{$table}`");
       $count  = $result['0']['count'];
           
       //备份表数据
       if($count){
           //写入数据注释
           if(0 == $start){
               $sql  = "-- -----------------------------\n";
               $sql .= "-- Records of `{$table}`\n";
               $sql .= "-- -----------------------------\n";
               $this->write($sql);
           }

           //备份数据记录
           $result = Db::query("SELECT * FROM `{$table}` LIMIT {$start}, 1000",[]);
           foreach ($result as $row) {
               $sql = "INSERT INTO `{$table}` VALUES (";
               foreach ($row as $item){
                   if($item===null){
                       $sql=$sql.'null,';
                   }
                   else{
                       $item=addslashes($item);
                       $sql=$sql."'".str_replace(array("\r","\n"),array('\r','\n'),$item)."',";
                   }
               }
               $sql=substr($sql,0,-1);
               $sql=$sql. ");\n";
               if(false === $this->write($sql)){
                   return false;
               }
           }

           //还有更多数据
           if($count > $start + 1000){
               return array($start + 1000, $count);
           }
       }

       //备份下一表
       return 0;
   }

   public function import($start){
       //还原数据

       if($this->config['compress']){
           $gz   = gzopen($this->file[1], 'r');
           $size = 0;
       } else {
           $size = filesize($this->file[1]);
           $gz   = fopen($this->file[1], 'r');
       }
       
       $sql  = '';
       if($start){
           $this->config['compress'] ? gzseek($gz, $start) : fseek($gz, $start);
       }
       
       for($i = 0; $i < 1000; $i++){
           $sql .= $this->config['compress'] ? gzgets($gz) : fgets($gz);
           if(preg_match('/.*;$/', trim($sql))){
               if(false !== Db::query($sql)){
                   $start += strlen($sql);
               } else {
                   return false;
               }
               $sql = '';
           } elseif ($this->config['compress'] ? gzeof($gz) : feof($gz)) {
               return 0;
           }
       }

       return array($start, $size);
   }

   /**
    * 析构方法,用于关闭文件资源
    */
   public function __destruct(){
       $this->config['compress'] ? @gzclose($this->fp) : @fclose($this->fp);
   }
}


2、修复数据表

修复数据表是mysql数据库提供的功能,可以通过执行sql语句来完成。

$list = Db::query("REPAIR TABLE `{$ids}`");


3、优化数据表

同样的,优化数据表也是mysql数据库提供的功能,可以通过执行sql语句来完成。

$list = Db::query("OPTIMIZE TABLE `{$ids}`");