前言
今天入门后端的时候,不小心改了非预期的数据,因为还没学到事务,所以恢复数据还比较麻烦,站在巨人的肩膀上还是解决了,原文连接在下面
https://blog.csdn.net/qq_42874315/article/details/140480570
解决办法
原理就是找到错误的sql语句记录,根据sql语句生成脚本,把sql语句“回滚”回之前数据。
步骤如下:
-
MySql默认是开启binlog的,查询指令是
show variables like 'log_bin';
-
查看binlog文件在本地的路径
SHOW VARIABLES LIKE 'log_bin_index'; //C:\ProgramData\MySQL\MySQL Server 8.0\Data\MYSZ-TIANW-bin
我们定位到自己的Data对应的文件夹
这个对应着修改的时间,我们找到对应时间修改的文件,我这里是bin.000010
- 需要知道你修改的大致的时间,执行cmd命令
需要cd到对应目录 :
cd C:\ProgramData\MySQL\MySQL Server 8.0\Data
mysqlbinlog --no-defaults --start-datetime="2024-08-15 14:00:00" --stop-datetime="2024-08-15 14:50:28" --base64-output=DECODE-ROWS --verbose bin.000010
命令有三个参数要改,开始和结束时间,日志的名称。开始结束时间如果不知道的话范围由大到小,慢慢缩减,注意名称bin.000010需要文件夹中的全称 需要结合你自己日志的名称。
假设我的错误的sql语句是(demo数据库)
update t_emp set job = "ANALYST",deptno = 20;
则部分的输出结果如下:
其中@3是job列,@8是deptno,修改的时间是14:09:04
如果觉得cmd面板不好分析 也可以转成txt查看执行:mysqlbinlog bin.000010 > ./binlog.txt
- 定位到是14:09:04时间后就可以保存对应的日志出来,生成对应的文件(其实就是截取文本而已)
mysqlbinlog --no-defaults --start-datetime="2024-08-15 14:09:00" --stop-datetime="2024-08-15 14:10:00" --base64-output=DECODE-ROWS --verbose bin.000010 > error_binlog_2024-08-15-14-09.sql
- 用idea执行下面代码,生成对应的sql语句(就是字符替换,修改改成你表中对应的字段)
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MySqlCallbackData {
public static void main(String[] args) {
String filePath = "C:\\ProgramData\\MySQL\\MySQL Server 8.0\\Data\\error_binlog_2024-08-15-14-09.sql";
String document = readFileContent(filePath);
List<String> updateStatements = parseDocument(document);
for (String statement : updateStatements) {
System.out.println(statement);
}
}
/**
* 读取文本内容
*/
private static String readFileContent(String filePath) {
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(new File(filePath)))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return content.toString();
}
/**
* 解析文本内容
*/
private static List<String> parseDocument(String document) {
List<String> updateStatements = new ArrayList<>();
// 每个"### UPDATE "是一条更新语句
String[] sections = document.split("### UPDATE ");
for (int i = 1; i < sections.length; i++) {
String section = sections[i];
String[] lines = section.split("\n");
// 待拼接的WHERE条件
String whereClause = "";
// 待拼接的SET
StringBuilder sb = new StringBuilder();
for (String line : lines) {
if (line.startsWith("### @1=")) {
whereClause = "empno = " + line.split("=")[1];
} else if (line.startsWith("### @3=")) {
//需要改成你表中的字段
sb.append("job = " + line.split("=")[1]);
} else if (line.startsWith("### @8=")) {
//需要改成你表中的字段
sb.append(",deptno = " + line.split("=")[1]);
}
// 不需要读取日志文件中SET的内容,跳过即可
if (line.startsWith("### SET")) {
break;
}
}
// 拼接SQL 需要改成你的表名字
String updateStatement = "UPDATE t_emp " + "SET " + sb + " WHERE " + whereClause + ";";
updateStatements.add(updateStatement);
}
return updateStatements;
}
}
编译器输出如下,拿到sql语句数据库执行下就恢复了:
UPDATE t_emp SET job = 'CLERK',deptno = 20 WHERE empno = 7369;
UPDATE t_emp SET job = 'SALESMAN',deptno = 30 WHERE empno = 7370;
UPDATE t_emp SET job = 'SALESMAN',deptno = 30 WHERE empno = 7521;
UPDATE t_emp SET job = 'MANAGER',deptno = 20 WHERE empno = 7566;
UPDATE t_emp SET job = 'SALESMAN',deptno = 30 WHERE empno = 7654;
UPDATE t_emp SET job = 'MANAGER',deptno = 30 WHERE empno = 7698;
UPDATE t_emp SET job = 'MANAGER',deptno = 10 WHERE empno = 7782;
UPDATE t_emp SET job = 'PRESIDENT',deptno = 10 WHERE empno = 7839;
UPDATE t_emp SET job = 'SALESMAN',deptno = 30 WHERE empno = 7844;
UPDATE t_emp SET job = 'CLERK',deptno = 20 WHERE empno = 7876;
UPDATE t_emp SET job = 'CLERK',deptno = 30 WHERE empno = 7900;
UPDATE t_emp SET job = 'CLERK',deptno = 10 WHERE empno = 7934;
UPDATE t_emp SET job = 'CLERK',deptno = NULL WHERE empno = 7999;
总结
sql语句修改数据需要很谨慎才行,后台之路任重而道远啊~