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设计模式  Command(命令模式)
Jun 17 PHP
PHP中改变图片的尺寸大小的代码
Jul 17 PHP
php计算十二星座的函数代码
Aug 21 PHP
PHP验证信用卡卡号是否正确函数
May 27 PHP
PHP整合七牛实现上传文件
Jul 03 PHP
PHP MYSQL实现登陆和模糊查询两大功能
Feb 05 PHP
Zend Framework基本页面布局分析
Mar 19 PHP
php实现按天数、星期、月份查询的搜索框
May 02 PHP
PHP使用PHPExcel删除Excel单元格指定列的方法
Jul 06 PHP
PHPExcel在linux环境下导出报500错误的解决方法
Jan 26 PHP
PHP 爬取网页的主要方法
Jul 13 PHP
Laravel框架源码解析之入口文件原理分析
May 14 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/01/02 PHP
php 接口类与抽象类的实际作用
2009/11/26 PHP
php实现删除指定目录下相关文件的方法
2014/10/20 PHP
PHP中str_split()函数的用法讲解
2019/04/11 PHP
用javascript实现读取txt文档的脚本
2007/07/20 Javascript
js对数字的格式化使用说明
2011/01/12 Javascript
javascript中的if语句使用介绍
2013/11/20 Javascript
JS执行删除前的判断代码
2014/02/18 Javascript
jQuery控制frames及frame页面JS的方法
2016/03/08 Javascript
BootStrap 智能表单实战系列(二)BootStrap支持的类型简介
2016/06/13 Javascript
JS把内容动态插入到DIV的实现方法
2016/07/19 Javascript
第一次接触神奇的前端框架vue.js
2016/12/01 Javascript
Angular.js之作用域scope'@','=','&amp;'实例详解
2017/02/28 Javascript
nodejs 递归拷贝、读取目录下所有文件和目录
2019/07/18 NodeJs
vue+webpack dev本地调试全局样式引用失效的解决方案
2019/11/12 Javascript
JS函数本身的作用域实例分析
2020/03/16 Javascript
用JS实现选项卡
2020/03/23 Javascript
[59:26]DOTA2上海特级锦标赛D组资格赛#1 EG VS VP第二局
2016/02/28 DOTA
python调用cmd复制文件代码分享
2013/12/27 Python
Python break语句详解
2014/03/11 Python
python实现bucket排序算法实例分析
2015/05/04 Python
Python计算三维矢量幅度的方法
2015/06/15 Python
python pandas 对series和dataframe的重置索引reindex方法
2018/06/07 Python
解决Python使用列表副本的问题
2019/12/19 Python
Python接口测试get请求过程详解
2020/02/28 Python
jupyter notebook参数化运行python方式
2020/04/10 Python
浅谈Keras的Sequential与PyTorch的Sequential的区别
2020/06/17 Python
利用python制作拼图小游戏的全过程
2020/12/04 Python
沙特阿拉伯网上购物:Sayidaty Mall
2018/05/06 全球购物
迪卡侬印尼体育用品商店:Decathlon印尼
2020/03/11 全球购物
2014年寒假社会实践活动心得体会
2014/04/07 职场文书
任命书格式
2014/06/05 职场文书
2014党的群众路线教育实践活动学习心得体会
2014/10/31 职场文书
2015年求职自荐信范文
2015/03/04 职场文书
中秋节作文(五年级)之关于月亮
2019/09/11 职场文书
Android移动应用开发指南之六种布局详解
2022/09/23 Java/Android