MySQL误操作UPDATE某张表的数据, 恢复UPDATE前的数据(Java提取SQL进行恢复)

时间:2024-04-07 09:15:30

1 事件背景

由于使用工具统一查看生产数据库和测试数据库,在生产数据库打开查询后,忘记关掉。在本地测试更改数据进行测试,误操作了UPDATE生产环境的某张表创建数据.

误操作语句为:UPDATE TABLE SET createtime = now()

2 恢复环境准备

2.1 MySQL的操作日志

必须具有MySQL的操作日志,如: mysql-bin.001540文件 ;至于文件哪里获取,先自行Google(因为每个公司的安装环境不一样)

2.2 MySQL的解析日志工具

本地安装一个MySQL,并且MySQL的bin目录下必须具有mysqlbinlog工具MySQL误操作UPDATE某张表的数据, 恢复UPDATE前的数据(Java提取SQL进行恢复)

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工具解析日志文件结构

MySQL误操作UPDATE某张表的数据, 恢复UPDATE前的数据(Java提取SQL进行恢复)
这些字段对应更新数据的字段

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文件恢复(完)