MySQL误操作过程
1 事件背景
由于使用工具统一查看生产数据库和测试数据库,在生产数据库打开查询后,忘记关掉。在本地测试更改数据进行测试,误操作了
UPDATE
生产环境的某张表创建数据.误操作语句为:
UPDATE TABLE SET createtime = now()
2 恢复环境准备
2.1 MySQL的操作日志
必须具有MySQL的操作日志,如:
mysql-bin.001540
文件 ;至于文件哪里获取,先自行Google(因为每个公司的安装环境不一样)
2.2 MySQL的解析日志工具
本地安装一个MySQL,并且MySQL的
bin
目录下必须具有mysqlbinlog
工具
3 恢复过程
3.1 mysqlBinlog工具解析日志文件
开始操作
windows系统进行
CMD
命令,进入到MySQL的bin
目录下(mysqlBinlog
工具的目录)
输入命令
mysqlbinlog --base64-output=decode-rows -v --set-charset=utf8 --start-position=9322490 --stop-position=9476586 d:\mysql-bin.001540 > d:\mysqlBinlog.sql
base64-output
针对日志内容进行解码(必须带上)start-position
从某行开始截取日志文件的记录(不填,默认解析日志文件所有的操作记录)stop-position
从某行结束截取日志文件的记录(不填,默认解析日志文件所有的操作记录)
3.2 提取sql语句恢复
先观察mysqlBinlog工具解析日志文件结构
这些字段对应更新数据的字段
Java代码提取误操作的SQL(针对单表提取)
public static void main(String [] args) throws IOException {
//mysqlbinlog工具解析后的sql文件
String readPath = "d://mysqlBinlog.sql";
String writePath = "d://flushFormat.sql";
writeSQLToFile(writePath,readFileToSQL2(readPath)) ;
}
/**
* 读取mysqlbinlog工具解析后的sql文件
* 注意:读取文件使用RandomAccessFile的readLine逐行读取处理,处理字符串没编码概念,容易乱码;可使用其他IO类代替读取
* @param path 读取文件的路径(绝对路径)
* @return StringBuilder 拼接好的SQL
* @throws IOException 读取文件相关的操作引起
*/
public static StringBuilder readFileToSQL2(String path) throws IOException {
StringBuilder sql =new StringBuilder() ;
RandomAccessFile file = new RandomAccessFile(path ,"r");
//固定提取的字符串
String formatSQL = "UPDATE `database`.`table` SET createtime=%s WHERE ID=%s AND createtime=%s; %n";
String createtime = StringUtils.EMPTY;
String id = StringUtils.EMPTY;
//修改前的值
String oldTime = StringUtils.EMPTY;
//逐行读取
while ( file.read() != -1 ){
//readLine方法没编码概念,对中文支持不好,若处理字符串乱码的,可使用其他IO类代替RandomAccessFile
String line = file.readLine();
if(Objects.isNull(line)){
break;
}
//先过滤特殊字符
line = line.replaceAll("#", StringUtils.EMPTY).trim();
//误操作的字段
if(StringUtils.contains(line,"@8") ){
if(StringUtils.isNotEmpty(oldTime)){
//误操作修改后的值
createtime = line.substring(line.indexOf("=")+1);
}else {
//误操作修改前的值
oldTime = line.substring(line.indexOf("=")+1);
}
}
//主键
if(StringUtils.contains(line,"@1") ){
id = line.substring(line.indexOf("=")+1);
}
//若数据已经不为空,进行拼接
if(!StringUtils.equalsAny( StringUtils.EMPTY,createtime,id,oldTime) ){
sql.append(String.format(formatSQL, oldTime, id, createtime));
//还原,用于下一次拼接
createtime = StringUtils.EMPTY;
id = StringUtils.EMPTY;
oldTime = StringUtils.EMPTY;
}
}
file.close();
return sql;
}
/**
* 将SQL字符串写到某个文件上
* @param path 写入到的文件路径(绝对路径)
* @param sql 拼接好的SQL
* @throws IOException 写入文件操作引起
*/
public static void writeSQLToFile(String path,StringBuilder sql) throws IOException {
RandomAccessFile file = new RandomAccessFile(path ,"rw");
file.writeUTF(sql.toString());
file.close();
}
注意:这里提取的SQL是为了还原误操作前的
UPDATE
数据
执行提取的SQL文件恢复(完)