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 相关文章推荐
解析thinkphp中的导入文件标签
Jun 20 PHP
PHP数组排序之sort、asort与ksort用法实例
Sep 08 PHP
PHP提示Cannot modify header information - headers already sent by解决方法
Sep 22 PHP
phplot生成图片类用法详解
Jan 06 PHP
PHP下载远程文件到本地存储的方法
Mar 24 PHP
PHP中isset与array_key_exists的区别实例分析
Jun 02 PHP
如何实现php图片等比例缩放
Jul 28 PHP
详解WordPress中创建和添加过滤器的相关PHP函数
Dec 29 PHP
PHP实现的进度条效果详解
May 03 PHP
php通过会话控制实现身份验证实例
Oct 18 PHP
[原创]PHP获取数组表示的路径方法分析【数组转字符串】
Sep 01 PHP
php输出反斜杠的实例方法
Sep 19 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
phpMyAdmin链接MySql错误 个人解决方案
2009/12/28 PHP
PHP微信企业号开发之回调模式开启与用法示例
2017/11/25 PHP
关于PHP虚拟主机概念及如何选择稳定的PHP虚拟主机
2018/11/20 PHP
JavaScript 继承详解(二)
2009/07/13 Javascript
Jquery Ajax学习实例3 向WebService发出请求,调用方法返回数据
2010/03/16 Javascript
window.ActiveXObject使用说明
2010/11/08 Javascript
jquery使用ColorBox弹出图片组浏览层实例演示
2013/03/14 Javascript
微信小程序使用picker实现时间和日期选择框功能【附源码下载】
2017/12/11 Javascript
JavaScript中Object值合并方法详解
2017/12/22 Javascript
Vue自定义指令实现checkbox全选功能的方法
2018/02/28 Javascript
JavaScript引用类型Date常见用法实例分析
2018/08/08 Javascript
读懂CommonJS的模块加载
2019/04/19 Javascript
Vue项目接入Paypal实现示例详解
2020/06/04 Javascript
vue实现登录拦截
2020/06/29 Javascript
jQuery实现评论模块
2020/08/19 jQuery
Vue 3自定义指令开发的相关总结
2021/01/29 Vue.js
[03:26]回顾2015国际邀请赛中国区预选赛
2015/06/09 DOTA
Python通过正则表达式选取callback的方法
2015/07/18 Python
python装饰器初探(推荐)
2016/07/21 Python
python+selenium实现163邮箱自动登陆的方法
2017/12/31 Python
解决python3中解压zip文件是文件名乱码的问题
2018/03/22 Python
详解Python用户登录接口的方法
2019/04/17 Python
处理Selenium3+python3定位鼠标悬停才显示的元素
2019/07/31 Python
python实现的汉诺塔算法示例
2019/10/23 Python
python 如何用urllib与服务端交互(发送和接收数据)
2021/03/04 Python
css 省略号 css3让多余的字符串消失并附加省略号的实现代码
2013/02/07 HTML / CSS
美国领先的医疗警报服务:Philips Lifeline
2018/03/12 全球购物
php优化查询foreach代码实例讲解
2021/03/24 PHP
应届医学毕业生求职信分享
2013/12/02 职场文书
公司财务总监岗位职责
2013/12/14 职场文书
演讲比赛获奖感言
2014/02/02 职场文书
行政专员的岗位职责
2014/03/10 职场文书
婚宴领导致辞
2015/07/28 职场文书
幼儿园中班教育随笔
2015/08/14 职场文书
简历自我评价范文
2019/04/24 职场文书
Python学习之迭代器详解
2022/04/01 Python