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目录管理函数小结
Sep 10 PHP
PHP 截取字符串 分别适合GB2312和UTF8编码情况
Feb 12 PHP
PHP遍历数组的几种方法
Mar 22 PHP
php 生成短网址原理及代码
Jan 23 PHP
PHP队列用法实例
Nov 05 PHP
php判断访问IP的方法
Jun 19 PHP
Zend Framework自定义Helper类相关注意事项总结
Mar 14 PHP
yii2缓存Caching基本用法示例
Jul 18 PHP
详解PHP中array_rand函数的使用方法
Sep 11 PHP
PHP中的密码加密的解决方案总结
Oct 26 PHP
php设计模式之装饰模式应用案例详解
Jun 17 PHP
Laravel 数据库加密及数据库表前缀配置方法
Oct 10 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实现创建微信自定义菜单的方法示例
2017/07/14 PHP
基于jquery的bankInput银行卡账号格式化
2012/08/22 Javascript
一个级联菜单代码学习及removeClass与addClass的应用
2013/01/24 Javascript
js window.open弹出新的网页窗口
2014/01/16 Javascript
获取select元素被选中的文本内容的js代码
2014/01/29 Javascript
javascript跨域方法、原理以及出现问题解决方法(详解)
2015/08/06 Javascript
QQ登录背景闪动效果附效果演示源码下载
2015/09/22 Javascript
基于jquery animate操作css样式属性小结
2015/11/27 Javascript
JavaScript控制浏览器全屏及各种浏览器全屏模式的方法、属性和事件
2015/12/20 Javascript
探讨JavaScript标签位置的存放与功能有无关系
2016/01/15 Javascript
简单的js计算器实现
2016/10/26 Javascript
理解javascript async的用法
2017/08/22 Javascript
微信小程序添加插屏广告并设置显示频率(一天一次)
2019/12/06 Javascript
JS的时间格式化和时间戳转换函数示例详解
2020/07/27 Javascript
VUE : vue-cli中去掉路由中的井号#操作
2020/09/04 Javascript
Python3安装Pymongo详细步骤
2017/05/26 Python
python3 requests中使用ip代理池随机生成ip的实例
2018/05/07 Python
python3 小数位的四舍五入(用两种方法解决round 遇5不进)
2019/04/11 Python
Python语法分析之字符串格式化
2019/06/13 Python
Pandas 重塑(stack)和轴向旋转(pivot)的实现
2019/07/22 Python
python 调用pyautogui 实时获取鼠标的位置、移动鼠标的方法
2019/08/27 Python
python将三维数组展开成二维数组的实现
2019/11/30 Python
Tensorflow:转置函数 transpose的使用详解
2020/02/11 Python
Pyecharts 中Geo函数常用参数的用法说明
2021/02/01 Python
CSS3中Animation动画属性用法详解
2016/07/04 HTML / CSS
纯css实现照片墙3D效果的示例代码
2017/11/13 HTML / CSS
Wiggle中国:英国骑行、跑步、游泳 & 铁三运动装备专卖网店
2016/08/02 全球购物
python re模块和正则表达式
2021/03/24 Python
年终考核评语
2014/01/19 职场文书
父亲八十大寿答谢词
2014/01/23 职场文书
违反工作纪律检讨书
2014/02/15 职场文书
财产保全担保书
2015/01/20 职场文书
交警失职检讨书
2015/01/26 职场文书
离婚被告答辩状
2015/05/22 职场文书
优秀学生主要事迹怎么写
2015/11/04 职场文书
Elasticsearch 批量操作
2022/04/19 Python