漫谈程序员(二十)炉石传说罕见数据库事故!丢失30%数据,疑似误操作?

漫谈程序员(二十)炉石传说罕见数据库事故!丢失30%数据,疑似误操作?

一、引言

  最近看到一篇关于“炉石传说数据库事故”的文章,分享给大家,并简要谈一下自己的心得体会。
  看到我这标题,千万别以为我是故意为了吸引你的眼球,而是官方这么说的哦:
这里写图片描述
  这里用到个词—“回档”,今天第一次听说,最开始不理解啥意思,接着恍然大悟,不就是Oracle 9i开始提供的新特性Flashback么!
  你的朋友圈是不是也被刷屏了?昨天大概6点左右开始,我的朋友圈被刷屏了。
这里写图片描述
  结合贴吧的一些留言,简单回顾下大体过程:

  • 1月14日15:20开始,数据库由于供电异常中断,数据损坏。接着数据库带病工作2天。
  • 1月16日凌晨开始进行修复维护。
  • 1月17日下午,维护时间超过30小时,数据“修复”失败,丢失数据超过30%,接着发出上述故障公告。

  接着在多玩网站,一位自称曾是“天下3”的维护者透露:
“你们以为数据都在服务器里? 服务器只有硬件而已,硬盘数据13年-16年都是用的DELL的磁盘阵列服务器,而且是双机热备+异地容灾,我这台数据丢了,我另一台会有克隆的相同的数据。就算广州整个机房炸了,我上海机房异地也会有一台克隆的数据。
  所以数据丢了,数据丢了30%什么,大家就不要信了。
  我在做天下3运维的时候也遇到过N种问题,不过都被总监、经理他们这些人带着解决了。
  可以说,就算来个10岁的小朋友,会动电脑鼠标看得懂字,按照流程都不会出问题。一个团队4个人,一个经理, 5个人同时犯错?怎么可能因为操作失误就丢30%数据?”
  同时,另一位网友号称是内部消息,说是服务器人为操作失误。
这里写图片描述

二、真实的原因到底是什么?

  嗯。其实你肯定看不到真实原因了,我只是在告诉你架构和维护的基本知识。
  我们从几个不同的角度来解读这个问题。
  老杨是运维界的老司机,而据了解涉及的库可能是Oracle,我们来看看从这个角度来分析,问题可能在哪些地方。
  同时有双机热备+异地容灾,当然还应该有备份(哦,公告里说,备份数据也损坏了!),数据还是丢了30%,这又中了墨菲定律!
那么这个架构的问题在哪里?
  从我的经验来看,这本身的架构有问题:
  备份几乎应该是假的!別懵,难道你家的所有数据库最近3个月做过恢复演练么?没有做过,就有可能是假的!书到用时方恨少,数据要恢复时方恨没有做演练!
  双机热备(或者是RAC)+异地容灾,对于一个想当然的情况来说,是可以的。但是,在16年的Salesforce文章《Salesforce数据库故障丢失5小时数据,仅仅是个案?》中我就提过,你还是图样图森破了。你至少应该再搭建个DG,延时应用。如果你真做了就不可能丢失30%数据!
  其次,公告中说,是由于机房掉电导致的故障。说得非常合理,我们经常遇到这样的故障。犹记得,14年初,某运营商春节前夕,机房掉电,存储拉不起来,主机起不来,数据库起不来……但是,但是,但是,因为有我们,整个故障零数据丢失,连夜调配专家把系统给修好了,天亮睁开眼睛,对于广大吃瓜群众来说,是无感知的。
  绝大部分的供电突然中断,导致的数据库故障,都是相对容易能解决的。极端情况下,可能要丢一部分数据,但绝不至于30%。
那么,唯一的可能原因,就是人为误操作了!
  我在贴吧看到,这个故障的影响,貌似没那么大,大多数是说这几天打了多少关,充了几百块钱……
  炉石传说这样的数据库架构,在银行、在运营商、在保险公司,并不少见。但是,如果这样的30%数据丢失,在任何一个这样的公司里,恐怕不只是DBA、运维负责人会没有年终奖,公司总裁都得下课吧!
  而建荣猜测这个数据库是MySQL的,会从游戏架构和基本的备份恢复对问题做一些解读,仅供参考,当然反思的更多是问题所带给我们的启示。
  首先专门下载这个游戏看了下,它分为PC版,IPhone和Android版,换句话说,是端游(PC版下载需要250MB左右)和手游并存的情况。可以间接说明这款游戏还是蛮火爆的。
这里写图片描述
  对很多游戏来说,都会存在大体如下的架构模式,图片引用自《腾讯互娱架构师谈游戏服务器缓存系统怎么造》。
这里写图片描述
  上面的方式表明在游戏中存在着大量的GS(Game Server),玩家的数据是存放在GS中的,比如你去玩游戏时,登录的某个服,可能就是在某个具体的GS上,而各个GS之间的信息一般是不会共享的,而且基本上表结构信息是相同的,所以也就直接实现了分库分表的方式,这种方式采用MySQL就是一件很自然的事情。而接入模块和更多的账号,充值信息等,可能会有大平台或者是相对独立的数据库。
  所以描述中说的30%的数据,要不就是某个服的数据丢失,或者是丢失了指定时间点的数据库日志,估且按照30%来算。而且再说一句,那就是游戏里的cache其实做得已经很不错了,宕机的时间不是很长,其实对于数据库来说影响不是很大,因为玩家的数据都在缓存里,如果耽误太长时间,数据落盘的时候才会有直接影响。
  有一点需要说明,其实运维行业是很难杜绝丢数据的,不管你接受还是反对。
  我听过很多行业的运维同仁达成的一个基本观点就是丢数据是可以接受的,但是数据不能乱,数据丢了可以补啊,但是乱了,这个复杂度就会提高几个量级。因为是个虚拟的世界,这个场景相对特殊一些,所以游戏行业里有一种说法就是回档,把所有的数据恢复到一个指定的时间点,而给玩家一些补偿。这个过程会给游戏运营来说,相对也会好一些,毕竟一个爆款的游戏每个小时的流水是相当不错的,你说让长达几十个小时去恢复,换做谁都不可以接受。说句实在的,有些核心业务宕机个把小时都要被骂得狗血淋头,几十个小时,那肯定是有一些更复杂的原因。
  对于数据恢复,我听过很多Oracle数据恢复场景,数据库是直接无法Open了,无法Open就意味着完全不可用,一个基本要求就是把数据库至少拉起来,而数据恢复是在这个基础之上的事情,至于是不是零数据丢失,这个就不太肯定了。而MySQL是没有这种状态的严格标示的,所以恢复也是在这个基准之上。哪一类场景比较麻烦呢,那就是人为错误,误删数据等。这个恢复起来非常复杂,而且退一步说,哪怕数据现在修复了,你能肯定数据100%没问题?所以从运营和稳定安全的角度来说,其实出现这种故障,如果增量恢复有问题,应急策略还是更倾向于回档。
  游戏行业相对来说还是挺激进的,很多游戏都会大规模开始部署云服务(云服务器或者RDS),如果大家用过一些云厂商的RDS就会知道,连从库都不用搭建了,所以备份恢复的事情是肯定不需要自己操心的,一旦出了问题,恢复是相对来说较为容易的事情,而如果有了问题,那背锅侠也不是运营方而是云厂商了。而如果使用云服务器,除非有什么特殊的场景,否则还不如直接用RDS方便和实惠。而这里就会有一个折中,那就是性能和网络,还有安全,所以有些对于性能要求高的游戏可能会更侧重于高配的服务器,或者高配的RDS。从这个案例的描述来看,感觉不像是使用了云服务。

三、几个不是逻辑的逻辑

  这里我想到几个不是逻辑的逻辑,有时候是糊弄自己,有时候是被糊弄。做运维其实心里也苦。

  • 数据量太大,所以不做备份。数据重要吗,很重要,那为什么没备份?
  • 系统也很稳定,要备库干什么,就是个摆设,多浪费,缩减一下预算吧。
  • 服务器现在已经过保了,得换一台新机器了。那现在服务器不是好好的吗?

四、年底了,我们能做点什么呢?

即使你的数据库架构有问题,也过了年后再说吧!
但你至少得在春节前做好这几件事:

  • 对你的系统做一次深入的大排查。
  • 及时修正排查到的隐患。
  • 抓紧完成核心系统恢复演练。(非核心的估计来不及了。真出问题,你就认了吧!)
  • 封网!不必要的变更,统统禁止。

  不要存在侥幸心理,墨菲定律就在我们身边。

五、从程序员的角度谈代码健壮性

5.1 在框架层面提高运行稳定性

5.2 在细节层面的技巧提供健壮性

5.2.1 必要时多使用WaitFor函数,同时调大timeout时间

  将所有的UI操作使用消息模式取代也是不现实的。有一些情况下也是需要鼠标等操作,或者在一些需要延时等待结果的步骤中,也可能会有timeout操作。在这里要首先选择,是否可以使用阻塞模式,如果步骤为关键步骤,那就直接使用阻塞模式即可,因为即便超时未返回结果,那就说明用例执行失败了,在框架层面会给出超时错误。对于一些比如超时的,可以增大timeout时间,这样可以在某些系统资源有限的情况下,程序依旧可以执行。
####合理布局函数返回值,保证函数返回值一致
  之前很多时候写函数往往很随性,返回值类型可以能代表函数执行成功或者失败的Bool型,也会有代表实际结果的Str或者Int等类型。这样的函数在外部调用时痛苦非常,因为在函数调用后处理时,处理不当就会出现typeError,所以在函数编写前,要思考后本函数的作用,同时确定返回值类型,在函数的所有涉及到返回结果时,给予一致类型的返回值,方便外部调用。

5.2.2 必要情况下的Try…Except…处理

  Try…Except…处理异常是各种语言都有的模式。但到底在何处使用却有讲究。在没有抛异常的语句使用try语句,会降低性能,带来代码冗余,而在需要处理的语句未加异常处理,则会带来运行崩溃的可能。所以,要深刻的了解代码的语句,是否存在抛异常的可能,对可能抛异常的语言要加以处理。

5.2.3 清理代码,去掉冗余代码

  很多时候,我们的代码都是迭代开发的。往往会罗列一些无用的函数,引入一些无用的类库。这些内容貌似无意义,但却是代码中的隐患。可能在后续的类库更新或者函数变更中爆炸。所以,代码要保持清理,对于无用的引用和定义,要加以清除。
  代码健壮性,说起来简单,但真想写出健壮性的代码需要大量的实践和总结。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
基于C++的炉石传说机器人.zip ######准备写个炉石传说自动战斗AI,准备采用图像识别技术进行模拟 一些问题: 1.图像的采集 2.图像的查找比较 3.控制鼠标操作 4.识别卡牌费用 test 1 图像的采集 思路: 获取炉石传说程序的句柄 2.通过句柄获取窗口 3.获取窗口像素 查找资料并实验得到有两种方法: 方法一:获取窗口后,使用bitblt函数进行窗口函数拷贝,缺点是窗口不能有遮挡 方法二:使用printWindows 函数获取窗口图像,缺点是该函数是xp下专用,win10上对于有些窗口会无法获取图像,得到黑屏 经测试,炉石传说可以使用第二张方法,代码见test/getWindowsImage.cpp test 2 图像的查找比较 思路: 1.获取当前画面 2.读取待比较bmp 3.比较 已知方法: 方法一:逐像素,判断rgb差值,缺点慢,优点准确 代码见 test/findSubImage.cpp 方法二:采用相似图像比较分方法,比如pHash等,未测试 test 3 控制鼠标操作 思路: 1.获取目标窗口句柄 2.给目标窗口发送消息 3.获取鼠标在窗口的位置 已知方法: 方法一:使用sendMessage 或者postMessage函数向炉石程序发送消息 方法二:使用mouse_event模拟鼠标行动 经测试,炉石传说对方法一的消息不响应,即使设置窗口为SetForegroundWindow 故采用第二种方式,第二种方式要求窗口在最前面,且固定位置,因此将窗口移到左上角,固定分辨率。代码见test/controlMouse.cpp test 4 识别卡牌费用 有2种方法,代码见test/HStest.cpp: 方法一:使用图像查找比较的方法。经测试,有如下问题: 1.图片大小不好统一 2.图片背景因为有粒子的变化,变动较大 3.费用在某些条件下会更改并改变颜色 方法二:使用数字识别的方法。 有2个场景需要识别费用: 场景一:发牌换牌阶段 此阶段,背景为黑色,便于分离。流程:灰度化,阈值成二值图像,轮廓提取,测试得到卡牌的轮廓范围,近似矩形,得到卡牌数(得到先手还是后手),根据轮廓得到每个卡牌,对每个卡牌的左上角进行灰度处理,阈值化,轮廓提取,得到数字图像。使用数字识别的方法进行判断。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No Silver Bullet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值