Mysql中分页查询的两个解决方法比较


Posted in PHP onMay 02, 2013

mysql中分页查询有两种方式, 一种是使用COUNT(*)的方式,具体代码如下

SELECT COUNT(*) FROM foo WHERE b = 1; 
SELECT a FROM foo WHERE b = 1 LIMIT 100,10;

另外一种是使用SQL_CALC_FOUND_ROWS
SELECT SQL_CALC_FOUND_ROWS a FROM foo WHERE b = 1 LIMIT 100, 10; 
SELECT FOUND_ROWS();

第二种方式调用SQL_CALC_FOUND_ROWS之后会将WHERE语句查询的行数放在FOUND_ROWS()之中,第二次只需要查询FOUND_ROWS()就可以查出有多少行了。

讨论这两种方法的优缺点:
首先原子性讲,第二种肯定比第一种好。第二种能保证查询语句的原子性,第一种当两个请求之间有额外的操作修改了表的时候,结果就自然是不准确的了。而第二种则不会。但是非常可惜,一般页面需要进行分页显示的时候,往往并不要求分页的结果非常准确。即分页返回的total总数大1或者小1都是无所谓的。所以其实原子性不是我们分页关注的重点。

下面看效率。这个非常重要,分页操作在每个网站上的使用都是非常大的,查询量自然也很大。由于无论哪种,分页操作必然会有两次sql查询,于是就有很多很多关于两种查询性能的比较:

SQL_CALC_FOUND_ROWS真的很慢么?

http://hi.baidu.com/thinkinginlamp/item/b122fdaea5ba23f614329b14

To SQL_CALC_FOUND_ROWS or not to SQL_CALC_FOUND_ROWS?

http://www.mysqlperformanceblog.com/2007/08/28/to-sql_calc_found_rows-or-not-to-sql_calc_found_rows/

老王这篇文章里面有提到一个covering index的概念,简单来说就是怎样才能只让查询根据索引返回结果,而不进行表查询

具体看他的另外一篇文章:

MySQL之Covering Index

http://hi.baidu.com/thinkinginlamp/item/1b9aaf09014acce0f45ba6d3

实验
结合这几篇文章,做的实验:

表:

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;

注意下这里是使用b,a做了一个索引,所以查询select * 的时候是不会用到covering index的,select a才会使用到covering index
<?php $host = '192.168.100.166'; 
$dbName = 'test'; 
$user = 'root'; 
$password = ''; 
$db = mysql_connect($host, $user, $password) or die('DB connect failed'); 
mysql_select_db($dbName, $db); 
  
echo '==========================================' . "\r\n"; 
$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 . "\r\n"; 
echo '==========================================' . "\r\n"; 
$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 . "\r\n"; 
echo '==========================================' . "\r\n"; 
$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 * FROM foo WHERE b = 1 LIMIT 100,10"); 
} 
$end = microtime(true); 
echo $end - $start . "\r\n"; 
echo '==========================================' . "\r\n"; 
$start = microtime(true); 
for ($i =0; $i<1000; $i++) { 
    mysql_query("SELECT SQL_NO_CACHE SQL_CALC_FOUND_ROWS * FROM foo WHERE b = 1 LIMIT 100, 10"); 
    mysql_query("SELECT FOUND_ROWS()"); 
} 
$end = microtime(true); 
echo $end - $start . "\r\n";

返回的结果:
Mysql中分页查询的两个解决方法比较
和老王里面文章说的是一样的。第四次查询SQL_CALC_FOUND_ROWS由于不仅是没有使用到covering index,也需要进行全表查询,而第三次查询COUNT(*),且select * 有使用到index,并没进行全表查询,所以有这么大的差别。

总结
PS: 另外提醒下,这里是使用MyISAM会出现三和四的查询差别这么大,但是如果是使用InnoDB的话,就不会有这么大差别了。

所以我得出的结论是如果数据库是InnoDB的话,我还是倾向于使用SQL_CALC_FOUND_ROWS

结论:SQL_CALC_FOUND_ROWS和COUNT(*)的性能在都使用covering index的情况下前者高,在没使用covering index情况下后者性能高。所以使用的时候要注意这个。

PHP 相关文章推荐
刚才在简化php的库,结果发现很多东西
Dec 31 PHP
php调用mysql存储过程
Feb 14 PHP
PHP的SQL注入过程分析
Jan 06 PHP
通过缓存数据库结果提高PHP性能的原理介绍
Sep 05 PHP
PHP类继承 extends使用介绍
Jan 14 PHP
php ci框架中加载css和js文件失败的解决方法
Mar 03 PHP
phpmyadmin中禁止外网使用的方法
Nov 04 PHP
详解Window7 下开发php扩展
Dec 31 PHP
Linux下编译redis和phpredis的方法
Apr 07 PHP
分享5个非常有用的Laravel Blade指令
May 30 PHP
CI框架实现创建自定义类库的方法
Dec 25 PHP
PHP随机生成中文段落示例【测试网站内容时使用】
Apr 26 PHP
记录mysql性能查询过程的使用方法
May 02 #PHP
基于MySQL分区性能的详细介绍
May 02 #PHP
php中使用$_REQUEST需要注意的一个问题
May 02 #PHP
PHP执行批量mysql语句的解决方法
May 02 #PHP
PHP闭包(Closure)使用详解
May 02 #PHP
PHP5中Cookie与 Session使用详解
Apr 30 #PHP
PHP容易忘记的知识点分享
Apr 30 #PHP
You might like
php面试实现反射注入的详细方法
2019/09/30 PHP
JS.findElementById()使用介绍
2013/09/21 Javascript
node.js中的fs.readlink方法使用说明
2014/12/17 Javascript
JavaScript基础函数整理汇总
2015/01/30 Javascript
JQuery中Bind()事件用法分析
2015/05/05 Javascript
js实现数组转换成json
2015/06/26 Javascript
以Python代码实例展示kNN算法的实际运用
2015/10/26 Javascript
WebGL利用FBO完成立方体贴图效果完整实例(附demo源码下载)
2016/01/26 Javascript
chrome浏览器如何断点调试异步加载的JS
2016/09/05 Javascript
浅析Javascript中双等号(==)隐性转换机制
2017/10/27 Javascript
原生javascript AJAX 三级联动的实现代码
2018/05/04 Javascript
微信 jssdk 签名错误invalid signature的解决方法
2019/01/14 Javascript
JS字符串常用操作方法实例小结
2019/06/24 Javascript
JavaScript实现五子棋游戏的方法详解
2019/07/08 Javascript
Node.js API详解之 timer模块用法实例分析
2020/05/07 Javascript
Javascript数组及类数组相关原理详解
2020/10/29 Javascript
跟老齐学Python之玩转字符串(1)
2014/09/14 Python
跟老齐学Python之编写类之四再论继承
2014/10/11 Python
Django imgareaselect手动剪切头像实现方法
2015/05/26 Python
基于使用paramiko执行远程linux主机命令(详解)
2017/10/16 Python
python筛选出两个文件中重复行的方法
2018/05/31 Python
Python+Tensorflow+CNN实现车牌识别的示例代码
2019/10/11 Python
Python matplotlib以日期为x轴作图代码实例
2019/11/22 Python
python matplotlib 绘图 和 dpi对应关系详解
2020/03/14 Python
Python私有属性私有方法应用实例解析
2020/09/15 Python
PyCharm安装PyQt5及其工具(Qt Designer、PyUIC、PyRcc)的步骤详解
2020/11/02 Python
英国网上电器商店:Electricshop
2020/03/15 全球购物
高级方案规划工程师岗位职责
2013/11/29 职场文书
高中英语教学反思
2014/02/04 职场文书
人事专员的岗位职责
2014/03/01 职场文书
寄语学生的话
2014/04/10 职场文书
房屋转让协议书范本
2014/04/11 职场文书
售房协议书
2014/08/19 职场文书
2014幼儿园教师个人工作总结
2014/11/08 职场文书
卫生院义诊活动总结
2015/05/07 职场文书
Spring中的@Transactional的工作原理
2022/06/05 Java/Android