误更新整张数据库表某字段值的还原方法

起因

  • 某个上午,写完需求后开始自测,数据库拟了些假数据,由于是重复使用该条记录多次debug,就写了条sql去修改status字段的值。
    update table set status = 'PAID';
  • 这操作简直下饭了,连条件都忘写了,吃完饭回来再看发现怎么看这么不对劲。咦?这里没有条件怎么知道我要更新哪条,这么智能了嘛?逐渐意识到十分有十二分不对劲。去看了一些表记录数有1万多条记录,状态全改为PAID了,再改为PAID前,还更新为NONPAY过,这就两次更新记录了。咋整啊,模型用多了sql都写不明白了。嗯,这锅还得ai模型来背。既然是模型的锅,那也得有模型来解决了,马上就给了两种方案。

方式1

  • binlog2sql。这种方式是本地数据库或者有服务器数据库的root权限才可以。

    • 使用sql查看binlog日志是否开启

      show variables like 'log_bin';
      

      在这里插入图片描述

    • 查看binlog日志文件名
      show master status;
      在这里插入图片描述

    • 查看文件是否记录操作时间的相关操作(navicat的tools->history log可以看到sql操作和对应时间),找到update的时间节点
      在这里插入图片描述

    • 使用mysqlbinlog查看binlog日志是否有记录你update的记录,如果有,那就是在当前binlog日志中,没有则SHOW BINARY LOGS;查看更多binlog日志文件,可能是倒数第二个文件。
      mysqlbinlog --no-defaults -h 你的ip -P 你的数据库端口 -u 账号 -p --read-from-remote-server -v --base64-output=DECODE-ROWS --start-datetime="2026-01-26 12:20:00" --stop-datetime="2026-01-26 12:30:00" binlog.000184 > rollback_1220.txt

    • 确定了日志文件,如果有python环境。安装binlog2sql工具

    # 1. 安装工具
    pip install binlog2sql
    
    • 然后使用binlog2sql根据日志文件回滚。
  # 2. 预览回滚语句 (闪回模式)
  python binlog2sql.py -h 127.0.0.1 -P 3306 -u root -p'你的密码' \
  -d 你的数据库名 -t 表名 \
  --start-file='binlog.000185' \
  --start-datetime='2026-01-26 14:10:00' \ 
  --stop-datetime='2026-01-26 14:11:00' \
  --flashback

方式2

  • 由于我修改的是远程测试数据库,没有权限使用binlog2sql操作数据库

  • 同样的,还是先确定好日志文件,然后在终端使用mysqlbinlog导出日志文件,并查看内容。输入下面语句,时间范围则是前面看history log确定,输入后再输入数据库密码,就可以导出文件了。

  • mysqlbinlog --no-defaults -h 你的ip -P 你的数据库端口 -u 账号 -p --read-from-remote-server -v --base64-output=DECODE-ROWS --start-datetime="2026-01-26 12:20:00" --stop-datetime="2026-01-26 12:30:00" binlog.000184 > rollback_1220.txt
    
    • 导出文件后,通过vscode查看内容
  • 找到对应区间的日志,查看日志,找到更新的区间,根据 # at去定位,修改的范围,比如我等一下修改为NONPAY,第二下修改为PAID。那日志记录则是原始status->NONPAY; NONPAY->PAID。所以我只要找到第一次修改的的日志范围,就可以将状态改为原样。

  • 下面输入文件就是前面终端导出文件,两个POS就是我看导出的文件确认第一次update的日志范围(使用ctrl+f,输入update '表名’可以快速定位到开始位置,然后update…where…set为一条修改记录,where是原来的状态,set是更新的状态,@num对应字段,我的status是@13,update前有# at pos,这就可以确认区间)

  • 在这里插入图片描述

  • 然后用下面执行下面的py脚本,将set后的值改为where的值(更新前的字段值),导出sql文件,再去数据库执行就可以还原啦。

    import sys
    
    input_file = 'rollback_1220.txt'
    output_file = 'rollback_by_pos.sql'
    
    # 你确定的位点区间
    START_POS = 854396348
    END_POS = 854402914
    
    # 目标字段
    STATUS_COL = "@13"
    ID_COL = "@1"
    
    results = []
    
    with open(input_file, 'r', encoding='utf-8', errors='ignore') as f:
        in_range = False
        current_id = None
        current_status = None
        in_where = False
    
        for line in f:
            # 1. 识别位点,进入或退出区间
            if line.startswith("# at"):
                pos = int(line.split()[2])
                if pos >= START_POS and pos <= END_POS:
                    in_range = True
                elif pos > END_POS:
                    in_range = False
                    break
            
            if not in_range:
                continue
    
            # 2. 在区间内寻找 topup_package_order 表的更新
            # 识别 WHERE 部分
            if "### WHERE" in line:
                in_where = True
                continue
            
            # 识别 SET 部分(表示 WHERE 结束,准备生成 SQL)
            if "### SET" in line:
                if current_id and current_status:
                    sql = f"UPDATE topup_package_order SET order_status = '{current_status}' WHERE id = {current_id};"
                    results.append(sql)
                # 重置当前块信息
                current_id = None
                current_status = None
                in_where = False
                continue
    
            # 3. 在 WHERE 块中提取数据
            if in_where:
                # 提取 ID
                if ID_COL + "=" in line.replace(" ", ""):
                    parts = line.split("=")
                    if len(parts) > 1:
                        # 提取数字
                        val = "".join(filter(str.isdigit, parts[1]))
                        if val:
                            current_id = val
                
                # 提取原始状态
                if STATUS_COL + "=" in line.replace(" ", ""):
                    parts = line.split("=")
                    if len(parts) > 1:
                        # 提取引号内的内容
                        status_val = parts[1].strip().strip("'")
                        current_status = status_val
    
    # 写入结果
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write("\n".join(results))
    
    print(f"解析完成!")
    print(f"在位点 {START_POS}{END_POS} 之间共提取到 {len(results)} 条 SQL。")
    if len(results) > 0:
        print(f"首条记录预览: {results[0]}")
    
  • 脚本也是ai写的,直接执行没什么问题,需要的话可以给ai修饰一下。

  • 如果大家还有其他方式也可以分享在评论区哦!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐