记一次值班热线数据恢复操作

今天值班遇到个问题,热线帮用户跨项目复制wiki,由于此功能并不能将原项目wiki的树结构也完整的复制过去,从而导致复制的wiki 在目标项目中是平铺的。因此用户想回滚此次热线的操作,于是有了这篇文章的产生

需要回滚(恢复)数据,首先需要了解几个问题:

  • 热线是什么时候进行复制操作的?
  • 源项目是什么?目标项目是什么?
  • 这个操作大概什么时候结束的?

关于上面三个问题的答案:

热线的回复是:

  • 4.23号16:13分操作的
  • 源项目是A, 目标项目是B
  • 操作时长不确定,做完操作,就去做其他事情了,再回来看是19:15。

要解决这个问题,想过两个方案:

  • 去源项目把wiki 的name 全部找出来,然后去目标项目用wiki的name找一把,把同名的删掉
  • 刨binlog ,把这段操作时间内的目标项目insert 的id 找出来,然后一把删除

方案一:介于功能本身的问题,如果wiki名在目标项目已存在,则会生成一条A项目id_为前缀的新wiki名,所以不清楚有多少目标项目本身存在同名的wiki。介于这个原因,暂时不考虑这个方案

方案二:这个方案的主要问题在于要清楚的知道操作的开始时间和结束时间,上面说过开始时间知道,但是不知道结束时间,所以要搞清楚结束时间才行。

  1. 看了下rotate的binlog的文件最新时间, 找出属于目标范围时间段的文件名。
  2. 具体的binlog 查找语句
mysqlbinlog --start-datetime="2020-04-23 16:13:00" --stoptime="2020-04-23 16:15:00" -vv --base64-output=decode-rows mybinlog.007001 | grep -B 6 -A 10 'INSERT INTO `库名`.`wikis`' | grep -B 10 -A 9 '@1=符合目标项目id的wiki id'

PS:@1 是表的id,这个id 本身包含了目标项目信息,所以用@1=xxxx来过滤是否是目标项目的插入操作

结束时间怎么定呢?看了下php.ini的max_execution_time 是30s。并且该复制操作没有使用异步。所以理论上不会很长时间才结束,可以看nginx upstream time,但是经我研究,发现不太准

所以我分别统计了一分钟,两分钟和余下当天时间内的,进行对比,这个对比包括数据量的对比以及数据本身的对比。

对比数据量的作用是:通常一两分钟内,同一个项目不会有很多的插入操作,不会一分钟内有很多人写wiki。(这个是项目经验之谈)

再者,对比数据本身,用提取出来的id去源项目里找,用名称找,可以找到同名的,证明很大可能是复制操作引入的。比如可以用提取出来的最后一条去源项目找找。以确定最后一条是否符合,如果最大时间符合,那么这时间之前的,理论上也应符合,因为时间比较短。

于是发现把–stoptime参数即可,当天这个开始时间点后面的插入操作的,基本热线值班的复制操作,于是在上面的基础上再提取出id即可。假设上面mysqlbinlog 代码产生的log文件是:/tmp/7001_04231612.log

接下来可以再继续刨出id行:

grep '@1=' /tmp/7001_04231612.log > /tmp/wiki_ids_7001_04231612.log

当提取出id之后,查出近w条数据,先做好备份,再删除,通过以上方法,顺利的帮用户恢复到操作之前的数据,打完收工。