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 相关文章推荐
Bo-Blog专用的给Windows服务器的IIS Rewrite程序
Aug 26 PHP
在字符串指定位置插入一段字符串的php代码
Feb 16 PHP
php microtime获取浮点的时间戳
Feb 21 PHP
PHP的加密方式及原理
Jun 14 PHP
apache+codeigniter 通过.htcaccess做动态二级域名解析
Jul 01 PHP
使用PHP会话(Session)实现用户登陆功能
Jun 29 PHP
php遍历树的常用方法汇总
Jun 18 PHP
php设置页面超时时间解决方法
Sep 22 PHP
CI分页类首页、尾页不显示的解决方法
Mar 28 PHP
PHP设计模式之工厂模式与单例模式
Sep 28 PHP
老生常谈PHP数组函数array_merge(必看篇)
May 25 PHP
PHP全局使用Laravel辅助函数dd
Dec 26 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 current函数获取未知字符键名数组第一个元素的值
2013/06/24 PHP
php实现可运算的验证码
2015/11/10 PHP
Zend Framework教程之视图组件Zend_View用法详解
2016/03/05 PHP
php自定义函数实现统计中文字符串长度的方法小结
2017/04/15 PHP
解决laravel5中auth用户登录其他页面获取不到登录信息的问题
2019/10/08 PHP
浅谈laravel5.5 belongsToMany自身的正确用法
2019/10/17 PHP
商城常用滚动的焦点图效果代码简单实用
2013/03/28 Javascript
javascript实现可改变滚动方向的无缝滚动实例
2013/06/17 Javascript
javascript中Number对象的toString()方法分析
2014/12/20 Javascript
jQuery实现的数值范围range2dslider选取插件特效多款代码分享
2015/08/27 Javascript
JS控制页面跳转时未请求要跳转的地址怎么回事
2016/10/14 Javascript
JS多物体实现缓冲运动效果示例
2016/12/20 Javascript
bootstrap paginator分页前后台用法示例
2017/06/17 Javascript
使用vue-cli导入Element UI组件的方法
2018/05/16 Javascript
vue定义全局变量和全局方法的方法示例
2018/08/01 Javascript
JavaScript函数式编程(Functional Programming)组合函数(Composition)用法分析
2019/05/22 Javascript
Vue2.X和Vue3.0数据响应原理变化的区别
2019/11/07 Javascript
基于JS实现快速读取TXT文件
2020/08/25 Javascript
[02:53]DOTA2英雄昆卡基础教程
2013/11/25 DOTA
python 字符串格式化代码
2013/03/17 Python
Python常用随机数与随机字符串方法实例
2015/04/09 Python
Python 逐行分割大txt文件的方法
2017/10/10 Python
Python简单读取json文件功能示例
2017/11/30 Python
python删除过期log文件操作实例解析
2018/01/31 Python
Python实现字典排序、按照list中字典的某个key排序的方法示例
2018/12/18 Python
使用PYTHON解析Wireshark的PCAP文件方法
2019/07/23 Python
Django REST framework内置路由用法
2019/07/26 Python
python+selenium 鼠标事件操作方法
2019/08/24 Python
python模块常用用法实例详解
2019/10/17 Python
python通过nmap扫描在线设备并尝试AAA登录(实例代码)
2019/12/30 Python
python中什么是面向对象
2020/06/11 Python
存储过程和函数的区别
2013/05/28 面试题
2014年元旦活动方案
2014/02/15 职场文书
新学期开学演讲稿
2014/05/24 职场文书
公司劳动纪律管理制度
2015/08/04 职场文书
导游词之金鞭溪风景区
2019/09/12 职场文书