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
PHP4和PHP5共存于一系统
Nov 17 PHP
用php实现像JSP,ASP里Application那样的全局变量
Jan 12 PHP
PHP与SQL注入攻击[一]
Apr 17 PHP
Php注入点构造代码
Jun 14 PHP
为PHP初学者的8点有效建议
Nov 20 PHP
PHP执行批量mysql语句的解决方法
May 02 PHP
php简单的留言板与回复功能具体实现
Feb 19 PHP
php猴子选大王问题解决方法
May 12 PHP
验证token、回复图文\文本、推送消息的实用微信类php代码
Jun 28 PHP
详解PHP处理字符串类似indexof的方法函数
Jun 11 PHP
PHP单例模式模拟Java Bean实现方法示例
Dec 07 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
深入HTTP响应状态码速查表的详解
2013/06/07 PHP
最新制作ThinkPHP3.2.3完全开发手册
2015/11/23 PHP
关于php微信订阅号开发之token验证后自动发送消息给订阅号但是没有消息返回的问题
2015/12/21 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
2018/06/14 PHP
判断是否输入完毕再激活提交按钮
2006/06/26 Javascript
JavaScript 中的replace方法说明
2007/04/13 Javascript
Javascript select下拉框操作常用方法
2009/11/09 Javascript
基于jQuery的一个扩展form序列化到json对象
2010/12/09 Javascript
jquery1.5.1中根据元素ID获取元素对象的代码
2011/04/02 Javascript
javascript中typeof的使用示例
2013/12/19 Javascript
jQuery实现列表的全选功能
2015/03/18 Javascript
Node.js中文件操作模块File System的详细介绍
2017/01/05 Javascript
vue2.5.2使用http请求获取静态json数据的实例代码
2018/02/27 Javascript
浅谈Vue2.0中v-for迭代语法的变化(key、index)
2018/03/06 Javascript
JavaScript获取移动设备型号的实现代码(JS获取手机型号和系统)
2018/03/10 Javascript
详解如何在Vue项目中发送jsonp请求
2019/10/25 Javascript
jQuery操作元素的内容和样式完整实例分析
2020/01/10 jQuery
浅谈vue websocket nodeJS 进行实时通信踩到的坑
2020/09/22 NodeJs
JavaScript实现alert弹框效果
2020/11/19 Javascript
vue使用require.context实现动态注册路由
2020/12/25 Vue.js
Python的Scrapy爬虫框架简单学习笔记
2016/01/20 Python
python基于phantomjs实现导入图片
2016/05/13 Python
Python的for和break循环结构中使用else语句的技巧
2016/05/24 Python
Python3利用SMTP协议发送E-mail电子邮件的方法
2017/09/30 Python
Python中的类与类型示例详解
2019/07/10 Python
Django框架HttpRequest对象用法实例分析
2019/11/01 Python
39美元购买一副眼镜或太阳镜:39DollarGlasses.com
2018/06/17 全球购物
日语翻译个人求职的自我评价
2013/10/14 职场文书
企业元宵节主持词
2014/03/25 职场文书
汉语言文学专业自荐信
2014/06/11 职场文书
机械工程及其自动化专业求职信
2014/08/08 职场文书
2014机关干部学习“焦裕禄精神”思想汇报
2014/09/19 职场文书
2015年保管员工作总结
2015/04/30 职场文书
2015年入党积极分子培养考察意见
2015/08/12 职场文书
超市主管竞聘书
2015/09/15 职场文书
python基础之文件处理知识总结
2021/05/23 Python