Loading...

MySql查询耗时的问题

2008-09-27 22:00:51 发表于PHP, 网站技术 本文链接: MySql查询耗时的问题

感谢bluehost的CPU限制,让我对SQL查询的效率进行无限的追求。这两天CPU老是超标,我仔细查看了日志,我的这个bloggermap也他的一份“功劳”,特别是RSS阅读那一块,一个查询就占据了6秒,再加上其他几个合租伙伴的“功劳”(已经给他们发送邮件),很容易就超过30s了,所以今天晚上回来继续优化。

首先我们来看看这段关于MySQL查询的分析

# Query_time: 4 Lock_time: 0 Rows_sent: 10 Rows_examined: 74584
use sevtiger_bloggermap;
SELECT sample_blogs.id blogid,
sample_blogs.url blogurl,
sample_blogs.feedurl feedurl,
sample_blogs.name blogname,
sample_article.id id,
sample_article.title title,
sample_article.content content,
sample_article.description description,
sample_article.link link,
sample_article.author author,
sample_article.timestamp timestamp
FROM sample_article LEFT JOIN sample_blogs ON sample_blogs.id = sample_article.blogid WHERE 1 ORDER BY sample_article.timestamp DESC LIMIT 17800,10

整个查询耗时4秒,查询涉及到的行数有74584,实际输出10行,我们可以看到,整个查询的效率并不高。虽然有LIMIT begin,per来限制查询的数量,但是行数依然没有减少,而LEFT JOIN更是增加了检索的数量,我想如果是LIMIT 17800,10,那么其实最多只需要查询17810或者两倍就可以了,LEFT JOIN将另外的一张表连接起来,导致的结果是查询影响的行数陡增至原来的4倍。

虽然LEFT JOIN查询省事,看来为了CPU时间的占用,不得不忍痛重写SQL。其实上面的那段SQL查询的目的就是从 article表中取出文章内容,取出的同时根据article中的blogid再从blogs表中取出blog的相关信息,具体效果请看这个页面。这样就能在显示BLOG RSS内容的同时显示对应RSS的blog名字和链接等信息。

我改写的思路是这样的:

  1. 定义一个函数A,函数的作用是根据blogid或者blog相关的信息,返回一个查询的object,可以根据object->name这样的形式获取blog信息;
  2. 获取RSS的时候不再连接blogs这张表,直接循环输出数据,在需要获取BLOG信息的时候时候引用函数A( 函数A同时被缓存,毕竟blogs信息改变的几率不大,所以这样在第二次查询的时候,A函数就不会再连接数据库了 ),从而提高了数据库的查询速度;

当然。上面由于在查询RSS的时候引用了函数,所以在函数没有被缓存之前其实是进行了嵌套SQL查询,这样的效率并不高,但是缓存之后的速度是相当的快的,现在已经改造超过2个小时了,再去查询sql slow queries,没有看到比较离谱的耗时查询了。

上面的是本人在bloggermap.org改版中遇到的实际问题,有些地方可能说得不准确,高手请指正 :)

标签:,
发表于 2008-09-27 22:00:51 目录:PHP, 网站技术 [RSS 2.0] 你可以发表评论, 或者从您的网站 trackback
feed url
feed url
下一篇: 爆强的flash广告 »
已经有9位大师动手指导 拒绝低俗
  • 1楼 muzik 在2008.09.27 23:10发表评论如下: 回复

    重点还是条数太多 你不妨试一下子查询,看速度如何
    SELECT sb.id blogid,sb.url blogurl,sb.feedurl feedurl,sb.name blogname,sa.*
    FROM sample_article sa
    LEFT JOIN sample_blogs sb ON sb.id = sa.blogid
    WHERE timestamp <= (
    SELECT timestamp FROM sample_article ORDER BY timestamp DESC LIMIT 17800 , 1
    ) LIMIT 10

    • 2楼 muzik 在2008.09.27 23:50发表评论如下: 回复

      关于这个事情,我的理解是这样的:
      好比你有50000条数据,limit 40000,10将会动用到所有的行
      而select *排序和select单行排序的效率是有显著差别的,至少差着十几倍罢——随口说说,没可靠依据
      所以先用子查询select一行,然后where会排除掉前40000行,接下来select *只动用剩余的10000行排序

      • 2楼附属品 江东 在2008.09.28 08:38发表评论如下: 回复

        很有道理!有时间测试一下

        • 2楼附属品 江东 在2008.09.28 09:18发表评论如下: 回复

          还有,我还停留在MySql不支持子查询的阶段

        • 3楼 Jiang 在2008.09.28 06:02发表评论如下: 回复

          性能问题可能主要是出在ORDER BY排序这一块儿。muzik的方法应该是可行的(虽然select了两次),因为排序用到的filesort算法会把query的columns (避免读取2次)以及sort key和row pointer都读进sort buffer中,这样sort buffer就会很容易满,一旦sort buffer满了,就需要进行一次quicksort,写入临时文件,从而触发更多的I/O,造成性能下降,因此可以按照muzik的方法,先select单行sort key,避免query的columns被读进sort buffer。另外可以尝试增大sort_buffer_size以及read_rnd_buffer_size系统变量,可以减少I/O触发。另外还可以给timestamp建立索引,这样可以避免排序。

          • 3楼附属品 江东 在2008.09.28 08:39发表评论如下: 回复

            所谓抛砖引玉便是如此,再测试一下,可惜直接分析sql都是毫秒级的,看不出什么差距,但是BH的sql log居然是秒级的,疑惑

            • 3楼附属品 avenger 在2008.10.06 13:39发表评论如下: 回复

              mysql 本身有 sql query cache,同样的 SQL 再次查询速度会快很多,可以在 SQL 中添加空格来看实际的效果。

              • 3楼附属品 江东 在2008.10.06 13:43发表评论如下: 回复

                是的,同样的查询第二次耗时基本是第一次的千分之一了

          • 4楼 muzik 在2008.09.28 15:22发表评论如下: 回复

            测试的时候可以重复取几万次….

            • 转到第
            (Required)
            (Required, not published)
            如果留言未显示无需重复留言,我将为你恢复!