【问题标题】:Php&mysql pagination script for huge website大型网站的php&mysql分页脚本
【发布时间】:2011-12-26 08:07:41
【问题描述】:

我有一个超过 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'>&lt;&lt;</a> &nbsp; ";  
  // obtine nr. pagina din urma 
  $prevpage = $currentpage - 1;  
  // arata < pt. link la o pagina in urma 
  echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$prevpage'>&lt;</a> &nbsp;";  
} 

// 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 "&nbsp; <a href='{$_SERVER['PHP_SELF']}?currentpage=$nextpage'>&gt;</a> ";  
  //  arata >> pt. ultima pagina
  echo " &nbsp; <a href='{$_SERVER['PHP_SELF']}?currentpage=$totalpages'>&gt;&gt;</a> ";  
}
?>

【问题讨论】:

  • 请阅读常见问题解答,您会在右上角找到搜索框。
  • 其实这是一个很好的问题。
  • 那么,谁能帮帮我?
  • 我认为您需要考虑您的用例。看看谷歌它的“测试”搜索结果在 830 个结果之后结束。我的观点很简单,当你点击 1000 页时,告诉用户改进他们的结果……如果有人真的浏览了 1000 页,那简直就是糟糕的生意。
  • 我没有将其用作搜索结果页面。我想让我的网页更容易被索引。

标签: php mysql database optimization pagination


【解决方案1】:

由于您使用的是 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】:

下次发布新问题之前,请使用搜索功能和相关问题小部件。

大的 LIMIT 问题:

当心大的 LIMIT 如果您需要前几行,则使用索引进行排序是有效的,即使发生了一些额外的过滤,因此您需要按索引扫描更多行,然后由 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)

【讨论】:

  • 虽然这是他应该记住的事情,但它与LIMIT 没有严格的关系;问题在于必须查看数据集的大小才能生成结果。对于分页,LIMIT 工作正常,并且确实是最好的方法。如果效率是一个问题,则应应用更窄的搜索条件。任何动态拉取一系列数据条目的可能方法都将遇到与LIMIT 完全相同的问题,因为它必须生成直到指定范围末尾的整个结果集。
【解决方案3】:

一些小事:

  • 您似乎只在每一行中使用了“id”和“texte”。如果只有这两列,在这种情况下使用 SELECT * 很好,但如果还有其他列,那就太浪费了。如果是这种情况,请改用SELECT id,texte

  • 1234563同样,对于检索行数:list($numrows) = mysql_fetch_row($result); 将起作用。
  • 您可以从所有 href 中删除 {$_SERVER['PHP_SELF']}。 "?currentpage=6" 是一个完全有效的(相对)href。对于较短的 html,请考虑使用 'p' 而不是 'currentpage' 作为查询字符串变量。


现在还有几个主要的:


(1) 就我所见,速度问题绝对是 MySQL 的问题 - 您的代码中没有大循环。

尝试 (a) 将 'id' 设为主键(如果还没有;或者如果您已经有另一个主键列,则将其设为索引),以及 (b) 将 ORDER BY id 子句添加到获取数据的选择语句。这肯定会有所帮助。

如果没有,作为最后的手段你可以创建第二个表来索引你的行:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 1970-01-01
    • 2013-10-14
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多