记一次从删库到恢复的经历

公司正在做社交项目,涉及到的各个端人员比较多,开发、运维、产品、测试20多个,当然,对大公司来说,20其实不算多,进入正题,我们数据库使用的是MySQL,运维搭建的主从,而正是由于这个所谓的主从,害苦了开发的兄弟们;缓存使用的是Redis,MySQL数据同步到Redis使用的是阿里开源的Canal,了解Canal的都知道,Canal是读取MySQL的二进制文件,进而得到数据的变更,然后同步到Redis。

某天傍晚,后台开发兄弟们日常更新,由于产品要求用户的昵称支持输入表情,我们不得不修改MySQL的配置文件,使其支持表情,修改了Master配置文件,自然就需要修改Slave 的配置文件,将Master重启后,然后重启Slave,都重启后,发现master上的数据不能同步到slave了,我擦,不过由于项目刚刚起步,访问量不大,加上天色已晚,后台开发兄弟决定,不能同步就不能同步吧,明天找运维同事搞定。

第二天,运维同事了解情况后,连上服务器,一顿操作。下午三点左右, 产品正在演示APP,说APP首页没有数据了,我们说,呵呵,怎么可能,然后登上数据库后,我我我擦,master 用户表的数据被清空了,瞬间有点发毛,难道被攻击了,赶紧看下 slave,我*,比master上的数据都干净,而此时的master 里面的用户表 还有几条刚刚注册进来的几条数据,冷汗直流。缓存,对,看下缓存中,赶紧连上缓存服务器,疑问了,缓存里面 还有数据,master 用户表的数据没有了,而缓存又是读取的master里面的二进制文件,先不考虑这个问题,目前的紧急问题是赶紧恢复数据。由于开启了binlog,那就从这里恢复,一同事赶紧下载binlog,玛德,文件太大,拉下来 将近40分钟,这个方法暂缓。诶,缓存不是有数据嘛,那就把缓存里面的数据重新写到MySQL,赶紧写一个for循环,从 丢失的数据的 ID 往前读,然后一个个insert到数据库,当然,这种方法会导致数据有点旧,不过,和数据完全丢失比起来,那就毛毛雨了。终于,历经将近一小时,数据恢复。

数据恢复后,我们首要的目标就是查原因,原因无非就是两个,一是被攻击,二是内部人员所为。查看MySQL的二进制日志,数据丢失大约是在3点左右,而服务器是在曼谷,时间比北京时间晚一个小时,也就是服务器上的时间是2点左右,拉二进制文件找原因,呵,太慢了,几行命令搞定,

1
2
3
4
5
-- 查看某个时间段的二进制日志,并且输出到指定的文件
mysqlbinlog --no-defaults --start-datetime="2018-12-12 13:00:00" --stop-datetime="2018-12-12 14:40:00" mysql-bin.000085 -vv --base64-output=decode-rows | more >> target.txt

-- 将@1、@2等一系列看不懂的符号转换为SQL语句
cat target.txt | sed -n '/###/p' | sed 's/### //g;s/\/\*.*/,/g;s/DELETE FROM/INSERT INTO/g;' | sed -r 's/(@4.*),/\1;/g' | sed 's/@[0-9]*\=//g' > test.sql

然后将 test.sql 文件拉下来,查看,发现两处可疑点(即两条SQL),如下:

1
2
3
4
-- 创建一个像user一样的表,user_bak
create table user_bak like user;
-- 删除 user_bak
drop table user_bak;

但是,没有清空或者删除user 表的sql,奇了怪了。突然,技术总监过来问,有没有找到原因,我说还没有,只是找到了两条可疑的SQL,但是还没发现有清空user的SQL,因为一直在看master上的日志,因为是主从,看master 上的日志和看slave上的一样,然而,总监却说,slave上的日志也要查下,好吧,虽然觉得看了没什么用,谁让他是老大呢。登上slave服务器,却发现,为什么 slave 上也有二进制日志呢,先不管,看下日志,发现有如下SQL:

1
2
3
4
5
6
7
8
9
10
11
-- 删除 user
drop table user;
-- 创建user
create table user(
...省略字段
)
COMMENT='用户表'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=4xxxxxx
;

虽然打脸了,但是为什么slave上也会有二进制日志呢,赶紧找运维问下到底是不是主从,运维说是,算了,还是看下配置文件,呵呵,竟然是主主,不是说好的主从呢,怪不得在 slave上执行了 删除用户表,然后创建用户表,创建的下一个自增点还是丢失前的自增点,master上用户表的数据就不存在了,这样Redis中数据存在的原因也破解了,Redis 读取的是master中的二进制文件,正是由于从所谓的“从”删除然后重建user表后(记录到“从”的二进制日志),“主”上就有了user 表(记录到“主”的中继日志,并没有在这个“主”的二进制日志,所以 Canal根本没有读取到在“从”上的操作),原因是找到了,但是 是谁执行的呢??当我们正在讨论的时候,运维说他在做测试,测试昨天的主从有没有正常了,他在修复昨天不能主从复制的问题。

崩溃,修复就修复,执行删除干嘛,而说好的主从,咋还变成主主了,问题 原因找到了,意味着不用加班到天亮了。

这种问题一定要杜绝:

  • 非特殊情况,禁止使用ROOT账户;
  • 相关人员分配MySQL账户,制定权限;
  • 禁止执行不带条件的DELETE、UPDATE,TRUNCATE,DROP
坚持原创技术分享,您的支持将鼓励我继续创作!
-------------本文结束感谢您的阅读-------------
分享到:
0%