• MySQL没有备份情况下误删除表恢复


    一、下载安装

    1.1 下载地址

    git clone https://github.com/twindb/undrop-for-innodb.git

    1.2 安装

    安装依赖包

    yum install -y make gcc flex bison

    编译安装

    cd undrop-for-innodb
    make

    增加用于恢复表结构的工具sys_parse

    gcc `$basedir/bin/mysql_config --cflags` `$basedir/bin/mysql_config --libs` -o sys_parser sys_parser.c

    $basedir 是 MySQL的安装路径

    二、开始测试

    2.1 测试数据

    undrop-for-innodb中带了一个 sakila 库,不过我在测试的时候,用的是官方的,address 表有一个字段类型是 geometry。恢复的时候报错,有兴趣的朋友可以试一下

    下载地址 https://dev.mysql.com/doc/index-other.html

    2.2 删除表

    set foreign_key_checks = 0;
    checksum table customer;
    +-----------------+-----------+
    | Table           | Checksum  |
    +-----------------+-----------+
    | sakila.customer | 399782750 |
    +-----------------+-----------+
    
    drop table customer;

    checksum table 用来做恢复后的校验

    2.3 数据恢复

    2.3.1 表结构恢复

    使用工具 stream_parser 解析文件内容。

    ./stream_parser -f /data/mysql/mysql_3306/data/ibdata1

    执行完毕后会在当前目录下生成文件夹 pages-ibdata1 , 目录下按照每个页为一个文件,分为索引页和数据较大的 BLOB 页。系统表的话,是存在索引页中的。使用另外一个重要的工具 c_parser 来解析页的内容。

    ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql > dumps/default/SYS_TABLES 2> dumps/default/SYS_TABLES.sql
    ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql > dumps/default/SYS_INDEXES 2> dumps/default/SYS_INDEXES.sql 
    ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000002.page -t dictionary/SYS_COLUMNS.sql > dumps/default/SYS_COLUMNS 2> dumps/default/SYS_COLUMNS.sql
    ./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000004.page -t dictionary/SYS_FIELDS.sql > dumps/default/SYS_FIELDS 2> dumps/default/SYS_FIELDS.sql

    参数解析:

    • 4 表示文件格式是 REDUNDANT,系统表的格式默认值。另外可以取值 5 表示 COMPACT 格式,6 表示 MySQL 5.6 格式。
    • D 表示只恢复被删除的记录。
    • f 后面跟着文件。
    • t 后面跟着 CREATE TABLE 语句,需要根据表的格式来解析文件。

    得到的结果 ‘SYS_TABLES’ 字段后面的就是系统表 SYS_TABLE 中对应存的记录。

     

    创建恢复数据库 recover,用来存放恢复的系统表

    create database recover

    导入系统表

    mysql recover < dictionary/SYS_TABLES.sql 
    mysql recover < dictionary/SYS_INDEXES.sql
    mysql recover < dictionary/SYS_FIELDS.sql 
    mysql recover < dictionary/SYS_COLUMNS.sql
    
    mysql recover < dumps/default/SYS_TABLES.sql 
    mysql recover < dumps/default/SYS_INDEXES.sql
    mysql recover < dumps/default/SYS_FIELDS.sql 
    mysql recover < dumps/default/SYS_COLUMNS.sql

    dictionary目录下是建表SQL,dumps/default/是刚才解析 page 得到的sql

    解析表结构

    ./sys_parser -h'127.0.0.1' -uroot -p'abc_1234' -d recover sakila/customer
    CREATE TABLE `customer`(
        `customer_id` SMALLINT UNSIGNED NOT NULL,
        `store_id` TINYINT UNSIGNED NOT NULL,
        `first_name` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL,
        `last_name` VARCHAR(45) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL,
        `email` VARCHAR(50) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci',
        `address_id` SMALLINT UNSIGNED NOT NULL,
        `active` TINYINT NOT NULL,
        `create_date` DATETIME NOT NULL,
        `last_update` TIMESTAMP,
        PRIMARY KEY (`customer_id`)
    ) ENGINE=InnoDB;

    对比发现,恢复出来的 CREATE TABLE 语句相比原来创建的语句信息量有点缺少,因为 innodb 系统表里面存的数据相比 frm 文件是不足的,比如 AUTO_INCREMENT, DECIMAL 类型的精度信息都会缺失,也不会恢复二级索引,外建等。

    2.3.2 表数据恢复

    找到表 customer 的 table_id

    grep customer dumps/default/SYS_TABLES
    00000018501F    3C00000188063B    SYS_TABLES    "sakila/customer"    480    9    33    0    80    ""    475
    00000018501F    3C00000188063B    SYS_TABLES    "sakila/customer"    480    9    33    0    80    ""    475

    再根据 table_id 找到 index_id

    grep 480 dumps/default/SYS_INDEXES
    00000018501F    3C0000018802CB    SYS_INDEXES    480    848    "PRIMARY"    1    3    475    4294967295
    00000018501F    3C00000188033D    SYS_INDEXES    480    849    "idx\_fk\_store\_id"    1    0    475    4294967295
    00000018501F    3C0000018803AF    SYS_INDEXES    480    850    "idx\_fk\_address\_id"    1    0    475    4294967295
    00000018501F    3C000001880421    SYS_INDEXES    480    851    "idx\_last\_name"    1    0    475    4294967295
    00000018501F    3C0000018802CB    SYS_INDEXES    480    848    "PRIMARY"    1    3    475    4294967295
    00000018501F    3C00000188033D    SYS_INDEXES    480    849    "idx\_fk\_store\_id"    1    0    475    4294967295
    00000018501F    3C0000018803AF    SYS_INDEXES    480    850    "idx\_fk\_address\_id"    1    0    475    4294967295
    00000018501F    3C000001880421    SYS_INDEXES    480    851    "idx\_last\_name"    1    0    475    4294967295

    grep 480 是对应 SYS_TABLE 的 TALE ID,848对应的 INDEX_ID

    MySQL5.6之后,默认 innodb_file_per_table = on 这种情况下每个表是保存在各自的 ibd 文件中的,当 drop table 之后 ,ibd 文件会被删除,此时最好能够设置磁盘整体只读避免有其它进程重写文件块stream_parser 这个工具不但可以读文件,还可以读磁盘,会根据 innodb 数据格式把数据页读出来。

    找到被删除的 ibd 文件的挂载磁盘

    df -h
    Filesystem               Size  Used Avail Use% Mounted on
    /dev/mapper/centos-root   46G   13G   33G  28% /
    devtmpfs                 1.9G     0  1.9G   0% /dev
    tmpfs                    1.9G     0  1.9G   0% /dev/shm
    tmpfs                    1.9G   89M  1.8G   5% /run
    tmpfs                    1.9G     0  1.9G   0% /sys/fs/cgroup
    /dev/vda1               1014M  142M  873M  14% /boot
    tmpfs                    379M     0  379M   0% /run/user/0

    我的实验环境是 /dev/mapper/centos-root

    ./stream_parser -f /dev/mapper/centos-root -t 50G

    磁盘大小执行 stream_parser,-t 表示磁盘的大小。执行的时候需要注意磁盘空间。跑完之后,在 undrop-for-innodb目录下会有一个 pages-centos-root 目录,其他环境不知道叫什么。类似 pages-ibdata1目录,下面依然是 FIL_PAGE_INDEX 跟 FIL_PAGE_TYPE_BLOB。我们要找的页在 FIL_PAGE_INDEX 目录下。

    pwd
    /root/undrop-for-innodb/pages-centos-root/FIL_PAGE_INDEX
    ll 0000000000000848.page 
    -rw-r--r--. 1 root root 81920 Oct 22 01:25 0000000000000848.page

    接下来解析 0000000000000848.page

    ./c_parser -6f pages-centos-root/FIL_PAGE_INDEX/0000000000000848.page -t customer.sql > dumps/default/customer 2> dumps/default/customer.sql

    参数解析:

    • 6 表示 MySQL 5.6 格式,4 表示文件格式是 REDUNDANT,系统表的格式默认值。另外可以取值 5 表示 COMPACT 格式。
    • D 表示只恢复被删除的记录。
    • f 后面跟着文件。
    • t 后面跟着 CREATE TABLE 语句,需要根据表的格式来解析文件。customer.sql 是我们解析表结构的文件

    有兴趣的朋友可以看一下 dumps/default/customer.sql,是 load data 语法SQL

    2.3.3 导入恢复的数据

    mysql sakila < customer.sql
    
    mysql sakila < dumps/default/customer.sql

    没有报错就是导入成功了,接下来就是校验数据了

    checksum table customer;
    +-----------------+-----------+
    | Table           | Checksum  |
    +-----------------+-----------+
    | sakila.customer | 399782750 |
    +-----------------+-----------+

    跟删除表前的校验值是一样的。恢复成功。

  • 相关阅读:
    软件研发:公司内部技术考试——算法编程题程序框架
    软件研发:公司内部技术考试——答题方法
    JConsole & JVisualVM远程监视Websphere服务器JVM的配置方法
    java.sql.SQLException: ORA-01578: ORACLE 数据块损坏问题解决办法
    ORA-00001: unique constraint (...) violated并不一定是数据冲突
    Linux部署Web应用程序超链接下载中文名称文件404问题解决办法
    Windows平台使用RMAN命令自动删除Oracle过期归档日志的方法
    Oracle调整内存超出限制出现ORA-27100: shared memory realm already exists问题解决办法
    linux下查看最消耗CPU、内存的进程
    RMAN正确地删除Archivelog以及设置有备库的归档删除策略
  • 原文地址:https://www.cnblogs.com/ziroro/p/9829669.html
Copyright © 2020-2023  润新知