MySQL · 数据恢复 · undrop-for-innodb

2023-05-16

点击有惊喜


简介

undrop-for-innodb 是针对 innodb 的一套数据恢复工具,可以从文件级别恢复诸如:DROP/TRUNCATE table, 删除表中某些记录,innodb 文件被删除,文件系统损坏,磁盘 corruption 等几种情况。本文简单介绍下使用方法和原理浅析。

准备

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

需要联合 MySQL 的安装路径编译工具 sys_parser,

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

需要的工具都已经完备:

420d94d6-79de-49b3-ad6c-c2648307d1dc.png

  • 重要的工具: c_parser && stream_parser && sys_parser
  • 其中 test.sh && recover_dictionary.sh && fetch_data.sh 是测试的脚本,可以看下里面的逻辑理解工具的用法。
  • 三个目录
  • dictionary 里面是模拟 innodb 系统表结构写的 CREATE TABLE 语句,innodb 的系统表对用户不可见,可以在 informatioin_schema 表中找到一些值,但实际上系统表是保存在 ibdata 固定的页上的。
  • sakila 是一些 SQL 语句,用来测试用。
  • include 是从 innodb 拿出来的一些用到的头文件和源文件。 

DROP TABLE

表结构恢复

一般情况下表结构是存储在 frm 文件中,drop table 会删除 frm 文件,还好我们可以从 innodb 系统表里读取一些信息恢复表结构。innodb 系统表有

SYS_COLUMNS | SYS_FIELDS | SYS_INDEXES | SYS_TABLES 

关于系统表结构的具体介绍可以参考 系统表 , 这几个表对于恢复非常重要,下面以一个恢复表结构的例子来说明。

使用目录 sakila/actor.sql 中的例子:

CREATE TABLE `actor` (
 `actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `first_name` varchar(45) NOT NULL,
 `last_name` varchar(45) NOT NULL,
 `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`actor_id`),
 KEY `idx_actor_last_name` (`last_name`)
) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8; insert into actor(first_name, last_name) values('zhang', 'jian'); insert into actor(first_name, last_name) values('zhan', 'jian'); insert into actor(first_name, last_name) values('zha', 'jian'); insert into actor(first_name, last_name) values('zh', 'jian'); insert into actor(first_name, last_name) values('z', 'jian');

mysql> checksum table actor;
+-----------+------------+
| Table | Checksum |
+-----------+------------+
| per.actor | 2184463059 |
+-----------+------------+ 1 row in set (0.00 sec)
DROP TABLE actor

需要从系统表中恢复,而系统表是保存在 $datadir/ibdata1 文件中的,使用工具 stream_parser 解析文件内容。

$./stream_parser -f /home/zj118228/rds_5616/data/ibdata1

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

$./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql | grep 'sakila/actor' 000000005927 24000001C809C6 SYS_TABLES "sakila/actor" 52 4 1 0 80 "" 38 

参数解析:

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

得到的结果 ‘SYS_TABLES’ 字段后面的就是系统表 SYS_TABLE 中对应存的记录。 同样的恢复其它三个系统表:

/* --- SYS_INDEXES 'grep 52' 是对应 SYS_TABLE 的 TALE ID --- */ 
$./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql | grep '52' 000000005927 24000001C807FF SYS_INDEXES 52 57 "PRIMARY" 1 3 38 4294967295 000000005927 24000001C80871 SYS_INDEXES 52 58 "idx\_actor\_last\_name" 1 0 38 4294967295 /* --- SYS_COLUMNS --- */
./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000002.page -t dictionary/SYS_COLUMNS.sql | grep 52 000000005927 24000001C808F2 SYS_COLUMNS 52 0 "actor\_id" 6 1794 2 0 000000005927 24000001C80927 SYS_COLUMNS 52 1 "first\_name" 12 2162959 135 0 000000005927 24000001C8095C SYS_COLUMNS 52 2 "last\_name" 12 2162959 135 0 000000005927 24000001C80991 SYS_COLUMNS 52 3 "last\_update" 3 525575 4 0 /* --- SYS_FIELD 'grep 57\|58' 对应 SYS_INDEXES 的 ID 列 --- */
$./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000004.page -t dictionary/SYS_FIELDS.sql | grep '57\|58' 000000005927 24000001C807CA SYS_FIELDS 57 0 "actor\_id" 000000005927 24000001C8083C SYS_FIELDS 58 0 "last\_name" 

我们恢复表结构的数据都在这四张系统表中了,SYS_COLUMNS 后面几列的表示 MySQL 内部对于数据类型的编号。

接下来是恢复阶段

  1. 使用目录 dictionary 下的四个文件创建四张表(这里数据库名为 recover )。
  2. 把上面恢复出来的数据分别导入到对应的表中(注意相同的行去重)。
  3. 使用工具 sys_parser 恢复 CREATE TABLE 语句。
$./sys_parser -h 127.0.0.1 -u root -P 56160 -d recover sakila/actor
CREATE TABLE `actor`(
 `actor_id` SMALLINT 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,
 `last_update` TIMESTAMP NOT NULL,
 PRIMARY KEY (`actor_id`)
) ENGINE=InnoDB; 

对比发现,恢复出来的 CREATE TABLE 语句相比原来创建的语句信息量有点缺少,因为 innodb 系统表里面存的数据相比 frm 文件是不足的,比如 AUTO_INCREMENT, DECIMAL 类型的精度信息都会缺失,也不会恢复二级索引,外建等。可以看成是表存储结构的恢复。如果有 frm 文件就可以完完整整的恢复了,这篇文章介绍了恢复方法:Get Create Table From frm

表数据恢复

innodb_file_per_table off

这种情况表中的数据是保存在 ibdata 文件中的,虽然表的数据在数据库中被删除了,但是如果没有被重写,数据还在保存在文件中的,执行下列步骤来恢复:

  1. 使用 stream_parser 分析 ibdata 文件,分别得到每个页的文件。
 $./stream_parser -f /home/zj118228/rds_5616/data/ibdata1
  1. 如表结构分析小节中所示,使用 c_parser 分析系统表 SYS_TABLES 和 SYS_INDEXES ,根据表名得到 TABLE ID, 根据 TABLE ID 得到 INDEX ID。(INDEX ID 就是上述例子的第 5 列,值为 57 和 58)
  2. 根据得到的 INDEX ID,到目录 pages-ibdata1 下去找对应的页号,这就是对应的索引表数据所在的数据页。
  3. 使用 c_parser 读取第 3 步得到的页文件,得到数据。
$./c_parser -6f pages-ibdata1/FIL_PAGE_INDEX/0000000000000065.page -t actor.sql
-- Page id: 579, Format: COMPACT, Records list: Valid, Expected records: (5 5)
000000005D95 E5000001960110 actor 201 "zhang" "jian" "2017-11-04 12:30:07" 000000005D96 E6000001970110 actor 202 "zhan" "jian" "2017-11-04 12:30:07" 000000005D98 E80000019A0110 actor 203 "zha" "jian" "2017-11-04 12:30:07" 000000005D99 E90000019B0110 actor 204 "zh" "jian" "2017-11-04 12:30:07" 000000005DA9 F1000002480110 actor 205 "z" "jian" "2017-11-04 12:30:08" 

数据看起来没什么问题,表结构和数据都有了,导进去即可,看一下 checksum 也相同。

mysql> checksum table actor;
+-----------+------------+
| Table | Checksum |
+-----------+------------+
| per.actor | 2184463059 |
+-----------+------------+
1 row in set (0.00 sec)

innodb_file_per_table on

这种情况下表是保存在各自的 ibd 文件中的,当 drop table 之后 ,ibd 文件会被删除,此时最好能够设置磁盘整体只读,避免有其它进程重写文件块。整体的恢复步骤和上一个小节(innodb_file_per_table off) 相同,只是无法从 pages-ibdata1 目录下面找到对应的 page 号。 假设已经完成了前两步,拿到了 INDEX ID。

stream_parser 这个工具不但可以读文件,还可以读磁盘,会根据 innodb 数据格式把数据页读出来。为了恢复 68 号数据页,我们执行下面几个步骤:

  1. 找到被删除的 ibd 文件的挂载磁盘 /dev/sda5:

     $df 
     Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda2 52327276 47003636 2702200 95% /
     tmpfs 99225896 9741300 89484596 10% /dev/shm
     /dev/sda1 258576 55291 190229 23% /boot
     /dev/sda5 1350345636 1142208356 208137280 85% /home
     /dev/sdb1 3278622264 2277365092 1001257172 70% /disk1
    
  2. 根据 INDEX ID , 磁盘大小执行 stream_parser,-t 表示磁盘的大小。

     	$./stream_parser -f /dev/sda5 -s 1G -t 1142G
    
  3. 在目录 pages—sda5 下找到 68 号页,像上一个小节第 4 步一样恢复数据即可。
  4. <划重点> 测试了三次,有两次是恢复不出来的,因为文件很可能被其它进程重写,这取决于文件系统调度还有整体服务器的负载。
  5. 如果挂载的磁盘上还有其它 mysqld 的数据目录,那么很可能一个 page 文件会很大,监测到其它 ibd 文件的数据,同一个页号的综合在一起,这样辨别出我们需要的数据就比较麻烦。

文件页脏写

MySQL 每次从磁盘读取数据的时候都会进行 checksum 校验,如果校验失败,整个进程就会重启或者退出,校验失败很可能是文件页被脏写了。使用恢复工具直接读取文件很可能可以把未被脏写的行或者页读取出来,损失降到最低。

模拟脏写

同样使用目录 sakila/actor.sql 中的例子,innodb_per_file_table = on:

CREATE TABLE `actor` (
 `actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `first_name` varchar(45) NOT NULL,
 `last_name` varchar(45) NOT NULL,
 `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`actor_id`),
 KEY `idx_actor_last_name` (`last_name`)
) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8; insert into actor(first_name, last_name) values('zhang', 'jian'); insert into actor(first_name, last_name) values('zhan', 'jian'); insert into actor(first_name, last_name) values('zha', 'jian'); insert into actor(first_name, last_name) values('zh', 'jian'); insert into actor(first_name, last_name) values('z', 'jian'); 

模拟脏写,打开 actor.ibd 文件, 使用 ‘#’ 覆盖其中一行数据,

00fdb870-b7df-4122-b7d9-1622bc354737.png


点击有惊喜


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL · 数据恢复 · undrop-for-innodb 的相关文章

  • 条件对列表的 In 子句

    有一个表 我需要通过在配对值列表中应用和条件来获取分页记录 下面是解释 假设我有一堂课Billoflading其中有各个领域 表中两个重要字段是 tenant billtype 我有一个包含值的对列表 tenant1 billtype1 t
  • MySQL - 查找接近的匹配项

    MySQL 有没有办法在文本字段中找到紧密匹配的内容 说找到 email protected cdn cgi l email protection当搜索时 email protected cdn cgi l email protection
  • 在 Python 中,如果我有 unix 时间戳,如何将其插入 MySQL 日期时间字段?

    我正在使用 Python MySQLDB 我想将其插入 Mysql 中的 DATETIME 字段 我该如何使用cursor execute 来做到这一点 要将 UNIX 时间戳转换为 Python 日期时间对象 请使用datetime fr
  • Mysql 将 --secure-file-priv 选项设置为 NULL

    我在 Ubuntu 中运行 MySQL 我在运行特定的查询集时收到此错误 MySQL 服务器正在使用 secure file priv 选项运行 因此无法执行此语句 当我这样做的时候SELECT secure file priv 在我的 m
  • 具有“日期之间”的 CakePHP 模型

    我有一个很大的数据集 超过十亿行 数据在数据库中按日期分区 因此 我的查询工具必须在每个查询上指定一个 SQL Between 子句 否则它将必须扫描每个分区 而且 它会在返回之前超时 所以 我的问题是 分区的数据库中的字段是日期 使用 C
  • Monkeyrunner/jython 中未找到 JDBC 驱动程序错误

    我需要在中插入一些东西DB 我在用着JDBC as a connector jython the script mysql数据库和脚本正在运行CentOS 我的代码看起来像这样 from com android monkeyrunner i
  • MySQL 两种日期格式之间的转换

    用户将以这种格式输入日期 2017 年 2 月 17 日 存储在 mysql 数据库中的日期格式如下 2015 02 17 00 00 00 我想做的是 SELECT FROM insurance where DATE FORMAT in
  • 控制数据是否存在于数组中

    我在mysql中有两个不同的表 我正在使用curl从json文件中获取数据 我的第一个表名称是 tblclients 该表存储客户端数据 我的第二个表名称是 tblcustomfieldsvalues 该表使用 tblclients 表的
  • MySQL 连接器 C++ 64 位在 Visual Studio 2012 中从源代码构建

    我正在尝试建立mySQL 连接器 C 从源头在视觉工作室2012为了64 bit建筑学 我知道这取决于一些boost头文件和C 连接器 跑步CMake生成一个项目文件 但该项目文件无法编译 因为有一大堆非常令人困惑的错误 这些错误可能与包含
  • 如何对字段数据进行分组?

    我有 sql 查询来显示数据 SELECT artikel foto naam fotografer id fotografer name fotografer customer first name customer last name
  • 如何关闭整个数据库的区分大小写

    我创建了一个包含许多脚本和许多存储过程的数据库 在这个数据库中 我们没有注意担心区分大小写 因为它对于我的本地开发计算机来说是关闭的 综上所述 我试图弄清楚如何使以下两条语句返回相同的结果 SELECT FROM companies SEL
  • 使用嵌入qt的mysql?

    我正在尝试使用嵌入 QT 的 mysql 我已经有一个与 mysqld 链接的 Qt mysql 插件 该插件可以很好地加载嵌入式数据库 但 QT 没有简单的方法来设置 dataDir 等嵌入式选项 我在这里看到 http doc qt i
  • 连接数据库错误类型:2002:权限被拒绝

    我正在尝试使用以下脚本连接数据库 cxn test php
  • mysqli_num_rows 无法正常工作

    I have an admin panel in my website in which the admin creates new pages he provides the page name and then the spaces o
  • 如何处理多个连接

    我有一个复杂的查询 需要总共 4 个表中的字段 内部联接导致查询花费的时间比应有的时间长得多 我已经运行了一个 EXPLAIN 语句 其可视化结果附在下面 这是我的查询 SELECT pending corrections correcte
  • MySQL分层存储:搜索所有父母/祖父母等。给定子节点 id 的节点?

    我使用分层模型存储类别 如下所示 CATEGORIES id parent id name 1 0 Cars 2 0 Planes 3 1 Hatchbacks 4 1 Convertibles 5 2 Jets 6 3 Peugeot 7
  • 性能 多次插入或多值单次插入

    从性能角度 时间和服务器负载 来看 最好是进行多个插入或单个插入多个值 我在 stackoverflow 上发现每次插入最多可以有 1000 个值集 我说的是两种情况 要插入大约 1000 3000 个值 有时我会在 mySQL 数据库中插
  • 从heroku 提取数据库失败并出现 Encoding::CompatibilityError

    我在执行 db pull 从 heroku 回到本地开发环境时遇到一些问题 我的设置是通过 RVM 在 Mac OS X Snow Leopard 下的本地 Rails 3 Ruby 1 9 2 环境上通过 MacPorts 安装 MySQ
  • MySQL Amazon RDS:超出锁定等待超时

    在 Mysql Amazon RDS 上 当我尝试运行以下 SQL 查询时 UPDATE table1 INNER JOIN table2 USING CommonColumn SET table1 col1 table2 x table1
  • 使用 PHP MySql 进行关键字搜索?

    我的 mysql 表中有标题 varchar 描述 text 关键字 varchar 字段 我保留了关键字字段 因为我认为我只会在这个字段中搜索 但我现在需要在所有三个字段中进行搜索 所以对于关键字 word1 word2 word3 我的

随机推荐

  • Flutter和Native 通信 pigeon

    文章目录 1 pigeon2 定义接口3 定义sh文件 pigeon sh xff08 lib同级目录创建 xff09 4 运行sh文件 pigeon sh 会生成一下文件5 配置6 使用7 IOS xxx plugin h8 XxxPlu
  • flutter调用go

    文章目录 命令引入greeting aar和使用android中使用Flutter2gopluginPlugin kt参考文档 命令 mkdir demo cd demo go mod init demo 编写greeting go go
  • solidity 学习2.批量转账,存入eth。读取数据。

    pragma solidity 0 4 17 import 39 zeppelin solidity contracts token ERC20 StandardToken sol 39 contract BLEOS is Standard
  • 根据图片获取图片中最多的颜色

    根据网络图片获取背景色 xff0c 用Palette 获取出来的颜色总是不对 Palette p 61 Palette from resource generate int defaultColor 61 ContextCompat get
  • flutter-border

    文章目录 Border继承构造方法BorderStyle和BorderSideBorderStyleBorderSide构造方法 BoxShadow构造方法 BoxShape是Code BorderRadius继承CodeRadius xf
  • ubuntu20.04中安装Flatpak,切换数据源

    安装 Flatpak xff1a sudo apt install flatpak 接着 xff0c 使用以下命令添加 Flatpak 数据源 xff1a sudo flatpak remote span class token opera
  • centeros8 图形化界面设置

    基于性能及通用性等因素的考虑 xff0c 阿里云官方提供的公共Linux系统镜像 xff0c 默认不安装图形化桌面组件 通过管理终端连接Linux实例 执行以下命令 xff0c 安装图形桌面的软件包 yum groupinstall 34
  • 定时器周期计算

    对定时器周期公式的总结 xff1a 1 T 61 xff08 arr 43 1 xff09 PSC 43 1 Tck 其中TCK为时钟频率 xff0c PSC为时钟预分频系数 xff0c arr为自动重装载值 f 61 Tck psc 43
  • [已解决 2020年]你的支付授权失败。请核对你的信息并重试,或尝试其他支付方式。请联系你的银行了解更多信息

    博主更多实战教程 xff1a NET WebApi实战教程 微信小程序实战教程 因为苹果政策的调整 xff0c 目前进行开发者计划加入时 xff0c 有两个模式 如果账号本身是在apple developer app中申请的 xff0c 那
  • LPC1768 IIC通信示——PCF8563

    PCF8563与AT24C02一样 xff0c 是典型的IIC通信器件 xff0c 这里就以它为例 xff0c 编写基于LPC1768硬件IIC的通信代码 xff1a 上图是PCF8563各个寄存器地址 xff0c PCF8563的IIC地
  • 程序员笔试题----字符串的操作

    在程序员面试的过程当中 xff0c 很多时候都会问到对字符串的操作 xff0c 其中包括 xff1a 字符串的逆序 xff0c 字符串的最大字串 xff0c 字符串按单词逆序 xff0c 两个字符串的最大公共子串 xff0c 记录字符串中某
  • 如何选择离线数据集成方案 - 全量&增量

    1 前言 我在上一篇中介绍了实时集成与离线集成该怎么选择 xff0c 接着介绍一下离线集成中的增量与全量的选择问题 要设计方案 xff0c 我们先分析一下数据产生的方式 我们把音视频流这种非结构化的数据集成从这里排除出去 xff0c 因为这
  • 使用阿里云PCDN降低内容分发成本

    点击打开链接 阿里云PCDN xff08 P 2P CDN 的 简称 xff09 是基于P2P技术的内容分发 网络 产品 xff0c 相比CDN而言 xff0c PCDN单价较低 xff0c 更适 用 于大流量内容分发 PCDN产品是与传统
  • 机器学习--线性代数基础

    原文地址 数学是计算机技术的基础 xff0c 线性代数是机器学习和深度学习的基础 xff0c 了解数据知识最好的方法我觉得是理解概念 xff0c 数学不只是上学时用来考试的 xff0c 也是工作中必不可少的基础知识 xff0c 实际上有很多
  • Dockerfile小案例(systemctl)

    Dockerfile小案例 xff08 systemctl xff09 文章目录 Dockerfile小案例 xff08 systemctl xff09 Dockerfile制作 xff08 systemctl xff09 镜像 Docke
  • 怎么打造属于自己的天猫精灵

    原文地址 看了天猫精灵的介绍 xff0c 是不是觉得很神奇 xff0c 实际每个程序要都可以打造属于自己的智能家居 可以实现的功能 点歌 最基础的功能了 xff0c 可以将自己喜欢的歌曲下载下来 xff0c 随时点歌定时提醒 提醒自己吃饭
  • 聊一聊数据仓库中的元数据管理系统

    原文地址 一 元数据的定义 按照传统的定义 xff0c 元数据 xff08 Metadata xff09 是关于数据的数据 在数据仓库系统中 xff0c 元数据可以帮助数据仓库管理员和数据仓库的开发人员非常方便地找到他们所关心的数据 xff
  • 300万知乎多标签文本分类任务经验分享(附源码)

    点击有惊喜 七月 xff0c 酷暑难耐 xff0c 认识的几位同学参加知乎看山杯 xff0c 均取得不错的排名 当时天池AI医疗大赛初赛结束 xff0c 官方正在为复赛进行平台调试 xff0c 复赛时间一拖再拖 看着几位同学在比赛中排名都还
  • 二战时图灵机破译的Enigma密码,现在AI仅需13分钟便可破译

    点击有惊喜 第二次世界大战期间 xff0c 布莱切利园是英国破译密码的中心 图灵当时也在那里工作 密码破译者的天才工作挽救了许多平民和士兵的生命 xff0c 据说将战争缩短了两年 Enigma密码机非常复杂 xff0c 它最先进的化身可以配
  • MySQL · 数据恢复 · undrop-for-innodb

    点击有惊喜 简介 undrop for innodb 是针对 innodb 的一套数据恢复工具 xff0c 可以从文件级别恢复诸如 xff1a DROP TRUNCATE table 删除表中某些记录 xff0c innodb 文件被删除