iT邦幫忙

0

[php mysql]資料庫 換頁概念+效能

各位好:

目前想到的換頁(pagination)方式流程大概如下:
(PHP+MYSQL)

1.第一次query先撈出總筆數,並做好相關分頁計算,也準備將這些值帶入下一次的limit設定中
2.第二次query取出所有資料(limit筆數)

我是有聽過最好減少資料庫query的次數,盡量縮減成一次query就好?
尤其是在千人存取的環境下...

問題一:所以還有更好的query方式嗎?

問題二:我要來轉成PDO方式存取,概念是否雷同?

SUDO程式碼:

$smt="from test where name='john' ";

$query = "SELECT COUNT(*) as num $smt ";  //第一次query
$result = mysql_query($query, $connection);
$query_data = mysql_fetch_row($result);
資料總筆數 = $query_data[0];  //取出SQL條件限制下的總筆數

....進行分頁所需計算:如算出頁數等....
略


//第二次query
$query = "SELECT tname FROM $smt limit 位置,10";

$result = mysql_query($query, $connection);
while($row=mysql_fetch_array($result)){
   //loop
}
看更多先前的討論...收起先前的討論...
應該會有 where 條件,不會將整個table john都到出來吧?

效能是需要靠正確的 key值設定,沒有 key值,你下的query 就會做
Full table scan,除非你的資料量接近所有table資料,否則建議
做好 key值。
fillano iT邦超人 1 級 ‧ 2013-04-10 12:03:46 檢舉
where就在$smt裡面吧?
喔,我漏看了!
player iT邦大師 1 級 ‧ 2013-04-10 17:37:31 檢舉
你會用MySQL的預存程序嗎?
如果會的話
可以只下一次查詢去跑預存程序 (傳查詢條件, 排序條件, 第幾頁, 每頁筆數)
然後輸出2個查詢的資料表
1個是已分頁的資料1頁
另一個是總資料筆數, 總頁數, ...等
isthome iT邦新手 4 級 ‧ 2013-04-11 09:48:03 檢舉
預存程序!!...
願聞其詳~

我也先來找尋相關資料~感謝

1 個回答

12
wiseguy
iT邦超人 1 級 ‧ 2013-04-10 14:16:58
最佳解答

兩次 query 是分頁 general case 必要的成本。
不過因為第一次只是在算次數,假如索引設好的話,第一次 query 的成本是蠻低的。
另外,如果這個分頁是屬於《非個人化》,也就是一堆人上來所看到的分頁內容其實是同一頁,那就可以考慮使用暫存,例如下面的虛擬碼:

<pre class="c" name="code">if (暫存庫已經有這一頁資料,且未過期)
{
	直接由暫存庫取出頁面資料輸出
}
else
{
	由資料庫查詢 // 這個就是你現在寫的程式碼
	存到暫存庫中一段時間
	輸出頁面資料
}

但假如每個人上來看的頁面會依身份不一樣,比如你這例子有 where name='john' 可能是只看自己的資料,那就沒法暫存,或者暫存只能供自己看 (因為可能有 user 會在前後頁反覆翻來翻去,就不用每一次都向資料庫查詢)。

使用暫存,要注意的是暫存時間。就看這頁面資料變化的頻率而定了。

看更多先前的回應...收起先前的回應...
isthome iT邦新手 4 級 ‧ 2013-04-10 20:58:48 檢舉

兩次 query 是分頁 general case 必要的成本。

ok!!那我大概知道了,
換頁主要是還是先透過(1)查出總筆數,框出所需的範圍後,以及產生供相對應的頁數等資料使用。
(2)在指定的範圍下(limit)在query一次。

看來是無可避免,以及應該是在可承受的範圍內。

另外wiseguy兄提到的,
我之前也有在實作。
將資料內容取出後存成文字檔緩存,避免每次都從mysql資料庫開啟撈資料,在緩存時間內直接從文字檔開啟(include進來)
假設{緩存更新時間}設定5秒,則在5秒內(未過時)的讀取內容都可以從文字檔載入,
若判斷到文字檔的本身檔案時間超過現在時間,則從mysql撈資料存到文字檔緩存。

if (file_exists($cachefile) && (filemtime($cachefile)+500)>time() ) {
include($cachefile);
exit;
}
ob_start();

//這邊要放資料輸出的文字,例如從mysql資料讀取
echo <<<EOD

echo "緩存時間:" . time();

//以上放資料輸出的文字

//以下為緩存更新檔案判斷
$fp = fopen($cachefile, 'w');
fwrite($fp, ob_get_contents());
fclose($fp);
ob_end_flush();

wiseguy iT邦超人 1 級 ‧ 2013-04-10 21:30:50 檢舉

isthome提到:
$fp = fopen($cachefile, 'w');
fwrite($fp, ob_get_contents());
fclose($fp);
ob_end_flush();

這四行可以簡化成一行:
file_put_contents($cachefile, ob_get_clean());

wiseguy iT邦超人 1 級 ‧ 2013-04-11 09:22:36 檢舉

isthome提到:
if (file_exists($cachefile) && (filemtime($cachefile)+500)>time() ) {

這一行也可以優化成

&lt;pre class="c" name="code">if (($t = filemtime($cachefile)) !== FALSE && ($t+500)>(int)$_SERVER['REQUEST_TIME'] ) {
  1. 用 filemtime() 同時代替偵測檔案是否存在的 file_exists(),可以避免兩次硬碟 I/O。
  2. 用 $_SERVER['REQUEST_TIME'] 這個現成的 timestamp 可以避免再取一次時間。
isthome iT邦新手 4 級 ‧ 2013-04-11 09:58:16 檢舉

wiseguy兄:
發現若把四行簡化成一行:
file_put_contents($cachefile, ob_get_clean());

當程式要執行更新動作時,畫面會全空白....再來抓原因

wiseguy iT邦超人 1 級 ‧ 2013-04-11 22:28:54 檢舉

喔~ 不好意思,沒注意你最後下的是 ob_end_flush() 那應該改用
file_put_contents($cachefile, ob_get_flush());

我要發表回答

立即登入回答