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 相关文章推荐
Ajax PHP分页演示
Jan 02 PHP
PHP与SQL注入攻击[三]
Apr 17 PHP
ubuntu 编译安装php 5.3.3+memcache的方法
Aug 05 PHP
php下保存远程图片到本地的办法
Aug 08 PHP
WordPress判断用户是否登录的代码
Mar 17 PHP
PHP fgetcsv 定义和用法(附windows与linux下兼容问题)
May 29 PHP
php绘图之加载外部图片的方法
Jan 24 PHP
php发送短信验证码完成注册功能
Nov 24 PHP
Smarty模板简单配置与使用方法示例
May 23 PHP
php 可变函数使用小结
Jun 12 PHP
thinkphp框架无限级栏目的排序功能实现方法示例
Mar 29 PHP
PHP与Web页面的交互示例详解一
Aug 04 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
用DBSQL类加快开发MySQL数据库程序的速度
2006/10/09 PHP
PHP定时更新程序设计思路分享
2014/06/10 PHP
Yii redis集合的基本使用教程
2020/06/14 PHP
关于document.cookie的使用javascript
2010/10/29 Javascript
javascript textarea光标定位方法(兼容IE和FF)
2011/03/12 Javascript
jquery利用event.which方法获取键盘输入值的代码
2011/10/09 Javascript
js生成随机数之random函数随机示例
2013/12/20 Javascript
把jQuery的类、插件封装成seajs的模块的方法
2014/03/12 Javascript
JS、jquery实现几分钟前、几小时前、几天前等时间差显示效果的代码实例分享
2014/04/11 Javascript
Bootstrap每天必学之按钮
2015/11/26 Javascript
jquery实现鼠标悬浮停止轮播特效
2020/08/20 Javascript
jQuery hover事件简单实现同时绑定2个方法
2016/06/07 Javascript
angularjs 源码解析之injector
2016/08/22 Javascript
BootStrap中Table隐藏后显示问题的实现代码
2017/08/31 Javascript
Egg.js 中 AJax 上传文件获取参数的方法
2018/10/10 Javascript
JS闭包原理与应用经典示例
2018/12/20 Javascript
js常用正则表达式集锦
2019/05/17 Javascript
JavaScript实现左右滚动电影画布
2020/02/06 Javascript
javascript实现简易数码时钟
2020/03/30 Javascript
jquery实现穿梭框功能
2021/01/19 jQuery
[01:41]DOTA2 2015国际邀请赛中国区预选赛第三日战报
2015/05/28 DOTA
python中enumerate的用法实例解析
2014/08/18 Python
Python实现登录接口的示例代码
2017/07/21 Python
用python与文件进行交互的方法
2018/03/01 Python
将Django项目部署到CentOs服务器中
2018/10/18 Python
Python中单线程、多线程和多进程的效率对比实验实例
2019/05/14 Python
把JSON数据格式转换为Python的类对象方法详解(两种方法)
2019/06/04 Python
Python实现朴素贝叶斯的学习与分类过程解析
2019/08/24 Python
美国在线乐器和设备商店:Musician’s Friend
2018/07/06 全球购物
C面试题
2015/10/08 面试题
早餐连锁店计划书
2014/01/08 职场文书
乐观大学生的自我评价
2014/01/10 职场文书
会计工作能力自我评价
2015/03/05 职场文书
2015年社区服务活动总结
2015/03/25 职场文书
Python基础之数据结构详解
2021/04/28 Python
pytorch实现ResNet结构的实例代码
2021/05/17 Python