本系列文章主要是本人在游戏服务端开发过程中,遇到的一些不那么为人熟知但我又觉得比较重要的MySQL知识的介绍。希望里面浅薄的文字能为了提供一点点的帮助。
数据回滚与数据还原
相信大家在自己的职业生涯中可能都有听说过甚至是遇到过下面这样的场景:
- 直接操作数据库时失误,不小心delete错表或者delete的where条件错误(曾经同组一个开发因为delete语句没加where条件半夜把我call醒)。对于开发成员有线上数据库操作权限的项目组来说出现这种情况的概率比想象的要大很多。需要手动操作数据库说明情况已经比较紧急了,慌忙且紧急的情况下出问题的概率自然会高;
- 代码出了bug导致数据操作错误,比如数据被误删、修改等等;
- 某某公司的一位员工一怒之下把线上数据库给清了;
DBA或开发人员,有时会误删或者误更新数据。线上环境如果出现这样的情况就需要快速恢复。其中使用数据库手段回滚数据,是最常用并且很多时候是唯一的选项(有些功能上出现的BUG可能通过其他途径进行修复)。
上面所说的问题有些可以通过权限管理和其他手段去避免(这些我会在后面提到)。但是这里想介绍的是如果真的不幸发生了以上这些问题,我们可以如何做数据回滚。下面我用一个自己遇到的案例来介绍,并列举一些业界比较常用的方案做一些简单的对比。如果你们还没有对恢复数据这一块做过考虑,可以根据自身项目的实际情况看看哪种比较适合你们。
回滚案例介绍
Bug表现:
线上新上了一个换皮活动B(由活动A换皮而来),活动B上线一段时间陆续有玩家投诉之前的活动A的奖励进度没了,原来一些能够领取的活动A的奖励也无法领取;还有一些玩家刚参与活动B就能领取最终大奖。
Bug原因:
查看代码发现,因为活动A和活动B逻辑和持久化的数据都一模一样,开发只是将活动A的代码拷过来就当是活动B,最后导致活动B的数据存进了活动A的表中。这样就有一部分玩家活动A的数据被活动B数据替换,导致活动A的数据丢失和活动B的数据错乱。数据的逻辑错误和正确的情况如下图所示:
错误的做法:
image.png
正确的做法:
image.png
活动B上线之后,活动A是不会对数据进行修改。所以我们只要拿到活动B上线前对表A最近的一次备份,然后执行从这个备份时间节点到活动B真正产生数据时间节点这一时间段表A产生的SQL即可。
修复过程:
活动A的数据修复:
1. 取受影响的活动A的表A最近一次全量备份,我们线上数据库是一天一备。所以最近一次备份是当天0点;
2. 用第一步得到的备份表A恢复出一个临时表A;
3. 然后通过线上binlog日志,解析出从备份时间节点到活动B上线之间表A产生的SQL(只取增删改的SQL);
4. 把第3步得到的SQL,在临时表A执行一遍;
5. 最后把临时表A,复制到线上数据库,热更代码使活动A读取这张临时表A。
活动B的数据修复:
1. 紧急屏蔽线上活动B;
2. 通过线上binlog日志,解析出从活动B上线之后在表A产生的所有SQL(只取增删改的SQL);
3. 创建表B(和表A结构一致),然后把第2步得到的SQL,在表B执行一遍;
4. 热更代码使活动B读取表B,线上重新开启活动B。
业界其他数据回滚方式:
1. Flashback:
介绍:Flashback方式可以在没有全备的情况下,将数据回滚到一定时间范围内(和binlog日志保存时间挂钩)任意时间节点上。Flashback恢复数据的原理是:binlog_format=ROW的情况下会记录数据更新前后全部状态,这样就能从binlog中获得颠倒的SQL然后拿回到当前表进行重放(insert变delete、、detele变回insert、update前后的值互换)。原理介绍请戳:https://zhuanlan.zhihu.com/p/68845158
优点:利用备份重搭实例,再应用去除错误sql后的binlog来恢复数据的方式操作较为繁琐,甚至需要停机维护,很难做到快速回滚。Flashback利用binlog直接进行回滚,能快速恢复且不用停机。
缺点:因为binlog要设置为ROW格式,所以binlog日志会变得更大,对硬件的要求也会提升。
使用要求:需要数据库的binlog相关配置满足:
binlog_format=ROW # 日志记录精确到每一行的修改
binlog_row_image=FULL # 每次修改都记录修改前后的值
查看binlog日志是否开启命令:show global variables like 'log_bin%';
查看binlog_format格式命令:show global variables like '%binlog_format%';
或者查看MySQL的配置文件:my.cnf(Linux系统) 或者 my.ini(Windows系统)
业界一些优秀的Flashback工具:MyFlash 、binlog2sql。
2. 没有binlog日志的每日全备:
介绍:很多公司都有每日全备(存最近15天的全备数据,每天一份)。如果出现问题,直接从最近的一次全备数据中取数据。但是由于没有开启binlog日志,就没办法回滚到指定时间点的数据。比如想回滚2号下午5点的数据,只能是从2号凌晨0点全备的数据拿出来,舍弃了2号凌晨5点至2号下午5点变更的数据。
优点:不用打印binlog日志,节省机器性能;操作简单,还原速度快。适用于一些即使有少部分数据会被丢弃也没关系的系统——用户行为统计日志、策划需要查询的玩家物品收支日志等等;
缺点:数据恢复只能恢复一部分。
使用要求:对于每日全量备份这一点,其实所有云服务器厂商都支持。如果是自己搭建的MySQL服务器可以使用全量热备工具:Percona XtraBackup来实现每日全备,阿里和腾讯的MySQL云服务就是通过这个工具来实现备份的。该工具是一个MySQL物理热备份工具,优点有:
- 可以在目标数据库不关服的情况下进行热备,过程中目标库能正常运行(不会锁表锁库)。官网对原理的介绍比较粗略,这一篇写的更容易理解一点。实现原理是通过复制.ibd文件和redo log日志回放实现;
- 既可以进行全量备份也可以进行增量备份;
- 能将MySQL备份压缩传输到其他服务器;
- 备份过程对服务器负载很小(在可控范围会对IO和磁盘有一点点影响,当然还是建议选择在服务器压力最小的时段进行);
- 开源免费
总结
由于游戏常常面临大数据量和高并发的场景,所以会对原理和设计有比较高的要求。许多游戏公司可能没有专门的DBA(甚至也没有专门的运维、SDK支撑),这种情况下开发人员扎实的数据库基础就是项目可靠的重要保证。
网友评论