MySQL数据恢复

原理

MySQL的InnoDB引擎,数据库data目录下的.ibd文件存储数据,frm文件存放表结构

基本步骤

  1. 备份数据库data目录下的所有文件
  2. 创建同名数据库
  3. 创建同名表结构
  4. 导出表空间
  5. 恢复原.ibd文件
  6. 导入表空间

导出结构

使用frm文件恢复数据表DDL
工具:mysqlfrm (不推荐使用) - dbsake(推荐使用)

curl -s get.dbsake.net > dbsake
chmod u+x dbsake
mv dbsake /usr/bin/

通过frm文件恢复DDL如下

dbsake frmdump /path/to/frmfile
#通过-r可以指定目录,就可以批量解析DDL
dbsake frmdump -r /path/to/datapath/

自动化脚本

功能:导出表空间恢复原.ibd文件导入表空间

<?php
global $argv;
if (count($argv) !== 3) {
    echo "缺少参数" . PHP_EOL;
    exit(1);
}

//解析命令行参数
[$start_file, $database, $password] = $argv;
$username = 'root';

const ORIGIN = '/www/backup/recovery/';
$worker = __DIR__ . DIRECTORY_SEPARATOR;
$table = $worker . $database . '.sql';
is_file($table) and unlink($table);

//导出表结构
shell_exec(sprintf('dbsake frmdump -r %s%s >> %s', ORIGIN, $database, $table));
$sql = file_get_contents($table);

//正则替换ROW_FORMAT = COMPACT
const SEP = '{{SEP}}';
$regex = '#ENGINE=InnoDB(.+?);#';
$sql = str_replace('CREATE TABLE', SEP . 'CREATE TABLE', $sql);
$sqlArray = explode(SEP, $sql);
foreach ($sqlArray as &$item) {
    $matches_format = [];
    if (preg_match($regex, $item, $matches_format)) {
        $item = str_replace($matches_format[1], $matches_format[1] . ' ROW_FORMAT = COMPACT', $item);
    }
}
file_put_contents($table, implode('', $sqlArray));

//正则提取表名称
$regex = '#CREATE TABLE `(?P<name>.+?)`#';
preg_match_all($regex, $sql, $matches);

//生成批量操作脚本
if (!empty($matches['name'])) {
    $existInnoDB = false;
    $existMyISAM = false;

    $sh = '#!/bin/bash' . PHP_EOL;
    $discard = '';
    $cp = '';
    $import = '';
    foreach ($matches['name'] as $tableName) {
        //是否存在 InnoDB
        if (is_file(sprintf('%s%s/%s.ibd', ORIGIN, $database, $tableName))) {
            $existInnoDB = true;
            $discard .= sprintf('mysql -D%s -u%s -p%s -e "ALTER TABLE %s DISCARD TABLESPACE;"', $database, $username, $password, $tableName) . PHP_EOL;
            $import .= sprintf('mysql -D%s -u%s -p%s -e "ALTER TABLE %s IMPORT TABLESPACE;"', $database, $username, $password, $tableName) . PHP_EOL;
        }
        //是否存在 MyISAM
        if (is_file(sprintf('%s%s/%s.MYD', ORIGIN, $database, $tableName))) {
            $existMyISAM = true;
        }
    }

    if ($existInnoDB) {
        $cp .= sprintf('cp %s%s/*.ibd /www/server/data/%s/', ORIGIN, $database, $database) . PHP_EOL;
    }
    if ($existMyISAM) {
        $cp .= sprintf('cp %s%s/*.MYD /www/server/data/%s/', ORIGIN, $database, $database) . PHP_EOL;
        $cp .= sprintf('cp %s%s/*.MYI /www/server/data/%s/', ORIGIN, $database, $database) . PHP_EOL;
    }

    $cp .= sprintf('chown mysql:mysql -R /www/server/data/%s/', $database) . PHP_EOL;
    $cp .= sprintf('chmod 760 -R /www/server/data/%s/', $database) . PHP_EOL;

    file_put_contents($worker. $database . '.sh', $sh . $discard . $cp);
    file_put_contents($worker . $database . '_import.sh', $sh . $import);
}

echo 'ok' . PHP_EOL;
最后修改:2023 年 04 月 06 日 02 : 34 PM
如果觉得我的文章对你有用,请随意赞赏

发表评论