我有一个超过300万行的大型数据库,我需要一个php& mysql分页脚本。我已经有一个但是在我通过了1000页的水平后,它的工作速度非常慢,就像加载时间一样。欢迎任何可以帮助我的脚本/建议。
使用此脚本进行编辑
<?php
// Script de paginare, de la http://www.marplo.net
// Datele pt. conectare la baza de date
// MODIFICATI
$host = "localhost"; // server MySQL
$utilizator = "root";
$parola = "parola";
$numebd = "nume_bd"; // nume baza de date
// Conectarea la baza de date
$conn = mysql_connect($host, $utilizator, $parola);
if (!$conn) {
echo 'Conectare nereusita la MySQL';
exit;
}
// Selectarea bazei de date
if (!mysql_select_db($numebd, $conn)) {
echo 'Baza de date nu a putut fi selectata deoarece : '. mysql_error();
exit;
}
// Setarea pentru format UTF-8
$sql = "SET NAMES 'utf8'";
mysql_query($sql, $conn);
// Afla cate linii sunt in tabel (MODIFICATI 'nume_tb') din baza de date
$sql = "SELECT COUNT(*) FROM `nume_tb`";
$result = mysql_query($sql, $conn) or trigger_error(E_USER_ERROR);
$r = mysql_fetch_row($result);
$numrows = $r[0];
// Stabileste numarul de linii din tabel afisate in pagina
$rowsperpage = 10;
// afla numarul total necesar de pagini
$totalpages = ceil($numrows / $rowsperpage); // ceil face rotunjire la int. maxim
// Obtine pagina curenta sau seteaza default
if (isset($_GET['currentpage']) && is_numeric($_GET['currentpage'])) {
// seteaza variabila ca int
$currentpage = (int) $_GET['currentpage'];
} else {
// pagina care este initial afisata (pagina default)
$currentpage = 1;
}
// daca pagina curenta e mai mare decat total pagini...
if ($currentpage > $totalpages) {
// seteaza pagina curenta la ultima pagina
$currentpage = $totalpages;
}
// daca pagina curenta e mai mica decat prima pagina...
if ($currentpage < 1) {
// seteaza pagina curenta la prima pagina
$currentpage = 1;
}
// lista cu pagini, in functie de pagina curenta
$offset = ($currentpage - 1) * $rowsperpage;
// obtine datele din tabel (MODIFICATI 'nume_tb') din baza de date
$sql = "SELECT * FROM `nume_tb` LIMIT $offset, $rowsperpage";
$result = mysql_query($sql, $conn) or trigger_error(E_USER_ERROR);
// parcurgerea matricei cu datele obtinute
while ($list = mysql_fetch_assoc($result)) {
// - MODIFICATI numele coloanelor tabelului ('id' si 'texte')
// Stocheaza datele returnate de MySQL in variabile array pt. fiecare coloana
$id[] = $list['id'];
$text[] = $list['texte'];
}
mysql_close(); // Incheie conexiunea cu mysql
/*** Afisarea datelor obtinute ***/
// Parcurge variabilele array setate in bucla WHILE
for($i=0; $i<count($id); $i++) {
// Aici puteti adauga cod HTML pentru aspectul grafic al afisarii
echo $id[$i]. " - ". $text[$i]. "<br />";
}
/*** Construirea link-urilor pt. paginare ***/
// raza nr. link-uri din jurul celui curent
$range = 3;
// Link-uri inapoi, daca pagina curenta nu e prima
if ($currentpage > 1) {
// arata << pt. link la prima pagina
echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=1'><<</a> ";
// obtine nr. pagina din urma
$prevpage = $currentpage - 1;
// arata < pt. link la o pagina in urma
echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$prevpage'><</a> ";
}
// definirea link-urilor din raza paginii curente
for ($x = ($currentpage - $range); $x < (($currentpage + $range) + 1); $x++) {
// daca e un nr. de pagina valid ...
if (($x > 0) && ($x <= $totalpages)) {
// daca nr. e pagina curenta ...
if ($x == $currentpage) {
// afiseaza nr. pagina fara a fi link
echo " [<b>$x</b>] ";
// daca nr. nu e pagina curenta ...
} else {
// il face link
echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$x'>$x</a> ";
}
}
}
// Daca pagina curenta nu e ultima, afiseaza link inainte si spre ultima pagina
if ($currentpage != $totalpages) {
// obtine pagina urmatoare
$nextpage = $currentpage + 1;
// arata > pt. urmatoarea pagina
echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$nextpage'>></a> ";
// arata >> pt. ultima pagina
echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$totalpages'>>></a> ";
}
?>
答案 0 :(得分:3)
请在下次发布新问题之前使用搜索功能和相关问题小工具。
大限制问题:
小心大限制使用索引进行排序是有效的,如果您需要前几行,即使进行了一些额外的过滤,因此您需要按LIMIT请求按索引扫描更多行。但是,如果您处理LIMIT查询具有较大的偏移效率将受到影响。 LIMIT 1000,10可能比LIMIT 0,10慢。确实,大多数用户在结果中不会超过10页,但搜索引擎机器人可能会这样做。我见过机器人在我的项目中看了200多页。此外,对于许多未能处理此问题的网站,提供了一个非常容易的任务来启动DOS攻击 - 从少数连接请求包含大量数字的页面就足够了。如果您不做任何其他事情,请确保阻止页码太大的请求。
对于某些情况,例如,如果结果是静态的,那么预先计算结果可能是有意义的,这样您就可以查询它们的位置。 因此,不是使用LIMIT 1000,10查询,而是在1000和1009之间的WHERE位置,对于任何位置都具有相同的效率(只要它被索引)
ORDER BY … LIMIT Performance Optimization
相关问题: Alphabetical pagination gets progressively slower as you page (MySQL)
答案 1 :(得分:0)
由于您使用的是MySQL,因此您可以利用LIMIT
命令:
$start = ($current_page - 1) * $rows_per_page; //Entry 1 on page 1 is index 0
$select = "SELECT * FROM `your_table` LIMIT $start, $rows_per_page"
答案 2 :(得分:0)
一些小事:
您似乎只是从每一行使用'id'和'texte'。在这种情况下使用SELECT *
是好的,如果这些是唯一的两列,但如果有其他列则浪费。如果是这种情况,请改用SELECT id,texte
。
您可以通过while (list(id[],text[]) = mysql_fetch_row());
删除中间变量$ list(假设您的行的id和texte为前两列,或者您根据我之前的行更改了SELECT
要点)。同样,对于检索行计数:list($numrows) = mysql_fetch_row($result);
将起作用。
您可以从所有href中删除{$_SERVER['PHP_SELF']}
。 “?currentpage = 6”是完全有效的(相对)href。对于较短的html,请考虑使用'p'而不是'currentpage'作为查询字符串变量。
现在还有几个主要的:
(1)从我所看到的速度问题肯定是一个MySQL问题 - 你的代码中没有大的循环。
尝试(a)将'id'作为主键(如果它还没有;或者如果你已经拥有另一个主键列,则将其设为索引),以及(b)添加ORDER BY id
子句到获取数据的select语句。这肯定会有所帮助。
如果没有,作为最后的手段,您可以创建第二个表来索引您的行:
CREATE TABLE nume_tb_idx (
idx int NOT NULL PRIMARY KEY,
id int NOT NULL FOREIGN KEY REFERENCES nume_tb (id) ON UPDATE CASCADE ON DELETE CASCADE
);
(假设这里你的id列是一个int)。
在向nume_tb添加行时,您必须向此表添加一个条目,并且在从nume_tb删除行之后重新编号后面的条目(您可以使用触发器) 。我们的想法是'idx'中的值始终是从1开始的连续数字,直到nume_tb中的总行数。然后,您将使用以下SELECT语句而不是使用LIMIT:
"SELECT id,texte FROM nume_tb_idx INNER JOIN nume_tb USING (id)
WHERE idx BETWEEN $offset AND ".$offset+$rowsperpage-1." ORDER BY idx"
这应该总是很快。
(2)您进行页面导航链接的方式非常无用。对于像您这样的大量页面,理想的解决方案是“对数”页面导航。有关说明和PHP示例代码,请参阅我对此问题的回答:
How to do page navigation for many, many pages? Logarithmic page navigation