网站性能总结

网站有多慢

网站一直比美丽说蘑菇街等同类型的网站慢一些,但到底有多慢,下面是一个对比:

http://www.duitang.com/topics/				42297 bytes


Requests per second:    0.49 [#/sec] (mean)

http://www.mogujie.com/shopping/                        2390 bytes


Requests per second:    2.87 [#/sec] (mean)

http://guang.com/xihuan                                 945170 bytes


Requests per second:    1.86 [#/sec] (mean)

可以看到逛的qps是我们的3倍多,所以这次优化的目标主要是提升搜索页面的QPS.

ps:为什么是从topics下手,因为topics是除了首页之外访问量最大的页面。

瓶颈在哪里

用户访问topics,网站通过ajax请求url:http://7599.t.duitang.com/hot/masn/?page=5&page_size=24&_type=&begin_time=1342876671
, 会做以下几件事情:

1. 从搜索引擎获(solr)取查询结果。(平均5ms,偶尔有20ms+的查询)
2. 搜索引擎返回的是id,通过id查询相应的message,并cache。
3. 执行cf.jsonResultMblog(),次方法主要是循环每个message,组装相应信息返回给页面。

经过测试发现 cf.jsonResultMblog()需要耗时900ms,这对一个方法来说太慢了。

cf.jsonResultMblog()做了什么

下面是cf.jsonResultMblog()的代码:

    for

 item in blogs:
        if

 item:
            if

 get_latest:
                item = item.get_latest_forward()
            if

 not item:
                continue


            root = item.get_root()
            common = root and root.is_buyable_common() or False
            good = root and root.is_buyable_good() or False
            buylnk = root and root.is_buyable() and root.get_source()
            buylnk = buylnk and buylnk.link or ""
            usr = item.sender
            album = item.get_album()
            blog = {
                "id"

:item.id,
                "common"

:common,
                "good"

:good,
                "msg"

:mbtrimlinks(item.msg),
                "isrc"

:item.middle_photo_path2(),
                "iht"

:item.photo and item.photo.middle_height() or 0,
                "buylnk"

: buylnk,
                "uid"

:usr.id,
                "unm"

:usr.username,
                "ava"

:usr.get_profile().tinyAvatar(),
                "albid"

:album and album.id or '',
                "albnm"

:album and album.name or u'默认专辑',
                "favc"

:root and root.favorite_count or 0,
                "repc"

:item.reply_count,
                "zanc"

:item.like_count,
                "sta"

:item.status,
                "cmts"

:[]
            }
            if

 root and root.sender:
                rusr = root.sender
                blog["rid"

] = root.id,
                blog["ruid"

] = rusr.id
                blog["runm"

] = rusr.username
                blog["rava"

] = rusr.get_profile().tinyAvatar()

            comms = item.get_top_comments()
            for

 comm in comms:
                if

 comm:
                    blog["cmts"

].append({
                        "id"

:comm.sender.id,
                        "ava"

:comm.sender.get_profile().tinyAvatar(),
                        "name"

:comm.sender.username,
                        "cont"

:comm.content,
                    })
            ajblogs.append(blog)

我整理了一下,for循环有15个查询:

  • item.get_latest_forward()
  • item.get_root()
  • root.get_source()
  • item.get_album()
  • item.photo.middle_height()
  • usr.get_profile().tinyAvatar() (获取用户头像,此方法走2个查询,每次5ms)-
  • root.sender
  • rusr.get_profile().tinyAvatar()
  • item.get_top_comments()
  • comm.sender
  • comm.sender.get_profile().tinyAvatar()
  • comm.sender.username

其中有一些方法走了cache, 但还是有一些方法走的是数据库查询,数据库查询每次耗时都在几毫秒,循环24次,总耗时就是上百毫秒。
我这次优化主要就是把数据库查询改成cache,如果不能走cache,就通过select in子查询先一次性查询出结果再处理。

一些总结

1. item.sender.id 比 item.sender_id 慢一个数量级,前者会触发一次查询。

2. django的对象关联非常方便,获取一个物品发布人的信息只需要 item.sender.profile,但这样写性能很差,这些查询都是通过数据库查询,而没有走cache。建议不要直接用django的对象关联,通过提供方法来做:

    def get_sender(self):       
        key = cf.generate_cache_key(self.sender_id, User)
        model = key and cache.get(key)
        if

 not model:
            model =  self.sender
            cache.set(key, model, 60*60*24*3)
        return

 model

3.不要在for循环做耗时的数据库查询,累加效应之后性能非常差。

4.什么对象应该走cache? 我总结的就是被依赖的对象越多越应该cache起来,比如Auth_User,UserProfile,UploadFile应该被cache,而 Message对象没有被任何对象依赖,生命周期比较段,被cache起来命中率也不高。这点比较像jvm的GC里面的old区。

猜你喜欢

转载自san-yun.iteye.com/blog/1739392
今日推荐