什么不要做?关于失败和优化

标签: 优化 | 发表时间:2015-04-12 08:00 | 作者:
出处:http://ericliang.info

上周面试一个人,聊到了服务性能优化。

  他:线上的推送服务使用的单机单线程,性能撑不住。于是就改成了多机,还加了线程池。
问:撑不住是什么表现?什么原因?
他:因为是短信验证码服务,短信通道堵塞,所有请求都堵在vm里,引发了OOM。
问:看起来缓存使用内存不够,跟线程池有什么关系?
他:呃。。。正好当时在试用一个线程池库,就用了。

当时的对话没这么简单,因为并不是所有人都能说明遇到的问题,更不是所有人都能坦诚面对自己的折腾,况且我还要考虑提问的方式和方法。但情况大抵如此,在服务研发中最不愿看到的场景,恰恰是最经常发生的。

  A:线上服务有bug,我hotfix一下。唔,正好还优化了xxx,一起上线了。
B:这次增加了一个新功能,顺手也把vm参数改了,上去看看效果。
C:感觉请求处理的地方有点问题,我改了下,应该会起作用。
。。。

每次听到“正好”“顺手”“感觉”这样的词,我都得浑身一个激灵。天上要掉馅饼了?

真实情况往往相反,天上掉下来的是石头。这说起来也是不幸,要不墨菲不会那么出名。

要控制问题域

Person of interest

其实互联网服务做久了,线上问题处理早已变成了家常便饭。因为用户不能忍受宕机,所以你有问题查起来基本是不眠不休。个中滋味,只能自己慢慢体味。

这问题有时候是新问题,你刚改的代码有个bug,有时候是老问题,但是是暴露了老代码里的bug。多数情况下,都可以通过回滚代码来解决。个别情况下,你需要增加一个fix版本重新上线。这些都简单,难的地方在于定位问题。

定位问题最怕问题域太大。因为你得一步步分析,将可能发生问题的范围缩小。从现象出发分析,从改动入手检查算是一个捷径。如果改动太大,无疑会增加思考的成本和定位的难度。

而紧跟着的问题就是,如果作为一个fix版本上线,是否可以带入其他改动。我的回答是不行,因为也有太多次的失败来源于这个hotfix。如果再次出了问题,你需要首先判断是不是老问题没被解决还是引入了的新问题,这又涉及到整个请求处理链的重头分析。

我们不怕优化失败,但是怕优化出来的失败。优化失败最多还是老样子,但优化出的失败很可能是一次雪崩。

有些优化不要做

回到最开始的那个例子,他的问题表现出来是盲目引入新库,做法与我们常规的处理方式相背。根源则在于对问题的分析不足,不清楚问题的原因,当然可怕的地方在于(也是不可避免的),还使用了试试看的方式来修复问题。

应该做哪些优化呢,简单来说就一句话,做该做的优化。谈这个问题很多人都有经验,就算刚才说的“试试看”,在某种程度上也算。不过我今天想说的是不应该做的,一些经常被忽视的事情。

  1. 不要用不需要用的东西

    第一个事是不要用不该用的东西。一般来讲,互联网服务的峰值会很明显,峰值压力会是平时的十倍甚至百倍。应对请求洪峰,简单的方法就是用队列削峰。但是很多人用惯了队列后,只要设计服务,都会使用队列分离。但队列一旦进来,不仅会增加服务的复杂度,而且割裂了前后端之间的联系。

    由于已经跟前端解耦,很多时候在处理请求的时候,前端已经返回了。这时候发生错误的话,已经没办法通知用户,导致服务只能偷偷失败。这是很容易气坏用户的处理方式。

  2. 可以失败不要犹豫

    另一个事是可以失败不要犹豫。很多时候服务都是分层的,外层服务访问内层服务,需要应对各种失败,有环境问题像网络闪断机器故障等,有服务内部故障像空指针虚拟机崩溃等。那么问题来了,内层服务失败了怎么办?举例来说,如果用户注册时数据库出现问题,该返回错误还是重试?如果重试次数设多少?如果是超时怎么办?超时要设多久?

    除去数据问题,这里面经常被人忽视的却是性能问题。假设正常情况下10ms返回的请求,超时是1s,那么遇到超时的时候,请求处理速度其实下降了100倍,而如果还有重试,那就会继续降低。性能下降的直接结果是导致后续请求要么堆积要么丢弃。堆积会占内存,很多情况下会引发服务本身OOM退出,让服务质量雪上加霜。

    其实开始的时候直接失败,也就没有后续这么多的失败了。或者超时短一些,性能也不至于下降太多。看起来设计很完善的错误处理策略,很可能不过是往背篓里加了无用的石头而已。

  3. 不要修不该修的东西

    还有个事就是不要修不该修的东西。Wikipedia里一直有句提醒,“If it ain’t broke, don’t fix it”,但是没说原因。这里说两个例子。

    一个是虚拟机调优,不管是Java还是Erlang都会涉及。Java里多的是GC参数,Erlang里一般都搞调度器,情况差不多。如果哪一天你翻看参数说明,发现某个GC算法看起来更好或者新生代比例好像不太合适,或者发现CPU利用率一直不高而调度器线程池貌似可以调大,千万不要手一抖改了线上服务。因为线上服务实际运行情况复杂,很可能因为你的调整效果反而下降。GC调完引来了虚拟机OOM,调度器调完了请求超时不断。

    另一个是数据一致性问题。举一个最近的例子。Mnesia是Erlang自带的数据库,速度非常快,跟OTP集成度高,但它在事务处理的时候会出现死等情况。由于两个节点通讯是请求应答模式,请求节点在发出请求后等待,等待的过程会定时检查对端节点的存活情况。问题出在这个定时检查上,它假定对端只要在线就肯定会回应。但如果对端节点在这个检查中间死而复活,那么它即使在检查的时候活着,也不会再回应了,请求节点就因此一直等下去。同时由于所有的消息都是在一个事务管理进城内,后续的所有请求都无法处理了。要不了多久,这个节点就会因此挂掉。

    解决这个死等问题,一个想当然的方案是增加节点启停时间记录,检查的时候多检查一些东西。这也是我们开始时候想的方案。但是然后呢,这个请求该怎么处理?想想刚才讲的失败策略,该丢还是该继续呢?如果丢了,数据一致性怎么保证?如果你的场景像我们一样不希望丢,那么你很可能把原来已经可以常规处理的问题,变成了一个新问题。我们暂时是没有处理这个问题,还因为暂时还没想好要维护一份自己的Mnesia代码。

这三件事只是典型情况,还有很多情况,需要在做之前全面思考。毕竟成果是以产出来衡量,而不是付出,要看你解决了多少问题,而不是做了多少事情。

很多时候,做得少就是做得多。

Behavioral Economics: Why More Is Not Better https://stevensonfinancialmarketing.wordpress.com/2012/10/30/behavioral-economics-why-more-is-not-better/

给小团队的特别建议

小团队的普遍现象在于人力紧张,不管是在创业公司还是在大公司内。对于不写代码就手痒的技术人员,如果再在技术上有点儿完美主义情节,那真是可以为代码鞠躬尽瘁的。稍微一整理,事情恨不得已经排到一年后。但是大公司有绩效压力,小公司有生存压力,怎么办?这个事儿就是优先级处理,处理不好,就会变成目标管理,然后慢慢你就跟真的工地搬砖无二,天天还得受工头的鞭子。

如果你像我一样喜欢轻松的自组织团队,喜欢以一当十的挑战,那你更需要注意这点。如果你只是埋头做一件事,很可能错过了系统改进的时间窗口,等系统压力上来或者问题到来的时候措手不及。你只能过四处救火火不断,缝缝补补又一年的生活。而所有的补丁,最后又变成了你彻底修复时要处理的历史包袱。

你最需要的可能是坐下来,想想该做什么,想想先做什么,把有限的精力用在最值得用的地方。

有些时候,做得对才可能做得少。

相关 [优化] 推荐:

存储优化

- - CSDN博客推荐文章
定期对存储设备的固件和驱动程序做升级. 选择合适的磁盘阵列,RAID可以让很多磁盘驱动器同时传输数据,而这些磁盘驱动器在逻辑上又是一个磁盘驱动器,所以使用RAID可以达到单个磁盘驱动器几倍、几十倍甚至上百倍的速率,还能提供容错,冗余的功能,最常用的有raid10和raid5.. 使用主动多路径(Active Multipathing)技术.

mysql优化

- - 数据库 - ITeye博客
公司网站访问量越来越大,MySQL自然成为瓶颈,因此最近我一直在研究 MySQL  的优化,第一步自然想到的是 MySQL 系统参数的优化,作为一个访问量很大的网站(日20万人次以上)的数据库系统,不可能指望 MySQL  默认的系统参数能够让 MySQL运行得非常顺畅. 在Apache, PHP,  MySQL的体系架构中,MySQL对于性能的影响最大,也是关键的核心部分.

sql优化

- - 数据库 - ITeye博客
是对数据库(数据)进行操作的惟一途径;. 消耗了70%~90%的数据库资源;独立于程序设计逻辑,相对于对程序源代码的优化,对SQL语句的优化在时间成本和风险上的代价都很低;. 可以有不同的写法;易学,难精通. 固定的SQL书写习惯,相同的查询尽量保持相同,存储过程的效率较高. 应该编写与其格式一致的语句,包括字母的大小写、标点符号、换行的位置等都要一致.

ORACLE:plsql优化

- - CSDN博客数据库推荐文章
 1、登录后默认自动选中My Objects. 设置方法:Tools菜单--Brower Filters会打开Brower Filters的定单窗口,把“My Objects”设为默认即可. 同理,可以在Tools菜单--Brower Filters中把你经常点的几个目录(比如:tables Views Seq Functions Procedures)移得靠上一点,并加上颜色区分,这样你的平均寻表时间会大大缩短,试试看.

hive 优化 tips

- - CSDN博客推荐文章
一、     Hive join优化. 也可以显示声明进行map join:特别适用于小表join大表的时候,SELECT /*+ MAPJOIN(b) */ a.key, a.value FROM a join b on a.key = b.key. 2.     注意带表分区的join, 如:.

WebLogic 优化

- - 开源软件 - ITeye博客
WebLogic 配置文件(config.xml)包含了大量很直观的与性能有关的参数,能通过配置环境与应用程序得到很好的优化. 基于系统的需要调整这些参数不仅能改善单个点的性能,而且能提高整个应用程序性能的可衡量性. 试着采用下列WebLogic配置方法,或许能使你的系统达到最佳状态:. 一 修改运行队列线程数的值.

DOM优化

- - JavaScript - Web前端 - ITeye博客
1,Javascript语音与DOM操作就像孤岛. 他们之间的通行是要借助船的,而使用船是有很大成本的. 所以,要先做完一个再做另一个,最好不要交替进行. 如:添加1000个li时,先用一个字符串拼接好,最后一次追加到父节点,而不是向父节点追加1000次,一次追加一个. 如:添加1000个li时,先放到fragment,再添加到UL中.

底价优化

- - Koala++'s blog
RTB中引入了修改后的Second-Price竞价模型,竞价成功的广告不需要向Ad-Exchange付他的出价,而只会付第二名的出价.          Second-Price的理论思想是这样的:假设我在卖莫奈的油画. 有许多人想竞得这幅油画,每个人心里都有一个最高出价,但问题是他不想付比能竞得这幅画的出价多一分钱.

MapReduce优化

- - 行业应用 - ITeye博客
相信每个程序员在 编程时都会问自己两个问题“我如何完成这个任务”,以及“怎么能让程序运行得更快”. 同样,MapReduce计算模型的多次优化也是为了更好地解答这两个问题. MapReduce计算模型的优化涉及了方方面面的内容,但是主要集中在两个方面:一是计算性能方面的优化;二是I/O操作方面的优化.

hibernate优化

- - 开源软件 - ITeye博客
原文 http://developer.51cto.com/art/200906/129539.htm. 文章分为十三个小块儿对Hibernate性能优化技巧进行总结性分析,分析如下:. 一、在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会严重显示性能,所以在使用Hibernate处理大数据量的,可以使用session.