php超大树形分页,PHP+MySql千万级数据limit分页优化方案

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

6cd5e4fd49658da7be73f2e8e3760c00.png

1年前

阅读 2750

评论 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值只能为自增主键,否则这个方案用不了。

© 著作权归作者所有

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle数据库连接满了,ORACLE数据库连接数满的分析及优化

最近在使用Oracle的过程中&#xff0c;出现了数据库连接数满的情况&#xff0c;导致程序及数据库连接工具连接不上。主要从两个方面来考虑这件事&#xff0c;从程序方面来看&#xff1a;1.进行数据库连接操作后未释放连接&#xff1b;2.若使用了数据库连接池&#xff0c;则考虑…

oracle移动硬盘盒,oracle-linux下挂载移动硬盘 NTFS类型

环境&#xff1a;ORACLE-LINUX 5.7全新移动硬盘(未使用过)移动硬盘空间3T在默认情况下&#xff0c;Linux系统不支持NTFS分区挂载1、服务器&#xff1a;A服务器和B服务器为一套ORACLE-RAC&#xff0c;移动硬盘插在A服务器上&#xff1b;2、下载ntfs-3g包&#xff0c;在A服务器上…

linux自启动配置文件,Linux中如何设置服务自启动?

有时候我们需要Linux系统在开机的时候自动加载某些脚本或系统服务&#xff0c;主要用三种方式进行这一操作&#xff1a;ln -s 在/etc/rc.d/rc*.d目录中建立/etc/init.d/服务的软链接(*代表0&#xff5e;6七个运行级别之一)chkonfig 命令行运行级别设置nts…

linux系统下升级node,linux下安装指定版本的nodejs(升级到指定版本)

原因最近需要全栈开发但是服务器是linux系统&#xff0c;服务本身通过yum安装软件包&#xff0c;不过yum安装的nodejs版本太低。所以需要自己安装。方案下载编译好的文件解压后直接运行即可&#xff0c;不过我们需要全局运行node命令。只需要把目录设置为全局即可(建立软链接 l…

Linux bash卸载软件,Ubuntu卸载软件的4种方法

前言本文重点介绍Ubuntu卸载软件的4种方法。他们分别是图形化界面的synaptic、自动解决依赖关系的apt-get&#xff0c;处理依赖关系更强大的aptitude&#xff0c;还有安装本地deb包的dpkg。方法一: Ubuntu使用synaptic图形化界面管理软件oucanrongzcwyou:~$ sudo apt-get insta…

linux系统atom安装教程,Ubuntu/Linux Mint上安装Atom文本编辑器

Atom是一款由Github开发的开源文本编辑器&#xff0c;虽然目前该软件依然在Beta阶段&#xff0c;但我们依然可以在你的Ubuntu/Linux Mint上使用它。据Atom官方博客介绍&#xff0c;与Atom类似的编辑器Sublime和TextMate都深受开发者欢迎&#xff0c;但在扩展性上都有所限制&…

linux替换windows回车,转载 vi替换windows换行符为linux换行符

1.用vi打开指定文本文件&#xff1a;vi filename.c2.按 Esc 键&#xff0c;进入命令模式&#xff1b;3.按 : 键 (按 Shift 键不放后&#xff0c;同时按 : 键)进入命令输入状态&#xff1b;4.在冒号后输入&#xff1a;%s/^M//g注&#xff1a;^M是一个字符不是输入^和M两个字符&a…

ausam3x 嵌入式linux,ATSAM3X8EA-AU - 微控制器, 32位, SAM3X系列, ARM 皮质-M3, 84nb

ATSAM3X8EA-AU -微控制器, 32位, SAM3X系列, ARM 皮质-M3, 84 MHz,512 KB, 96 KB, 144 引脚, LQFPTheATSAM3X8EA-AU is a Flash Microcontroller, based on the highperformance 32-bit ARM Cortex-M3 RISC processor. It operates at amaximum speed of 84MHz and features up…

网络编程与分层协议设计:基于linux平台实现,网络编程与分层协议设计:基于Linux平台实现...

图书简介本书以Linux网络套接字编程和网络分层协议的设计与程序实现为主题&#xff0c;详细介绍如何在Linux平台下进行套接字程序设计&#xff0c;并给出了一个基于分层协议的应用实例&#xff0c;用于模拟Linux网络协议栈中IP层的核心功能—IP报文的分段和重组过程。本书旨在通…

labview 远程连接linux,SSH交互式通信总结:expect、plink、putty、sshpass、ALAB SSH

关于在linux脚本中远程执行命令的问题&#xff0c;笔者在以前文章中可以使用expect工具来完成交互式通信。在windows平台下可以使用plink或者putty工具。免密也可以设置SSH秘钥&#xff0c;参考文章-Linux怎么远程执行指令呢-SSH秘钥。但是在linux下需要一一对应设置&#xff0…

linux安装版本的python,linux安装python各种版本.md

### linux安装python各种版本PS&#xff1a;首先按照本文的问题按照教程解决&#xff0c;再安装编译python[python各个版本大全](https://www.python.org/ftp/python/)bashwget https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tar.xz #下载tar xJf Python-3.7.0.tar.xz…

tcping在linux用法,tcping的安装和使用

Tcping 网上比较少资料是关于linux对tcp端口ping测试的方法&#xff0c;我这里简单介绍2种方法&#xff1a; 1.yum安装&#xff0c;编译安装方法 1-1.wget http://linuxco.de/tcping/tcping-1.3.5.tar.gz ###下载tcping1-2 tar zxvf tcping-1.3.5.tar.gz ####解压缩tcping-1.3.…

linux子系统安装gromacs,科学网—Windows下GROMACS程序的编译 - 李继存的博文

2015-12-07 22:12:05总的来说, Windows下的GROMACS程序用于模拟意义不大, 对于长时间的模拟, 我都是放在Linux服务器上进行的. 但将Windows下的GROMACS程序作为一个辅助工具来使用还是有意义的. 因为大多数时候, 我都是在Windows下准备输入文件的, 然后测试一下准备好的输入文件…

surface装linux键盘不能用,Linux 5.13开始支持新款微软Surface系列笔记本的键盘与触摸板...

原标题&#xff1a;Linux 5.13开始支持新款微软Surface系列笔记本的键盘与触摸板改善Linux下微软Surface笔记本支持的探索还在继续。随着Linux 5.13的发布&#xff0c;不仅有Surface DTX驱动&#xff0c;另一个新的Surface驱动 "surface-hid"将允许在较新的Surface设…

linux mp3长度,得到一个wma或mp3文件,如何用最简单的方法得到它的长度信息呢?...

得到一个wma或mp3文件&#xff0c;如何用最简单的方法得到它的长度信息呢&#xff1f; Delphi / Windows SDK/APIhttp://www.delphi2007.net/DelphiMultimedia/html/delphi_20061108195617169.html不需要知道歌手名专辑名等&#xff0c;只要长度即可&#xff0c;哪个指令可以做…

c语言统计数字字母个数,请问这个用c怎么做:输入一串字符,分别统计其中数字和字母的个数...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#includemain(){int acount0,bcount0,ccount0,dcount0;char a;printf("请输入一行字符:\n");a getchar();while (a !\n){switch (a){caseq:casew:casee:caser:caset:casey:caseu:casei:caseo:casep:casea:cases:cased:c…

libmysqld_dev linux,Linux下python玩转MySQLdb

(0)目录Linux下python玩转MySQLdb一&#xff1a;起因(1)Linux下安装python的第三方模块 ---- MySQLdb&#xff0c;自己走了很多弯路&#xff0c;在此做一下总结&#xff0c;希望对于像我一样的初学者有所帮助(2)Python中mysql数据库连接的例子&#xff0c;请见Linux公社 或者 我…

单片机按键控制数码管c语言程序,基于单片机的按键控制LED数码管共阴极动态显示电路设计报告(毕业论文).doc...

基于单片机的按键控制LED数码管共阴极动态显示电路设计报告(毕业论文)物理与电子工程学院2014级课程设计PAGE IV物理与电子工程学院《单片机原理与接口技术》课程设计报告书设计题目&#xff1a; 基于单片机的按键控制LED数码管共阴极动态显示电路设计专 业&#xff1a; 自动化…

c语言把数据存放在文件中,急求如何将下列C语言程序数据存储到文件中?

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼求如何改动才能将下列程序的存储输入或输出数据(或两者一起)到指定的文件(或运行时直接创立一个文件)如Arrangement中。#include int n0;int rest[7][7]; //全局声明,以供全局调用int main(){void perm(int list[],int ,int );int …

c语言sgoto 标志位,如何在Go中设置TCP数据包的“不分段”标志位?(How to set “don't fragment” flag bit for TCP packet in Go?)...

如何在Go中设置TCP数据包的“不分段”标志位&#xff1f;(How to set “dont fragment” flag bit for TCP packet in Go?)我打算在Go中设置“do not fragment”标志位&#xff0c;与C中的这个标志位相同。我检查了常量列表&#xff0c;但没有找到该选项。 那么Go的相应选项是…