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 相关文章推荐
第三节--定义一个类
Nov 16 PHP
PHP 删除文件与文件夹操作 unlink()与rmdir()这两个函数的使用
Jul 17 PHP
PHP获取MAC地址的函数代码
Sep 11 PHP
phpMyAdmin 链接表的附加功能尚未激活问题的解决方法(已测)
Mar 27 PHP
『PHP』PHP截断函数mb_substr()使用介绍
Apr 22 PHP
ThinkPHP3.1新特性之对分组支持的改进与完善概述
Jun 19 PHP
php的ddos攻击解决方法
Jan 08 PHP
ThinkPHP安装和设置
Jul 27 PHP
PHP中array_keys和array_unique函数源码的分析
Feb 26 PHP
php抽象方法和抽象类实例分析
Dec 07 PHP
php获取ip及网址的简单方法(必看)
Apr 01 PHP
PHP编程获取各个时间段具体时间的方法
May 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
dedecms模版制作使用方法
2007/04/03 PHP
PHP UTF8编码内的繁简转换类
2009/07/20 PHP
PHP导航下拉菜单的实现如此简单
2013/09/22 PHP
使用PHP实现阻止用户上传成人照片或者裸照
2014/12/25 PHP
PHP封装的验证码工具类定义与用法示例
2018/08/22 PHP
php操作mongodb封装类与用法实例
2018/09/01 PHP
PHP+ajax实现上传、删除、修改单张图片及后台处理逻辑操作详解
2020/02/12 PHP
jQuery解决下拉框select设宽度时IE 6/7/8下option超出显示不全
2013/05/27 Javascript
jQuery function的正确书写方法
2013/08/02 Javascript
Jquery EasyUI中弹出确认对话框以及加载效果示例代码
2014/02/13 Javascript
JavaScript charCodeAt方法入门实例(用于取得指定位置字符的Unicode编码)
2014/10/17 Javascript
JavaScript实现的内存数据库LokiJS介绍和入门实例
2014/11/17 Javascript
基于豆瓣API+Angular开发的web App
2015/01/02 Javascript
Bootstrap每天必学之工具提示(Tooltip)插件
2016/04/26 Javascript
AngularJS 与Bootstrap实现表格分页实例代码
2016/10/14 Javascript
Bootstrap Img 图片样式(推荐)
2016/12/13 Javascript
使用jQuery,Angular实现登录界面验证码详解
2017/04/27 jQuery
浅谈Vue.js中ref ($refs)用法举例总结
2017/12/19 Javascript
原生JS实现多个小球碰撞反弹效果示例
2018/01/31 Javascript
vue v-model动态生成详解
2018/06/30 Javascript
jQuery实现移动端笔触canvas电子签名
2020/05/21 jQuery
antd日期选择器禁止选择当天之前的时间操作
2020/10/29 Javascript
python类参数self使用示例
2014/02/17 Python
使用python实现正则匹配检索远端FTP目录下的文件
2015/03/25 Python
Python读取视频的两种方法(imageio和cv2)
2018/04/15 Python
使用Python+wxpy 找出微信里把你删除的好友实例
2019/02/21 Python
瑞士灯具购物网站:Lampenwelt.ch
2018/07/08 全球购物
前台文员我鉴定
2014/01/12 职场文书
清扬洗发水广告词
2014/03/14 职场文书
国贸专业毕业求职信
2014/06/11 职场文书
民族学专业职业生涯规划范文:积跬步以至千里
2014/09/11 职场文书
家属答谢词
2015/01/05 职场文书
天那边观后感
2015/06/09 职场文书
致运动员加油稿
2015/07/21 职场文书
土木工程生产实习心得体会
2016/01/22 职场文书
2019年公司卫生管理制度样本
2019/08/21 职场文书