zoukankan      html  css  js  c++  java
  • MySQL的SQL_CALC_FOUND_ROWS

    分页程序一般由两条SQL组成:

    SELECT COUNT(*) FROM ... WHERE ....
    SELECT ... FROM ... WHERE LIMIT ...

      如果使用SQL_CALC_FOUND_ROWS的话,一条SQL就可以了:

    SELECT SQL_CALC_FOUND_ROWS ... FROM ... WHERE LIMIT ...

      在得到数据后,通过FOUND_ROWS()可以得到不带LIMIT的结果数:

    SELECT FOUND_ROWS()

      看上去,似乎SQL_CALC_FOUND_ROWS应该快于COUNT(*),但实际情况并不是这样简单,请看:

      To SQL_CALC_FOUND_ROWS or not to SQL_CALC_FOUND_ROWS?

      用数据说话,证明了COUNT(*)相对SQL_CALC_FOUND_ROWS来说更快。不过我觉得这个结论也不全面,某些情况下,SQL_CALC_FOUND_ROWS更有优势,看我的实验:

      表结构如下:

    CREATE TABLE IF NOT EXISTS `foo` (
    `a` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `b` int(10) unsigned NOT NULL,
    `c` varchar(100) NOT NULL,
    PRIMARY KEY (`a`),
    KEY `bar` (`b`,`a`)
    ) ENGINE=MyISAM;

      导入一些测试数据:

    for ($i = 0; $i <10000; $i++) {
    mysql_query("INSERT INTO foo SET b=ROUND(RAND()*10), c=MD5({$i})");
    }

      先测试COUNT(*)方式:

    $start = microtime(true);
    for ($i = 0; $i < 1000; $i++) {
    mysql_query("SELECT SQL_NO_CACHE COUNT(*) FROM foo WHERE b = 1");
    mysql_query("SELECT SQL_NO_CACHE a FROM foo WHERE b = 1 LIMIT 100, 10");
    }
    $end = microtime(true);
    echo $end - $start;

      结果输出(数据大小视测试机性能而定):0.75777006149292

      再测试SQL_CALC_FOUND_ROWS方式:

    $start = microtime(true);
    for ($i = 0; $i < 1000; $i++) {
    mysql_query("SELECT SQL_NO_CACHE SQL_CALC_FOUND_ROWS a FROM foo WHERE b = 1 LIMIT 100, 10");
    mysql_query("SELECT FOUND_ROWS()");
    }
    $end = microtime(true);
    echo $end - $start;

      结果输出(数据大小视测试机性能而定):0.6681969165802

      有数据有真相,那为什么我的实验结论和MySQL Performance Blog的结论相悖呢?这是因为在MySQL Performance Blog的实验里,COUNT(*)查询是执行的的Covering Index,而SQL_CALC_FOUND_ROWS是执行的表查询;而在我的实验里,因为我定义了适当的索引,COUNT(*)和SQL_CALC_FOUND_ROWS都是执行的Covering Index,所以结论出现了差异。

      既然使用了Covering Index,就意味着不能再使用SELECT *的形式了,只能使用类似SELECT id这样的形式了,用的列在索引里都能查到,如此说来,我们需要的实际数据从哪来呢?这个很简单,有了主键之后,实际数据可以通过Key/Value形式的缓存获得,这样的架构很常见。

      结论:SQL_CALC_FOUND_ROWS如果执行的是Covering Index的话,是很快的!换个角度看,如果COUNT(*)和SQL_CALC_FOUND_ROWS都只能通过表查询来检索,那么分页时,SQL_CALC_FOUND_ROWS同样会快于COUNT(*),读者可自行测试。

    转自:http://kb.cnblogs.com/page/82986/

     MySQL之Covering Index

    在网上随便搜搜,就能找到大把的关于MySQL优化的文章,不过里面很多都不准确,说个常见的:

    SELECT a FROM ... WHERE b = ...

    一般来说,很多文章会告诫你类似这样的查询,不要在“a”字段上建立索引,而应该在“b”上建立索引。这样做确实不错,但是很多时候这并不是最佳结果。为什么这样说?让我们先来分析一下查询的处理过程:在执行查询时,系统会查询“b”索引进行定位,然后再利用此定位去表里查询需要的数据“a”。也就是说,在这个过程中存在两次查询,一次是查询索引,另一次是查询表。那有没有办法用一次查询搞定问题呢?有,就是Covering Index!所谓Covering Index,就是说不必查询表文件,单靠查询索引文件即可完成。具体到此例中就是建立一个复合索引“b, a”,当查询进行时,通过复合索引的“b”部分去定位,至于需要的数据“a”,立刻就可以在索引里得到,从而省略了表查询的过程。

    如果你想利用Covering Index,那么就要注意SELECT方式,只SElECT必要的字段,千万别SELECT *,因为我们不太可能把所有的字段一起做索引,虽然可以那样做,但那样会让索引文件过大,结果反倒会弄巧成拙。

    如何才能确认查询使用了Covering Index呢?很简单,使用explain即可!只要在Extra里出现Using index就说明使用的是Covering Index。

    知道了以上这些知识,估计对Coverging Index的了解也差不多了。再举两个例子,让大家印象深点:

    (一)比如说在文章系统里统计总数的时候,一般的查询是这样的:

    SELECT COUNT(*) FROM articles WHERE category_id = ...

    当我们在category_id建立索引后,这个查询使用的就是Covering Index。

    参考文档:COUNT(*) vs COUNT(col)

    (二)比如说在文章系统里分页显示的时候,一般的查询是这样的:

    SELECT id, title, content FROM article ORDER BY created DESC LIMIT 10000, 10;

    通常这样的查询会把索引建在created字段(其中id是主键),不过当LIMIT偏移很大时,查询效率仍然很低,改变一下查询:

    SELECT id, title, content FROM article
    INNER JOIN (
        SELECT id FROM article ORDER BY created DESC LIMIT 10000, 10
    ) AS page USING(id)

    此时,建立复合索引"created, id"就可以在子查询里利用上Covering Index,快速定位id,查询效率嗷嗷的。

    Covering Index并不是什么很难的概念,但是人们往往会忽视它的价值,希望本文能给你提个醒。

    更多:http://blog.itpub.net/758322/viewspace-680921/

  • 相关阅读:
    2018年12月29日 Oracle查询性能优化
    B
    A
    洛谷 P2447 [SDOI2010]外星千足虫
    洛谷 P5358 [SDOI2019]快速查询
    欠的题目
    ZJU-ICPC Summer 2020 Contest 8 B-Picnic
    洛谷 P3164 [CQOI2014]和谐矩阵
    K
    J
  • 原文地址:https://www.cnblogs.com/youxin/p/3850666.html
Copyright © 2011-2022 走看看