• 2008-08-09

    《秒杀十分钟》之后

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/27478361.html

    zdonking在《秒杀十分钟》中留言:
    只是把一个10分钟的运算变成瞬间,就能解决“发布一次要5个小时”的问题吗? 难道这个10分钟的方法 在发布过程中被反复调用?

    好吧!我承认,《秒杀十分钟》只是故事的开始,那只是我当天所做的工作,所以,这个故事还有后续。

    做程序的人都知道,优化一个循环中的内容,往往比只调用一次的操作来得更加有效,因为循环里面的东西会执行很多次,看似一小点的浪费,累计起来也是很可怕的。《秒杀十分钟》所做的优化其实只是一个只调用一次的操作,对它的优化并不会让整个发布操作提高很多,但是如果不优化这部分的话,那么每次到循环之前,我们都要等待非常漫长的一段时间。当然,如果喜欢偷懒,置之不理也许是一个非常不错的选择。

    在克服了前面一个漫长的过程之后,我们站在了循环的面前。这个循环里面是一个重算分的操作,因为每次发布都会带来一些改变,所以,需要重算分来保证正确性。

    一如既往,优化之前,首先要找到一些慢的地方。所以,在代码加了日志,记录不同部分的调用时间。测试依然用的是产品数据,这样做的好处是,能够尽可能贴近实际情况,而且因为数据量够大,运行可以消耗很长时间,这时候,你可以选择做一些其他的,别人一旦问起,大可以骄傲的说,跑测试呢!这种行为俗称偷懒。

    当我们吃完午饭回来的时候,和我Pair的同事决定杀死这个进程,真的要把5个小时的程序运行完,我们今天真的可以休息了,更重要的是,这时在日志里已经有足够我们进行下一步分析的结果了。我们在巨大的日志中搜索,大多数算分的操作都是在可以接受的范围内,不过,我们还是找到了一些扎眼的数据,我们把一个超过35秒的算分过程单独拿了出来,它也许可以帮助我们发现问题所在。

    这个算分的方法里面实际上包括了两个部分,一部分是真正的算分,另一部分是算分之后,要保存历史信息。把这个两个部分分离开来的话,算分部分大约是25秒,保存历史部分大约是10秒。我的主要目标瞄准了算分部分。

    这个操作大概的逻辑是这样的,有6类的分数,我们需要分别为这6个类别计算得分。我们进一步加了些日志,分别为这6类统计时间,测试结果告诉我们,我发现有一个计算失分点的操作很耗时,而且这个操作是每个类别都需要的。进一步打开这个计算失分点的操作,我突然发现,这个里面计算出现了“类别”。我们每个失分点计算的操作都会计算所有类别的失分点,然后,每个类别都根据自己的类别取到失分点。这意味着什么?6个类别就把失分点计算了6次,而每一次的计算都是计算所有的类别的失分点,换句话说,本来应该一次完成的操作就多了5次,而且这还是一个耗时的操作。好在rails本身还为我们做了一些缓存,否则,这个操作的耗时会更多。找到问题之后,解决起来就容易了,我们把这个操作提取出来,对于每次算分,失分点只计算一次就可以了。再次运行我们的程序,算分部分的时间降到了10秒左右。

    在测试过程中,我和Pair的同事一直在观察日志,我们发现了一个奇怪的问题,计算一个企业得分的过程中,会不断有查询其它企业的信息。在整体运行的时候,这个操作就显得尤为扎眼。很快,我们定位到出现这个查询的代码。经过分析,我们发现,这是由于一个关联查询带来的结果。正如我们这里分析的,其实,我们根本不需要这些信息。经过对这个方法的分析,我们发现其实,这里也是一个SQL查询就可以搞定,换句话说,我们不知不觉中,我们又多出许多数据库查询来。经过努力,我们把这段方法变成了一句SQL,再次运行那个曾经复杂的操作,算分部分降到7秒以下。其实这个方法是系统中一个比较基本的方法,对它的修改不仅仅会对我们这里的优化有意义,对其他部分也是有好处的。

    把一个25秒的操作优化到7秒,这就是我第二天优化工作的结果。虽然不能说循环中的类似部分都有相同的提高,但一定量的提高肯定是有的。

    测试显示,这部分已经不再那么漫长,但整个发布过程依然很漫长,所以,我们可走的路还很多。算分时间主要是
        需要算分的企业 * (真正算分的时间 + 保存历史的时间)

    大致看了一下代码,保存历史部分可优化的空间可能不会太大,所以,我们下一个目标瞄准了需要算分的企业。

    分享到:

    历史上的今天:

    旁观茶餐会 2010-08-09
    引用地址:

    评论

  • 恭喜啊,能现场看奥运了。看的是哪场比赛啊?
  • 玩企业应用的,只要精通数据库和SQL就足够了,试图用java或者其他语言操作去代替SQL操作是很setup的。