鉴于本号的阅读群众特别喜欢看掉坑和挖坑的事,今天就披露一个高草职业生涯中最惨的一次发布上线经历。
现实中熟悉高草的朋友都知道,想坑高草那几乎是没可能的事,不被反手挖个坑还顺手帮埋就不错了。经常有人问,你是怎么看出来这事有坑而且是大坑的?
伟大的毛主席曾经曰过:世上本无路,趟着走的人多了,就把坑填平了,自然就有路了......
话说05-06年前后,高草辞掉了在一家创业小公司的工作,带着对开发和架构工作无比痛恨的情绪,申请了白盒测试工程师的职位,结果被龙哥一脚踢进了架构设计组(这又是另外一个有趣的故事了,暂时掠过)。于是成功地混进了刚刚组建的QQMail团队。虽然报道的时候大概已经有20-40人这个规模,但是这个架构组只有2个人,而且组长还来没报道。
于是在这个架构组混了一段时间,接到了一个大任务:原来的账号服务扛不住了,需要重构账号的后台服务体系。
高草当时拿到的设计要求只有1条,每秒要保证混合读写4000次,争取能用到年底。除了开发时间上来说很紧,4000/s混合读写这个看起来有点难度,其实这个倒是真的不难。毕竟,老的架构是用直连MySQL做的,咱对MySQL的熟悉程度不能说特别NB,但是一般人想跟我拼DB上的坑,尤其是MySQL的坑,那赢得可能性并不大。毕竟咱也是9x年就拿过18摸(IBM)DB2培训证书得人,虽然从来都不好意思亮出来。
老的架构无非是分服务器、分表。原来分表分的少,鉴于时间紧迫,那就先上一个10-100倍分表再加服务器的架构顶上去,原来是直连DB(这个是大忌暂时也不表),加一个中间服务就搞定了嘛。
设计目标4000次每秒混合读写,直接透传给MySQL读写SCSI磁盘的方式肯定扛不住,那就DB前面再加一个cache呗。cache也简化一下,用内存临时表抗住。内部设计讲完要利用MySQL内存表这个办法,倒是让运维同学眼前一亮,找到了新的方向:不久后就直接过渡到了采用讲MySQL的db文件部署到内存虚拟的磁盘上这条光明大道。
虽然不知道为什么,直觉上就觉得读写4000次的指标定的有点低,不保险,改成8000-10000次设计目标,给1倍冗余性能。
于是开工、自己测试、拉运维测试一路走通。自己又写了一系列测试脚本,压测到1w次每秒毫无压力,心情愉悦。
于是,顺利走到半夜变更线上服务这个环节,为了保险起见,高草跟运维F同学都一起熬夜变更服务。停服务、上新服务、暂停请求、挪数据、重开请求一条龙都很顺利,然后大家一起等着天亮有服务请求上来再继续观察几个小时就没事了。期间一旦发现问题,就要立即分析修改。
一共动用了3个人的庞大人力熬夜变更(也许记错,反正不会超过5人)。
目标清晰,步骤明确,1点过后开始有少量服务调用请求,3点前都很正常,随着接近天亮时间,请求越来越多,就开始有点不对了,注册环节开始出现大量失败,这个就奇怪了。
于是,立刻开始在线分析(修改一系列前后台服务,加日志)。HTML正常,CGI正常,账号服务模块正常,DB出现了大量锁DB锁表现象。大家都一脑门纳闷,不应该啊,按照现在的拆分表,除非这一波注册的账号名字和分配的内部ID都集中到了某一个表,否则没可能出现!于是,继续线上分析注册用户名什么的,一切也都正常,就是找不到锁表问题。
陆续排出了恶意批量注册、CGI服务出错等一系列地方后,天可就要大亮了,这要是在上午开工前还没解决,那就绝对是大事故了,所有人都急得不行。
要知道,这时候就算直接回退回到老的服务那也是扛不住的,果断都在集中精力分析线上请求。一顿操作猛如虎,终于找到了头绪,出现了大量的同一时刻相同的写操作和读操作请求,写操作的SQL语句都一模一样,读操作还就是刚刚写的那个字段!而且,所有被调用的表都是如此!
继续往上找,后台服务请求正常,CGI正常,HTML正常,一路追到页面的JS......
所有人集体吐血了,这位伟大的JS开发同学,在一个注册动作请求内,同一个时间(ms级别)发送了2个一模一样的写调用和一个读调用。通俗点说,比如有人注册了一个叫AAAAAA的账号,那最后端的DB要保证先生成并写入AAAAAA的所有新纪录,然后才能返回一个select请求读出这条AAAAAA账号的所有记录数据,在写操作完成前,因为是新的记录,所以读操作是100%会被挂起的。而且正常情况下,这种一个写一个读先后到来,是不会出问题的。死就死在同时来了2条一模一样的写操作(眼明的同学一定会发现,这条读操作是不必要的,但保留问题也不会太大),写-写-读这个调用次序到了DB层面其实无法保证的,那100%会造成至少2次锁表,这就相当于一个调用被扩大3倍,而我的冗余能力只有2倍......
找到问题那就立刻紧急修改,大家一一问候了这段JS代码的先祖安康福寿无边,终于在上班高峰到来前彻底解决,线上服务稳定下来了,一身冷汗,真凉快~
仔细回想一下,出问题是必然的,因为所有CGI部分、老的服务部分代码我都一一看过,开工前唯一没仔细看过的地方就是HTML和JS这里!
这个故事告诉我们,无论何时,后台开发同学永远都不要忽视调用链上下的任何一个环节,尤其是前端,特别是JS。
从这件事以后,高草就养成了一个习惯,任何项目,都要把原始需求、定型需求、架构设计、线上服务调用逻辑分布和开发人员分配的每一个环节都弄清楚,有一个地方弄不清楚绝不动手。
时间久了,即使后来不做开发了,仅在需求安全性分析时都有了回报,比如戳穿新来的开发的小心思:别耍小聪明说这个难那个也难,一个屁大点地方就要开发1周,这玩意难在哪里,讲出来,咱们现在就分析解决,老子见过的坑,比你丫开发过的系统还要多~
哼~