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 相关文章推荐
动态新闻发布的实现及其技巧
Oct 09 PHP
php重定向的三种方法分享
Feb 22 PHP
PHP输出数组中重名的元素的几种处理方法
Sep 05 PHP
解密ThinkPHP3.1.2版本之模板继承
Jun 19 PHP
Laravel实现用户注册和登录
Jan 23 PHP
十幅图告诉你什么是PHP引用
Feb 22 PHP
将PHP从5.3.28升级到5.3.29时Nginx出现502错误
May 09 PHP
PHP简单操作MongoDB的方法(安装及增删改查)
May 26 PHP
PHP实现留言板功能的详细代码
Mar 25 PHP
PHP封装curl的调用接口及常用函数详解
May 31 PHP
PHP实现八皇后算法
May 06 PHP
PHP实现通过二维数组键值获取一维键名操作示例
Oct 11 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生成月历代码
2007/06/14 PHP
抓取并下载CSS中所有图片文件的php代码
2011/09/26 PHP
PHP实现GIF图片验证码
2015/11/04 PHP
thinkPHP5框架中widget的功能与用法详解
2018/06/11 PHP
基于jquery的设置页面文本框 只能输入数字的实现代码
2011/04/19 Javascript
纯文字版返回顶端的js代码
2013/08/01 Javascript
使用GruntJS构建Web程序之Tasks(任务)篇
2014/06/06 Javascript
jquery模拟alert的弹窗插件
2015/07/31 Javascript
JavaScript lodash常见用法系列小结
2016/08/24 Javascript
Js查找字符串中出现次数最多的字符及个数实例解析
2016/09/05 Javascript
关于JavaScript和jQuery的类型判断详解
2016/10/08 Javascript
js中常用的Math方法总结
2017/01/12 Javascript
利用node.js搭建简单web服务器的方法教程
2017/02/20 Javascript
原生js封装运动框架的示例讲解
2017/10/01 Javascript
vue-cli构建项目使用 less的方法
2017/10/04 Javascript
微信小程序实现验证码获取倒计时效果
2018/02/08 Javascript
亲自动手实现vue日历控件
2019/06/26 Javascript
vue项目中使用bpmn为节点添加颜色的方法
2020/04/30 Javascript
vue渲染方式render和template的区别
2020/06/05 Javascript
[02:56]DOTA2矮人直升机 英雄基础教程
2013/11/26 DOTA
[01:16:16]DOTA2-DPC中国联赛定级赛 RNG vs Phoenix BO3第二场 1月8日
2021/03/11 DOTA
python双向链表实现实例代码
2013/11/21 Python
Python 模拟登陆的两种实现方法
2017/08/10 Python
python2.7实现邮件发送功能
2018/12/12 Python
python在新的图片窗口显示图片(图像)的方法
2019/07/11 Python
基于Python实现拆分和合并GIF动态图
2019/10/22 Python
使用Python实现正态分布、正态分布采样
2019/11/20 Python
python实现画图工具
2020/08/27 Python
使用AJAX和Django获取数据的方法实例
2020/10/25 Python
HTML5自定义data-* data(obj)属性和jquery的data()方法的使用
2012/12/13 HTML / CSS
html5中audio支持音频格式的解决方法
2018/08/24 HTML / CSS
师范大学生求职信
2014/06/13 职场文书
岗位职责范本大全
2015/02/26 职场文书
求职简历自荐信怎么写
2015/03/26 职场文书
MySQL Threads_running飙升与慢查询的相关问题解决
2021/05/08 MySQL
详解Laravel服务容器的优势
2021/05/29 PHP