记一次对某某云镜像中的数据提取与本地搭建(Mongodb与Innodb引擎Mysql)

前言

由于工作的需要,负责远程协助某单位进行数据提取与本地搭建,由于此前并没有操作过Mongodb和Mysql的Innodb引擎数据库本地搭建,因此有了此文做个记录。(以下所有操作均是在本机完成,在操作前已经使用了frp+ssh隧道技术,将远程网络环境完全转发至本地。PS:后续打算捣鼓个内网穿透、隧道技术的学习记录,给自己做个总结。)

挂载 raw 镜像

由于镜像是raw格式,因此考虑使用losetup挂载回环设备进行数据库的提取。以下操作环境远程的centos7系统(vm虚拟机,通过共享目录将镜像文件夹挂载到虚拟机中)。

1
2
3
4
5
6
7
8
9
losetup -f # 查询可用的回环设备,模式没使用显示为loop0以此类推loop1、loop2。。。。

losetup /dev/loop0 /mnt/hgfs/10.0.0.1.raw # 块设备挂载

kpartx -a /dev/loop0 # 块设备分区映射

# 接下来可以使用ls /dev/mapper 查看分区入口,一般是loop0p1

mount /dev/mapper/loop0p1 /vmdisk #将loop0p1挂载到根目录下的vmdisk目录中(目录可以自行mkdir创建一下)

卸载设备如下:

1
2
3
4
5
umount /vmdisk

kpartx -d /dev/loop0

losetup -d /dev/loop0

恢复过程

Mongodb

由于原先并没怎么接触过mongodb数据库,因此在恢复的时候踩了许多坑,最终正解如下。

首先在 usr.local/mongodb/data/db 中找到数据库文件 wt格式

1
2
tar zcvf mongodb_db.tar.gz db/    # 打包
tar zxvf mongodb # 解压

在虚拟机中安装对应版本的mongodb。

1
yum install https://repo.mongodb.org/yum/redhat/7/mongodb-org/3.2/x86_64/RPMS/mongodb-org-server-3.2.17-1.el7.x86_64.rpm

在查询相关资料时得知mongodb如果意外关闭的话需要对数据进行恢复,因为我拿到的直接是镜像(并未通过常规手段关闭服务后经行打包)因此拿到数据库文件后需要先对数据进行修复。

首先删除db目下的 mongod.lock、WiredTiger.lock、journal 文件,然后使用 –repari 重建索引,但在恢复过程中还是爆出了一系列的错误,最终逐一排查得以解决恢复数据库并成功拉起mongodb服务。

在做任何删改操作时,都请务必cp一份备份,以防万一。

修改文件句柄数

1
2
3
#数据库接近20G有大量文件,首先遇到的报错就是提示文件句柄满而导致恢复过程中断

ulimit -n 4096

修改大内存页面

1
2
echo never >>  /sys/kernel/mm/transparent_hugepage/enabled
echo never >> /sys/kernel/mm/transparent_hugepage/defrag

恢复

1
2
3
#这里也爆了错 通过增加storageEngine wiredTiger指定引擎
#其中/home/bak/mongodb/data/db 表示你数据库所在位置
mongod --repair --dbpath /home/bak/mongodb/data/db --storageEngine wiredTiger

启动

1
mongod --dbpath /home/bak/mongodb/data/db --storageEngine wiredTiger

后台运行

1
mongod --dbpath=/home/bak/mongodb/data/db --logpath=/home/bak/mongodb/logs/mongodb.log --port=27017 --storageEngine wiredTiger --logappend --fork

关闭

1
mongod --shutdown --dbpath /home/bak/mongodb/data/db/

至此数据库成功恢复并在本地运行。(*^__^*)开心

mongodb

Innodb引擎数据库

当时在找到mysql的data目录时以为直接打包后拖至本地的data目录中即可恢复,却并未注意到表是由.frm和.idb文件组成,因此导致了在查询时找不到表名的错误。

1
2
3
select * from ks_admin;

ERROR 1146 (42S02): Table 'test.ks_admin' doesn't exist

后来查阅资料得知从5.5开始,默认使用Innodb做表存储引擎,且由于事务的存在,导致这些文件如果直接复制的话是不可以还原直接查看数据。
而又由于innodb表存储引擎的数据都是存放在idb文件中,因此尝试通过从idb文件内恢复数据。最后有幸在zcgonvh大佬的博客中找到了解决方法,其大致思路为:

1
2
3
4
1.使用mysqlfrm工具将frm转换为建表语句
2.创建空表
3.释放表空间
4.复制ibd文件,还原表空间

并且大佬还特此编写了恢复工具(再次感谢zcgonvh大佬)

1
2
3
4
5
6
工具的大致原理

1.修改frm头部标志位为_DB_TYPE_HEAP,表示内存表
2.修改frm文件中保存的表引擎名称为MEMORY
3.复制文件到数据目录,执行show create table还原sql
4.替换语句中的MEMORY为原始引擎

而在大佬提供的工具中只需要使用 InnoDBRestore.exe 即可恢复数据

1
2
InnoDBRepair <username> <password> <port> <srcdir> <destDB>
InnoDBRepair.exe root pass 3306 c:\dbcopy my_database

大佬编译好的程序,默认连接到本地mysql,但所幸大佬已经提供了程序源码。如果mysql服务不在本机的话可以修改源码实现。

大佬原文参考链接:http://www.zcgonvh.com/post/mysql_innodb_restore.html

修改后编译好的附件:

1
链接: https://pan.baidu.com/s/198Pq0jC4l7i94CM-P6PiqA 提取码: beb2 

修改后源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
using System;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Security;
using System.Diagnostics;
using System.Security.Permissions;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using MySql.Data.MySqlClient;
class a{
static void Main(string[] args){
Console.WriteLine("MySql InnoDB Restore tool by zcgonvh.\r\n");
if(args.Length!=6){Console.WriteLine("usage: InnoDBRestore <username> <password> <mysql_ip> <port> <srcdir> <destDB>");return;}
string dbname=args[4];
try{
MySqlConnection conn=new MySqlConnection(string.Format("Host={2};UserName={0};Password={1};Port={2};",args[0],args[1],args[3],args[2]));
conn.Open();
MySqlCommand cmd=conn.CreateCommand();
cmd.CommandTimeout=0x200000;
cmd.CommandText="SET GLOBAL innodb_file_format='Barracuda';";//innodb-compressed row_format
cmd.ExecuteNonQuery();
cmd.CommandText="set global innodb_file_per_table='on';";//single innodb
cmd.ExecuteNonQuery();
cmd.CommandText="set names utf8;";
cmd.ExecuteNonQuery();
cmd.CommandText="create database "+dbname;
cmd.ExecuteNonQuery();
cmd.CommandText="select @@datadir";
string datadir=cmd.ExecuteScalar()+"\\"+dbname+"\\";
string filedir=args[3];
conn.ChangeDatabase(dbname);
foreach(FileInfo frm in new DirectoryInfo(filedir).GetFiles("*.frm"))
{
Console.WriteLine("restoring : "+frm);
string tablename=frm.Name.Substring(0,frm.Name.Length-4);
//modify frm set type=memory
try{
byte[] b=File.ReadAllBytes(frm.FullName);
if(b[0]!=0xfe || b[1]!=0x01) //table magic fe 01
{
Console.WriteLine("not a table frm");
continue;
}
if(b[3]!=0x0c) //table type innodb
{
if(b[3]==0x09)
{
Console.WriteLine("this is a MyISAM db, copy it.");
frm.CopyTo(datadir+frm.Name,true);
File.Copy(filedir+tablename+".MYD",datadir+tablename+".MYD");
File.Copy(filedir+tablename+".MYI",datadir+tablename+".MYI");
}
else
{
Console.WriteLine("unknown table type");
}
continue;
}
uint offset=BitConverter.ToUInt16(b,6);//io_size
offset+=BitConverter.ToUInt16(b,0x0e);//tmp_key_length
offset+=BitConverter.ToUInt16(b,0x10);//rec_length
offset+=2;//00 00
uint len=BitConverter.ToUInt16(b,(int)offset);//type string length,in word
offset+=2;
if(Encoding.Default.GetString(b,(int)offset,(int)len)!="InnoDB")
{
Console.WriteLine("not an innodb frm");
continue;
}
b[3]=6;//_DB_TYPE_HEAP
Array.Copy(new byte[]{0x4d,0x45,0x4d,0x4f,0x52,0x59},0,b,offset,6);//MEMORY
FileInfo ibd=new FileInfo(frm.FullName.Substring(0,frm.FullName.Length-3)+"ibd");
if(!ibd.Exists)
{
Console.WriteLine("can not found ibd: "+ibd);
continue;
}
File.WriteAllBytes(datadir+frm.Name,b);//write to frm file
cmd.CommandText="flush tables;";
cmd.ExecuteNonQuery();
cmd.CommandText="SHOW CREATE TABLE `"+tablename+"`";
MySqlDataReader reader=cmd.ExecuteReader();
reader.Read();
string createsql=reader[1].ToString().Replace(" ENGINE=MEMORY"," ENGINE=InnoDB");
reader.Close();
cmd.CommandText="drop table `"+tablename+"`";
cmd.ExecuteNonQuery();
cmd.CommandText=createsql;
cmd.ExecuteNonQuery();
cmd.CommandText="ALTER TABLE `"+tablename+"` DISCARD TABLESPACE;";
cmd.ExecuteNonQuery();
ibd.CopyTo(datadir+ibd.Name,true);
try{
cmd.CommandText="ALTER TABLE `"+tablename+"` IMPORT TABLESPACE;";
cmd.ExecuteNonQuery();
}catch(MySqlException ex)
{
//change row_format
if(new Regex(@"Schema mismatch \(Table has ROW_TYPE_(\w+) row format, .ibd file has ROW_TYPE_(\w+) row format.\)").IsMatch(ex.Message))
{
string type=new Regex(@"Schema mismatch \(Table has ROW_TYPE_(\w+) row format, .ibd file has ROW_TYPE_(\w+) row format.\)").Matches(ex.Message)[0].Groups[2].Value.ToString();
cmd.CommandText="drop table `"+tablename+"`";
cmd.ExecuteNonQuery();
File.Delete(datadir+ibd.Name);
cmd.CommandText=createsql+" row_format="+type;
cmd.ExecuteNonQuery();
cmd.CommandText="ALTER TABLE `"+tablename+"` DISCARD TABLESPACE;";
cmd.ExecuteNonQuery();
ibd.CopyTo(datadir+ibd.Name,true);
cmd.CommandText="ALTER TABLE `"+tablename+"` IMPORT TABLESPACE;";
cmd.ExecuteNonQuery();
}else{Console.WriteLine("unknown error:"+ex);}
}
}catch(Exception ex){Console.WriteLine("error!! :"+ex);}
}
conn.Close();
}catch(Exception ex){Console.WriteLine(ex);}
}
}

后话

其实raw镜像是可以使用qemu-img直接转成vmdk文件,然后在VMware中打开。

1
qemu-img convert -f raw f:\192.168.1.2.raw -O vmdk f:\centos.vmdk

懒0.0如果没用特殊情况的话,直接挂载他不香吗?(手动滑稽)
有机会的话找个时间根据zcgonvh大佬的源码捣鼓个python版的出来。

交个朋友
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

吹吹牛吗?

微信