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 相关文章推荐
也谈php网站在线人数统计
Apr 09 PHP
PHP 页面编码声明方法详解(header或meta)
Mar 12 PHP
Blitz templates 最快的PHP模板引擎
Apr 06 PHP
DedeCMS 核心类TypeLink.class.php摘要笔记
Apr 07 PHP
检测png图片是否完整的php代码
Sep 06 PHP
PHP冒泡排序算法代码详细解读
Jul 17 PHP
Opcache导致php-fpm崩溃nginx返回502
Mar 02 PHP
php计算一个文件大小的方法
Mar 30 PHP
Symfony2框架学习笔记之表单用法详解
Mar 18 PHP
基于PHP制作验证码
Oct 12 PHP
PHP流Streams、包装器wrapper概念与用法实例详解
Nov 17 PHP
PHP7新增函数
Mar 09 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中调用其他系统http接口的方法说明
2014/02/28 PHP
php对称加密算法示例
2014/05/07 PHP
PHP实现数字补零功能的2个函数介绍
2014/05/12 PHP
PHP比你想象的好得多
2014/11/27 PHP
Yii2框架引用bootstrap中日期插件yii2-date-picker的方法
2016/01/09 PHP
zend framework中使用memcache的方法
2016/03/04 PHP
php判断电子邮件是否正确方法
2018/12/04 PHP
JS 常用校验函数
2009/03/26 Javascript
各种常用浏览器getBoundingClientRect的解析
2009/05/21 Javascript
做好七件事帮你提升jQuery的性能
2014/02/06 Javascript
jquery获取tr并更改tr内容示例代码
2014/02/13 Javascript
JavaScript中Cookies的相关使用教程
2015/06/04 Javascript
jQuery实现带水平滑杆的焦点图动画插件
2016/03/08 Javascript
JS两种类型的表单提交方法实例分析
2016/11/28 Javascript
Node.js数据库操作之连接MySQL数据库(一)
2017/03/04 Javascript
jQuery Plupload上传插件的使用
2017/04/19 jQuery
PHP 实现一种多文件上传的方法
2017/09/20 Javascript
通过源码分析Vue的双向数据绑定详解
2017/09/24 Javascript
微信小程序数字滚动插件使用详解
2018/02/02 Javascript
详解Vue源码学习之callHook钩子函数
2018/07/25 Javascript
JS算法题之查找数字在数组中的索引位置
2019/05/15 Javascript
windows下create-react-app 升级至3.3.1版本踩坑记
2020/02/17 Javascript
vue倒计时刷新页面不会从头开始的解决方法
2020/03/03 Javascript
python中使用xlrd、xlwt操作excel表格详解
2015/01/29 Python
Python实现字符串的逆序 C++字符串逆序算法
2020/05/28 Python
python print出共轭复数的方法详解
2019/06/25 Python
使用 CSS3 中@media 实现网页自适应的示例代码
2020/03/24 HTML / CSS
酒店出纳岗位职责
2013/12/29 职场文书
领导班子三严三实心得体会
2014/10/13 职场文书
幼儿园小班见习报告
2014/10/31 职场文书
六年级学生期末评语
2014/12/26 职场文书
简历中的自我评价怎么写呢?
2019/04/30 职场文书
两行代码解决Jupyter Notebook中文不能显示的问题
2021/04/24 Python
Nginx反向代理至go-fastdfs案例讲解
2021/08/02 Servers
日本官方排名前10的动漫,名侦探柯南上榜,第一是一部创造历史的动漫
2022/03/18 日漫
Java实现经典游戏泡泡堂的示例代码
2022/04/04 Java/Android