mysql 错误操作整个表全部数据后如何恢复?(百万行SQL,通过binlog日志恢复)
事件起因
找Binlog文件位置
我的是mysql 8,ubuntu,我的binlog文件在这个目录
/var/lib/mysql
聪明的你一眼就能看出这个binlog 27号文件只有六百多兆,序号也是最新的,证明这个是最近的一个文件,就是他了,冲
读取指定时间范围内的数据操作DML
mysqlbinlog --base64-output=DECODE-ROWS --verbose --start-datetime="2023-12-23 01:00:00" --stop-datetime="2023-12-23 01:10:00" /var/lib/mysql/binlog.000027 > restore.sql
把导出的SQL清理数据
导出的sql,我用IDEA打开的,随便你用哪个好用的文本处理工具,我们直接搜索set这个词,就能找到SQL的最开始位置如图
例如一条
### UPDATE `xx`.`t_xx`
### WHERE
### @1=1
### @2='xx'
### @3=NULL
### @4='默认'
### @5='xx'
### @6='xxxx'
### @7='1'
### @8=1
### @9='2023-08-19 15:29:58'
### @10='2023-08-19 18:41:06'
### SET
### @1=1
### @2='xx'
### @3='xx'
### @4='默认'
### @5='xx'
### @6='xx'
### @7='1'
### @8=1
### @9='2023-08-19 15:29:58'
### @10='2023-12-23 01:07:09'
这个数据的格式不难看出,记录了变更之前和之后的每个字段数据,xx为示意作用,在真实场景中就是你的真实数据,字段根据顺序@1一直到@10,证明我有10个字段,然后没个字段的值都记录了,整个结构为
update 表
where
每个字段的旧值
set
每个字段的新值
那么我们现在开始处理数据
IDEA 对于这个26MB的文件直接变仅读模式,不给我编辑,于是打开了subline text这个工具处理文本
处理数据
删除update语句以外的所有无关行
我们要的核心就是这个update语句,我们先掐头去尾,保留中间的update,我这个文件目前有100万行~
现在我这个文件只有那一个失误SQL执行的所有update语句,总共92万行~
去掉开头的###
/**
* author: humorchen
* date: 2023/12/23/023 1:57
* desc:
**/
public class RestoreData {
/**
* 移除头部标识
*
* @throws Exception
*/
public static void removeHeadSignal() throws Exception {
String file = "C:\\Users\\Administrator\\Desktop\\restore.sql";
String newFile = "C:\\Users\\Administrator\\Desktop\\restore_1.sql";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(newFile));
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
if (line.startsWith("### ")) {
line = line.substring(4);
}
bufferedWriter.write(line);
bufferedWriter.write("\n");
}
bufferedWriter.flush();
bufferedWriter.close();
bufferedReader.close();
}
public static void main(String[] args) throws Exception {
removeHeadSignal();
}
}
set和where互换
/**
* where和set互换
*
* @throws Exception
*/
public static void reverseWhereAndSet() throws Exception {
String file = "C:\\Users\\Administrator\\Desktop\\restore_1.sql";
String newFile = "C:\\Users\\Administrator\\Desktop\\restore_2.sql";
final String SET = "SET";
final String WHERE = "WHERE";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(newFile));
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
if (line.startsWith(WHERE)) {
line = SET;
} else if (line.startsWith(SET)) {
line = WHERE;
}
bufferedWriter.write(line);
bufferedWriter.write("\n");
}
bufferedWriter.flush();
bufferedWriter.close();
bufferedReader.close();
}
public static void main(String[] args) throws Exception {
// removeHeadSignal();
reverseWhereAndSet();
}
字段替换
把字段数组作为参数数组传入函数,然后读取@多少,替换为对应下表的字段名
/**
* 替换字段名
*
* @param columnNames
*/
public static void replaceColumns(String[] columnNames) throws Exception {
String file = "C:\\Users\\Administrator\\Desktop\\restore_2.sql";
String newFile = "C:\\Users\\Administrator\\Desktop\\restore_3.sql";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(newFile));
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
final String PREFIX = " @";
final int PREFIX_LEN = PREFIX.length();
while ((line = bufferedReader.readLine()) != null) {
// line: @1=1
if (line.length() > PREFIX_LEN && line.startsWith(PREFIX)) {
// 等于号位置
int eqIndex = line.indexOf("=");
String indexStr = line.substring(PREFIX_LEN, eqIndex);
if (StrUtil.isNumeric(indexStr)) {
int index = Integer.parseInt(indexStr) - 1;
if (index >= 0 && index < columnNames.length) {
// 替换字段名
line = line.replace(PREFIX + indexStr, columnNames[index]);
}
}
}
bufferedWriter.write(line);
bufferedWriter.write("\n");
}
bufferedWriter.flush();
bufferedWriter.close();
bufferedReader.close();
}
public static void main(String[] args) throws Exception {
// removeHeadSignal();
// reverseWhereAndSet();
replaceColumns(new String[]{"id", "title", "icon", "type", "email", "prompt", "temperature", "keep_context", "create_time", "update_time"});
}
加,和and
/**
* 添加,和and和;
*
* @throws Exception
*/
public static void addPrefix(String[] addPrefix) throws Exception {
String file = "C:\\Users\\Administrator\\Desktop\\restore_3.sql";
String newFile = "C:\\Users\\Administrator\\Desktop\\restore_4.sql";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(newFile));
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
int index = -1;
final String UPDATE = "UPDATE";
while ((line = bufferedReader.readLine()) != null) {
if (line.startsWith(UPDATE)) {
index = 0;
bufferedWriter.write("\n\n");
;
}
if (index >= 0) {
line = line + addPrefix[index];
index++;
}
bufferedWriter.write(line);
bufferedWriter.write("\n");
}
bufferedWriter.flush();
bufferedWriter.close();
bufferedReader.close();
}
public static void main(String[] args) throws Exception {
// removeHeadSignal();
// reverseWhereAndSet();
// replaceColumns(new String[]{"id", "title", "icon", "type", "email", "prompt", "temperature", "keep_context", "create_time", "update_time"});
addPrefix(new String[]{" ", " ", ",", ",", ",", ",", ",", ",", ",", ",", ",", " ", " ", " and ", " and ", " and ", " and ", " and ", " and ", " and ", " and ", " and ", " ;"});
}