mysql在limit的offset效能會非常的差,在web實作的應用,常常是分頁會用到的。
實作的問題
假設資料庫的結構是這樣子...
postid, userid, user_posttime, post_title, post_content postid是primary key index建在 userid + userposttime (combine的)
列出使用者文章的sql是(最新的文章排前面,每頁10筆)
第一頁是... select * from table where userid='girvan' order by user_posttime desc limit 0,10
第二頁是...select * from table where userid='girvan' order by user_posttime desc limit 10,10
但是如果... 使用者的文章很多,然後第951頁的sql是...
select * from table where userid='girvan' order by user_posttime desc limit 9500,10
你將會在你的slow query裡面看到這一筆。
而且... 可怕的不是使用者會點選,而是搜尋引擎的機器人,機器人很有心... 會幫你每一頁都執行過。
我曾經去查過這項問題,結論是... 不要使用!那我們該怎麼做分頁?
後來由幾個地方得到靈感...
- blogger的部落格只有上一頁、下一頁
- Google的搜尋結果,只讓你看前20頁的結果
- Gmail裡面只有上一頁和下一頁
- 無名小站會限制使用者文章上限、留言總數
可能是offset的問題造成這類的限制。
解法
第一頁的查詢跟原本一樣。然而第一頁裡的第二頁的連結可能會改成...
index.php?time=155815154&p=2
time的時間是第1頁裡第10筆的時間
然而第二頁的sql查詢就會變成這樣子...
select * from table where userid='girvan' and user_posttime < 155815154 order by user_posttime desc limit 10;
因為index是建combined的,所以這類的查詢非常的快。以此類推,到第951頁的查詢,也會跟第二頁一樣快。
常見問題
在第3頁裡的第二頁連結的sql會變成這樣子... select * from table where userid='girvan' and user_posttime > 153514825 order by user_posttime asc limit 0,10; 然後搜尋的結果記得倒過來!(php可以用array_reverse() )
如果第10篇和第11篇的時間是一樣的,從第1頁到第2頁裡,第11篇會不見。(Google Blogger)就有這個問題。
其它的不多說,實作就會知道了。