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 相关文章推荐
APACHE的AcceptPathInfo指令使用介绍
Jan 18 PHP
深入phpMyAdmin的安装与配置的详细步骤
May 07 PHP
JoshChen_php新手进阶高手不可或缺的规范介绍
Aug 16 PHP
三种php连接access数据库方法
Nov 11 PHP
MongoDB在PHP中的常用操作小结
Feb 20 PHP
Laravel 5框架学习之环境与配置
Apr 08 PHP
php跨服务器访问方法小结
May 12 PHP
PHP面向对象继承用法详解(优化与减少代码重复)
Dec 02 PHP
PHP文件管理之实现网盘及压缩包的功能操作
Sep 20 PHP
Laravel框架使用monolog_mysql实现将系统日志信息保存到mysql数据库的方法
Aug 16 PHP
PHP 二维array转换json的实例讲解
Aug 21 PHP
PHP实现唤起微信支付功能
Feb 18 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中strtotime函数使用方法详解
2011/11/27 PHP
PHP函数超时处理方法
2016/02/14 PHP
smarty自定义函数用法示例
2016/05/20 PHP
php arsort 数组降序排序详细介绍
2016/11/17 PHP
php中类和对象:静态属性、静态方法
2017/04/09 PHP
Yii 2.0实现联表查询加搜索分页的方法示例
2017/08/02 PHP
Laravel框架处理用户的请求操作详解
2019/12/20 PHP
php设计模式之策略模式实例分析【星际争霸游戏案例】
2020/03/26 PHP
摘自百度的图片轮换效果代码
2007/11/19 Javascript
img的onload的另类用法
2008/01/10 Javascript
jQuery Easyui 验证两次密码输入是否相等
2016/05/13 Javascript
javascript 广告移动特效的实现代码
2016/06/25 Javascript
AngularJS入门教程之Scope(作用域)
2016/07/27 Javascript
ionic2 tabs使用 Modal底部tab弹出框
2016/12/30 Javascript
浅谈VUE单页应用首屏加载速度优化方案
2018/08/28 Javascript
cdn模式下vue的基本用法详解
2018/10/07 Javascript
vue.js实现回到顶部动画效果
2019/07/31 Javascript
js实现省级联动(数据结构优化)
2020/07/17 Javascript
Js利用正则表达式去除字符串的中括号
2020/11/23 Javascript
[04:48]DOTA2亚洲邀请赛林书豪为VGJ加油
2017/04/01 DOTA
Python抓取Discuz!用户名脚本代码
2013/12/30 Python
python计算两个地址之间的距离方法
2018/06/09 Python
浅谈pycharm下找不到sqlalchemy的问题
2018/12/03 Python
浅谈PYTHON 关于文件的操作
2019/03/19 Python
JYSK加拿大:购买家具、床垫、家居装饰等
2020/02/14 全球购物
疾病防治方案
2014/05/31 职场文书
计算机应用专业毕业生求职信
2014/06/03 职场文书
公司口号大全
2014/06/11 职场文书
中共广东省委常委会党的群众路线教育实践活动整改方案
2014/09/23 职场文书
2014年幼儿园老师工作总结
2014/12/05 职场文书
2014年稽查工作总结
2014/12/20 职场文书
2015社区精神文明建设工作总结
2015/04/21 职场文书
2016年师德学习心得体会
2016/01/12 职场文书
2016北大自主招生自荐信模板
2016/01/28 职场文书
Python基础之pandas数据合并
2021/04/27 Python
PHP设计模式(观察者模式)
2021/07/07 PHP