php分页limit排序,PHP+MySql千万级数据limit分页优化方案

PHP+MySql千万级数据limit分页优化方案

6cd5e4fd49658da7be73f2e8e3760c00.png

1年前

阅读 2790

评论 0

喜欢 0

### 原因

徒弟突然有个需求,就是他发现limit分页,页数越大之后,mysql的消耗越大,查询时间越长,当突破百万级数据之后,一个简单的翻页都需要5-6秒,极其不方便。

### 测试数据库结构

```

CREATE TABLE IF NOT EXISTS `video_info` (

`id` int(10) unsigned NOT NULL COMMENT '自增ID',

`channel_id` varchar(30) DEFAULT NULL COMMENT '频道ID',

) ENGINE=InnoDB AUTO_INCREMENT=4565068 DEFAULT CHARSET=utf8mb4;

ALTER TABLE `video_info`

ADD PRIMARY KEY (`id`),

ADD KEY `channel_id` (`channel_id`);

ALTER TABLE `video_info`

MODIFY `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',AUTO_INCREMENT=1;

```

上面数据库随机生成700W数据,进行效率测试。

### ThinkPHP5.1的分页代码:

```php

namespace app\index\controller;

use think\Controller;

class Index extends Controller

{

public function index() {

$page = !empty($_GET['page']) ? $_GET['page'] : 1;

$limit = !empty($_GET['limit']) ? $_GET['limit'] : 10;

$where = [];

$param = '?';

if (!empty($_GET['keys'])) {

$where[] = ['channel_id', 'like', '%'.$_GET['keys'].'%'];

$param .= 'keys='.$_GET['keys'];

}

$total = \think\Db::name('video_info')->where($where)->count();

// 取最后一条记录做翻页条件

$sql = \think\Db::name('video_info')->where($where)->limit((($page-1)*$limit), 1)->field('id')->buildSql();

$list = \think\Db::name('video_info')->where($where)->where('id >= '.$sql.'')->limit($limit)->field('id, channel_id')->select();

$this->assign('page', $page);

$this->assign('limit', $limit);

$this->assign('param', $param);

$this->assign('total', $total);

$this->assign('list', $list);

// 渲染模板输出

return $this->fetch();

}

}

```

### 原生PHP的分页代码:

```php

//程序运行时间

$starttime = explode(' ',microtime());

# 设置html页面为UTF-8编码

header("Content-type:text/html;charset=utf-8");

# 使用MySqli连接数据库

$DB = mysqli_connect('127.0.0.1', 'localhost_db', 'localhost_db', 'localhost_db', 3306);

# 设置数据库为UTF-8编码

mysqli_query($DB, 'set names utf8');

$page = !empty($_GET['page']) ? $_GET['page'] : 1;

$limit = !empty($_GET['limit']) ? $_GET['limit'] : 10;

$where = ' 1=1';

$param = '?';

if (!empty($_GET['keys'])) {

$where .= ' AND channel_id like "%'.$_GET['keys'].'%"';

$param .= 'keys='.$_GET['keys'];

}

$sql = 'SELECT COUNT(*) AS count FROM video_info where'.$where;

# 使用mysqli_query()执行SQL语句

$res = mysqli_query($DB, $sql);

# 判断是否执行成功

if ($res == false) {

echo '查询失败'; exit;

}

$array= mysqli_fetch_array($res);

$total = $array['count'];

$sql = ' SELECT `id`,`channel_id` FROM `video_info` WHERE '.$where.' AND ( id >= ( SELECT `id` FROM `video_info` WHERE '.$where.' LIMIT '.(($page-1)*$limit).', 1 ) ) LIMIT '.$limit;

$res = mysqli_query($DB, $sql);

# 判断是否执行成功

if ($res == false) {

echo '查询失败'; exit;

}

$list= mysqli_fetch_all($res, MYSQLI_ASSOC);

//程序运行时间

$endtime = explode(' ',microtime());

$thistime = $endtime[0]+$endtime[1]-($starttime[0]+$starttime[1]);

$thistime = round($thistime,7);

$title = "本网页执行耗时:".$thistime." 秒";

?>

test page

搜索

ID渠道ID
<?php echo $v['id'];?><?php echo $v['channel_id'];?>

function getParameter(name) {

var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");

var r = window.location.search.substr(1).match(reg);

if (r!=null) return unescape(r[2]); return null;

}

//init

$(function(){

var totalPage = <?php echo $total/$limit;?>;

var totalRecords = <?php echo $total;?>;

var pageNo = getParameter('page');

if(!pageNo){

pageNo = 1;

}

//生成分页

//有些参数是可选的,比如lang,若不传有默认值

kkpager.generPageHtml({

pno : pageNo,

//总页码

total : totalPage,

//总数据条数

totalRecords : totalRecords,

//链接前部

hrefFormer : '/2/index.php',

//链接尾部

hrefLatter : '',

getLink : function(n){

return this.hrefFormer + this.hrefLatter +'<?php echo $param;?>'+"&page="+n;

}

/*

,lang: {

firstPageText: '首页',

firstPageTipText: '首页',

lastPageText: '尾页',

lastPageTipText: '尾页',

prePageText: '上一页',

prePageTipText: '上一页',

nextPageText: '下一页',

nextPageTipText: '下一页',

totalPageBeforeText: '共',

totalPageAfterText: '页',

currPageBeforeText: '当前第',

currPageAfterText: '页',

totalInfoSplitStr: '/',

totalRecordsBeforeText: '共',

totalRecordsAfterText: '条数据',

gopageBeforeText: ' 转到',

gopageButtonOkText: '确定',

gopageAfterText: '页',

buttonTipBeforeText: '第',

buttonTipAfterText: '页'

}*/

//,

//mode : 'click',//默认值是link,可选link或者click

//click : function(n){

//this.selectPage(n);

// return false;

//}

});

});

```

### 最终效果

在没优化之前,正常的limit翻页到4.5W页,最后一页时,需要耗时22秒左右,优化之后则只需要花费1.5秒,提高了17倍左右的查询速度。

### 原理和缺点:

原理很简单,就是使用子查询获得max(id),然后进行当前分页。

缺点也很明显,那就是分页的关键参数,id值只能为自增主键,否则这个方案用不了。

© 著作权归作者所有

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值