生产环境mysql数据库爆炸(windows),无法启动mysql。幸好ibd数据还在,所以查阅各种帖子,自己整理了一套恢复完整数据的方法。思路以及技术都比较简单,如有专业人士有更好的解决办法,欢迎沟通交流。
1,进入mysql 》data文件夹内,找到要恢复的库,将ibd文件备份出来。
2,在备份出来的文件夹内打开cmd,运行如下命令:
for /R %x in (*.ibd) do ibd2sdi --dump-file "%x".txt "%x"
这是批量运行mysql自带的命令> ibd2sdi。如果cmd无法执行命令,可以添加mysql/bin目录为环境变量。
这是批量转换后的txt文件。
如图所示,这是txt的json内容。第2,第3个Object是数据表的相关数据。
从这里面可以看到表名,字段,字段属性等等。图不完整,各位尝试后可仔细观察,所有信息都在此json 中。
3,有了json,用代码生成建表sql语句,代码如下:
public void test() {
Path tp = Paths.get("C:\\Users\\kings\\Desktop\\sxh-bak");
try {
List<Path> ps = Files.walk(tp).map(Path::toAbsolutePath).collect(Collectors.toList());
for (Path p : ps) {
if (!Files.isDirectory(p)) {
String jsonStr = TxtUtil.readTxt(p.toString());
JSONArray ja = JSON.parseArray(jsonStr);
JSONObject table = ja.getJSONObject(1)
.getJSONObject("object")
.getJSONObject("dd_object");
String tableName = table.getString("name");
String comment = table.getString("comment");
JSONArray columns = table.getJSONArray("columns");
String start = "CREATE TABLE `" + tableName + "` ( \n";
LinkedList<String> cols = new LinkedList<>();
for (int i = 0; i < columns.size(); i++) {
JSONObject col = columns.getJSONObject(i);
String colName = col.getString("name");
String colType = col.getString("column_type_utf8");
boolean isNullable = col.getBoolean("is_nullable");
String nullableStr = isNullable ? "DEFAULT NULL" : "NOT NULL";
String colComment = col.getString("comment");
String colstr;
if (colName.equals("id")) {
colstr = " `id` bigint NOT NULL AUTO_INCREMENT,";
} else if (colName.equals("DB_TRX_ID") || colName.equals("DB_ROLL_PTR")) {
continue;
} else {
colstr = "`" + colName + "` " + colType + " " + nullableStr + " COMMENT '" + colComment + "',";
}
cols.add(colstr);
}
String res = String.join("\n", cols);
String end = " PRIMARY KEY (`id`) \n" +
") ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4;";
String finalres = start + res + end;
System.out.println(finalres);
TxtUtil.createAndWrite(finalres, p.getParent() +"\\" + tableName + ".sql");
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
4,代码相关
上述代码中,比如主键,是否自增,索引什么的都没有仔细处理,各位自行斟酌。
5,恢复表数据
5.1,删除表空间
只要存在这个表,操作是肯定成功的。
ALTER TABLE `table_name` DISCARD TABLESPACE;
5.2,复制ibd到Mysql 的 data 文件夹
5.3,导入表空间
如果当前表(恢复出来的)的表结构和ibd文件(原始的)的表结构不一样。操作会失败。在工具里打开这张表mysql 会停止服务。必须手动删除DATA文件夹内的ibd文件,再重启mysql服务。
ALTER TABLE `table_name` IMPORT TABLESPACE;
该语句如正常执行,恭喜数据已完整恢复。
6,写在最后
mysql5.x的data文件不太一样。本帖子应该不适用。
数据无价,还是建议各位多备份。数据库出一次问题可是遭罪的很。
ibd2sdi 相关参考链接:
【MySQL】ibd2sdi工具介绍和使用_SQLplusDB的博客-优快云博客
有些老哥需要工具类,这里补充下txtutil
@Slf4j
public class TxtUtil {
public static String readTxt(String txtpath) {
StringBuilder result = new StringBuilder();
try {
InputStreamReader isr = new InputStreamReader(Files.newInputStream(Paths.get(txtpath)), StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr);
String s;
while ((s = br.readLine()) != null) {
result.append(System.lineSeparator()).append(s);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return result.toString();
}
/**
* 控制读几行
*
* @param txtpath 路径
* @param lineNumber 读取的行数
* @return
*/
public static String readTxt(String txtpath, int lineNumber) {
File file = new File(txtpath);
StringBuilder result = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String str;
for (int i = 0; i < lineNumber; i++) {
if ((str = br.readLine()) == null) {
break;
}
result.append(str);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return result.toString();
}
/**
* 读取指定行
*
* @param txtpath
* @param lineNumber
* @return
*/
public static String readLine(String txtpath, int lineNumber) {
File file = new File(txtpath);
StringBuilder result = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String str;
int i = 1;
while ((str = br.readLine()) != null) {
if (i == lineNumber) {
result.append(str);
break;
}
i++;
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return result.toString();
}
/**
* 读取指定行(多行)
*
* @param txtpath 路径
* @param lineNumbers 要取数据的行数
* @return 数据列表(有序list,对应数组)
*/
public static LinkedList<String> readLine(String txtpath, int[] lineNumbers) {
LinkedList<String> list = new LinkedList<>();
File file = new File(txtpath);
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String str;
int i = 1;
while ((str = br.readLine()) != null) {
for (int k = 0; k < lineNumbers.length; k++) {
if (i == lineNumbers[k]) {
list.add(str);
}
}
i++;
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
/**
* 读取指定行(多行)
*
* @param txtpath 路径
* @param targetLines 要取数据的行数
* @return 数据列表(有序list,对应数组)
*/
public static JSONObject readLine(String txtpath, JSONObject targetLines) {
JSONObject result = new JSONObject();
File file = new File(txtpath);
try {
BufferedReader br = new BufferedReader(new FileReader(file));
StringBuilder total = new StringBuilder();
String str;
int i = 1;
while ((str = br.readLine()) != null) {
for (String key : targetLines.keySet()) {
if (targetLines.getInteger(key) == i) {
String value = str.replaceAll((char) 12288 + "", "").replaceAll(":", ":").replaceAll(" ", "");
result.put(key, value);
}
}
total.append(str).append("\n");
i++;
}
br.close();
result.put("content", total.toString());
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 读取所有返回LIST
*
* @param txtpath 路径
* @return 数据列表(有序list,对应数组)
*/
public static LinkedList<String> readLine(String txtpath) {
LinkedList<String> list = new LinkedList<>();
File file = new File(txtpath);
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String str;
while ((str = br.readLine()) != null) {
list.add(str);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
public static void createAndWrite(String content, String filePath) {
BufferedWriter out = null;
try {
File file = new File(filePath);
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
//编码格式可自己更换
out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(filePath, true), "UTF-8"));
out.write(content + "\r\n");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}