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开启gzip页面压缩实例代码
Mar 11 PHP
PHP的SQL注入实现(测试代码安全不错)
Feb 27 PHP
PHP与C#分别格式化文件大小的代码
May 14 PHP
PHP 清空varnish 缓存的详解(包括指定站点下的)
Jun 20 PHP
浅析php数据类型转换
Jan 09 PHP
PHP中定义数组常量(array常量)的方法
Nov 17 PHP
PHP中使用Imagick实现各种图片效果实例
Jan 21 PHP
详解PHP错误日志的获取方法
Jul 20 PHP
分享php多功能图片处理类
May 15 PHP
thinkPHP框架实现图像裁剪、缩放、加水印的方法
Mar 14 PHP
Laravel框架实现简单的学生信息管理平台案例
May 07 PHP
PHP过滤器 filter_has_var() 函数用法实例分析
Apr 23 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函数指定默认值方法的小例子
2013/12/04 PHP
ThinkPHP登录功能的实现方法
2014/08/20 PHP
学习php设计模式 php实现命令模式(command)
2015/12/08 PHP
WordPress后台中实现图片上传功能的实例讲解
2016/01/11 PHP
详解PHP如何更好的利用PHPstorm的自动提示
2017/08/18 PHP
thinkphp5 加载静态资源路径与常量的方法
2017/12/24 PHP
js关闭模态窗口刷新父页面或跳转页面
2012/12/13 Javascript
JQuery实现用户名无刷新验证的小例子
2013/03/22 Javascript
用JS做的简单的可折叠的两级树形菜单
2013/09/21 Javascript
nodeJS代码实现计算交社保是否合适
2015/03/09 NodeJs
JS实现新浪博客左侧的Blog管理菜单效果代码
2015/10/22 Javascript
vue.js表格组件开发的实例详解
2016/10/12 Javascript
AngularJS服务service用法总结
2016/12/13 Javascript
vue-cli+webpack在生成的项目中使用bootstrap实例代码
2017/05/26 Javascript
详解tween.js的使用教程
2017/09/14 Javascript
微信小程序实现折叠面板
2018/01/31 Javascript
深入探索VueJS Scoped CSS 实现原理
2019/09/23 Javascript
Vue实现点击当前元素以外的地方隐藏当前元素(实现思路)
2019/12/04 Javascript
浅谈TypeScript的类型保护机制
2020/02/23 Javascript
vue-cli3自动消除console.log()的调试信息方式
2020/10/21 Javascript
Python使用metaclass实现Singleton模式的方法
2015/05/05 Python
Flask框架的学习指南之用户登录管理
2016/11/20 Python
详解Python中的静态方法与类成员方法
2017/02/28 Python
Python双精度浮点数运算并分行显示操作示例
2017/07/21 Python
python简易远程控制单线程版
2018/06/20 Python
Python使用pyautocad+openpyxl处理cad文件示例
2019/07/11 Python
解决TensorFlow模型恢复报错的问题
2020/02/06 Python
Python如何把Spark数据写入ElasticSearch
2020/04/18 Python
HTML5+CSS3模仿优酷视频截图功能示例
2017/01/05 HTML / CSS
深入理解HTML5定时器requestAnimationFrame的使用
2018/12/12 HTML / CSS
HTML文本属性&amp;颜色控制属性的实现
2019/12/17 HTML / CSS
西班牙灯具网上商店:Lampara.es
2018/06/05 全球购物
船舶专业个人求职信范文
2014/01/02 职场文书
小学语文教师研修感悟
2015/11/18 职场文书
php去除数组中为0的元素的实例分析
2021/11/17 PHP
进阶篇之linux环境下安装MySQL数据库
2022/04/09 MySQL