记录一次生产事故MYSQL执行语句错误,回滚数据

MYSQL执行语句错误,使用binlog回滚数据:

早上接到 leader 通知,说有个开发不小心将数据库的一张表数据全量使用update语句更新错误了,由于之前的备份数据是一周之前的,与用户使用存在偏差,因此需要采用mysql-binlog恢复这张表数据 ~


问题描述

首先,因为使用原来一周前的数据库备份数据,用户使用会有偏差,因此不能使用原来的备份数据进行恢复,那么,采取binlog的方式恢复。

因为不确定服务器上面的mysql 是否开启了binlog 以及 它使用的模式,我们先进行一次binlog相关信息的查询。
1.确保数据库开启了log_bin

show variables  like 'log_bin';

+-------------+-----+
|Variable_name|Value|
+-------------+-----+
|log_bin      |ON   |
+-------------+-----+

2.查看binlog 的模式类型

show global variables like “%binlog_format%;
+-------------+---------+
|Variable_name|  Value  |
+-------------+---------+
|binlog_format|STATEMENT|
+-------------+---------+

好的,由此可以确定服务器该mysql库已经开启了binlog日志,且使用的是STATEMENT模式。
在这里有必要说明一下binlog 三种模式的区别(问题排查可以略过此章节)。

binlog三种日志模式区别:

ROW Level

记录的方式是,即如果批量修改数据,记录的不是批量修改的SQL语句事件,而是每条记录被更改的SQL语句,因此,ROW模式的binlog日志文件会变得很“重”。(存每一条改动sql,数据量大)

优点:row level的binlog日志内容会非常清楚的记录下每一行数据被修改的细节。而且不会出现某些特定情况下存储过程或function,以及trigger的调用和触发器无法被正确复制的问题。

缺点:row level下,所有执行的语句当记录到日志中的时候,都以每行记录的修改来记录,这样可能会产生大量的日志内容,产生的binlog日志量是惊人的。批量修改几百万条数据,那么记录几百万行……

Statement level(默认)

记录每一条修改数据的SQL语句(批量修改时,记录的不是单条SQL语句,而是批量修改的SQL语句事件)。看上面的图解可以很好的理解row level和statement level两种模式的区别。

优点:statement模式记录的更改的SQ语句事件,并非每条更改记录,所以大大减少了binlog日志量,节约磁盘IO,提高性能。

缺点:statement level下对一些特殊功能的复制效果不是很好,比如:函数、存储过程的复制。由于row level是基于每一行的变化来记录的,所以不会出现类似问题

Mixed

实际上就是前两种模式的结合。在Mixed模式下,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。


找到错误sql:

接下来,我们需要在服务器上面找到对应的binlog日志。

mysql> show master status;

+----------------+---------+------------+----------------+-----------------+
|File            |Position |Binlog_Do_DB|Binlog_Ignore_DB|Executed_Gtid_Set|
+----------------+---------+------------+----------------+-----------------+
|mysql-bin.000150|182954971|            |                |                 |
+----------------+---------+------------+----------------+-----------------+

我们可以看到,最新的binlog日志应该是150 ,于是我们上服务器找到对应mysql存放目录。
由于我再my.cnf文件中没有额外配置存放目录,因此binlog就在/var/lib/mysql下面。

cd /var/lib/mysql
ll
total 157584044
------
-rw-rw---- 1 mysql mysql 1074201869 Sep 15 00:30 mysql-bin.000148
-rw-rw---- 1 mysql mysql 1074201893 Sep 15 00:30 mysql-bin.000149
-rw-rw---- 1 mysql mysql 1074201352 Sep 15 00:30 mysql-bin.000150

这个文件理论上可以直接打开,但是vim 过后发现是乱码情况。
查阅后知道,mysqlbinlog使用的编码有bug问题,因此,只能使用mysql本身的命令。
和对应的开发对接后,发现执行命令失误的时间为2022-09-16 10点左右,然后进行排查。

mysqlbinlog --no-defaults --database=bp_wms --start-datetime='2022-09-16 10:44:20' --stop-datetime='2022-09-16 10:44:30' mysql-bin.000150

命令说明:
–no-defaults 表示使用mysql 的编码格式进行查看
–database指定库名
–start-datetime及–stop-datetime指定开始时间及结束时间
最后接mysql-bin查看日志。

以下是查询结果内容(已做数据脱敏处理):

------
/*!*/;
# at 542053108
#220916 10:44:24 server id 3306 end_log_pos 542054167 CRC32 0x0a71be01    Query thread_id=170164 exec_time=1 error_code=0
use `bp_wms` /*!*/;
SET TIMESTAMP=1663296264 /*!*/;
update `RECEIVE_OUT` set CREATE_USER='xx.xxx',UPDATE_USER='xx.xxx' where 1=1;
/*!*/;
# at 542054167
------

由此,我们可以确定当时出现错误的语句,找到问题所在,update没有加条件执行了。

解决问题并恢复数据:

我们计划恢复数据,先将一周前的备份数据重新存库,新建库表,然后使用命令, 将一周时间到错误sql日志之前的所有sql命令全部执行一遍。尽管该办法比较笨,但是确实是一个恢复的手段,目前我们能想到的办法也只有这样了。

1.新建库表,执行一周前的数据存储

grep "RECEIVE_OUT" bp_wms.sql > RECEIVE_OUT.sql

仅仅将该表数据进行获取,然后再你新建的库中执行sql。
2.再根据时间节点,将一周前到至今的binlog sql日志获取
观察之后发现有三个binlog日志,需要进行恢复。

mysqlbinlog --no-defaults --database=bp_wms --start-datetime='2022-09-11 00:00:00' --stop-datetime='2022-09-16 10:44:30' mysql-bin.000148 > /home/rollback/binlog148.sql
mysqlbinlog --no-defaults --database=bp_wms --start-datetime='2022-09-11 00:00:00' --stop-datetime='2022-09-16 10:44:30' mysql-bin.000149 > /home/rollback/binlog149.sql
mysqlbinlog --no-defaults --database=bp_wms --start-datetime='2022-09-11 00:00:00' --stop-datetime='2022-09-16 10:44:30' mysql-bin.000150 > /home/rollback/binlog150.sql

依次将三个日志的内容分别存储,再筛选出RECEIVE_OUT表的所有数据

grep "RECEIVE_OUT" binlog148.sql >/home/rollback/sql/SQL.148.sql
grep "RECEIVE_OUT" binlog149.sql >/home/rollback/sql/SQL.149.sql
grep "RECEIVE_OUT" binlog150.sql >/home/rollback/sql/SQL.150.sql

3.处理sql 日志,将里面的sql 每条sql最后添加; 使它能够执行。
4.最终执行sql 并将数据恢复成功。

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值