select * from table LIMIT 5,10; #返回第6-15行數據 mysql
select * from table LIMIT 5; #返回前5行 sql
select * from table LIMIT 0,5; #返回前5行 數據庫
性能優化:瀏覽器
[sql] view plaincopyprint?緩存
基於MySQL5.0中limit的高性能,我對數據分頁也從新有了新的認識. 性能優化
1. 架構
Select * From cyclopedia Where ID>=( 性能
Select Max(ID) From ( 大數據
Select ID From cyclopedia Order By ID limit 90001 優化
) As tmp
) limit 100;
2.
Select * From cyclopedia Where ID>=(
Select Max(ID) From (
Select ID From cyclopedia Order By ID limit 90000,1
) As tmp
) limit 100;
一樣是取90000條後100條記錄,第1句快仍是第2句快?
第1句是先取了前90001條記錄,取其中最大一個ID值做爲起始標識,而後利用它能夠快速定位下100條記錄
第2句擇是僅僅取90000條記錄後1條,而後取ID值做起始標識定位下100條記錄
第1句執行結果.100 rows in set (0.23) sec
第2句執行結果.100 rows in set (0.19) sec
很明顯第2句勝出.看來limit好像並不徹底像我以前想象的那樣作全表掃描返回limit offset+length條記錄,這樣看來limit比起MS-SQL的Top性能仍是要提升很多的.
其實第2句徹底能夠簡化成
Select * From cyclopedia Where ID>=(
Select ID From cyclopedia limit 90000,1
)limit 100;
直接利用第90000條記錄的ID,不用通過Max運算,這樣作理論上效率因該高一些,但在實際使用中幾乎看不到效果,由於自己定位ID返回的就是1條記錄,Max幾乎不用運做就能獲得結果,但這樣寫更清淅明朗,省去了畫蛇那一足.
但是,既然MySQL有limit能夠直接控制取出記錄的位置,爲何不乾脆用Select * From cyclopedia limit 90000,1呢?豈不更簡潔?
這樣想就錯了,試了就知道,結果是:1 row in set (8.88) sec,怎麼樣,夠嚇人的吧,讓我想起了昨天在4.1中比這還有過之的"高分".Select * 最好不要隨便用,要本着用什麼,選什麼的原則, Select的字段越多,字段數據量越大,速度就越慢. 上面2種分頁方式哪一種都比單寫這1句強多了,雖然看起來好像查詢的次數更多一些,但其實是以較小的代價換取了高效的性能,是很是值得的.
LIMIT偏移量越大,從磁盤IO讀取的記錄行數就越多,因此要儘量少的從磁盤IO讀取數據,總的來講有如下幾種方式:
1.子查詢優化法
先找出第一條數據,而後大於等於這條數據的id就是要獲取的數據
缺點:數據必須是連續的,能夠說不能有where條件,where條件會篩選數據,致使數據失去連續性
2.倒排表優化法
倒排表法相似創建索引,用一張表來維護頁數,而後經過高效的鏈接獲得數據
缺點:只適合數據數固定的狀況,數據不能刪除,維護頁表困難
3.反向查找優化法
當偏移超過一半記錄數的時候,先用排序,這樣偏移就反轉了
缺點:order by優化比較麻煩,要增長索引,索引影響數據的修改效率,而且要知道總記錄數,偏移大於數據的一半
4.limit限制優化法
把limit偏移量限制低於某個數。。超過這個數等於沒數據,我記得alibaba的dba說過他們是這樣作的
總結:limit的優化限制都比較多,因此實際狀況用或者不用只能具體狀況具體分析了。頁數那麼後,基本不多人看的。。。
====================================================================================================================================
好久之前讀了一篇關於分頁的文章,後來越想越有道理,最近又從新找出來,並作了翻譯,原文參考:Four ways to optimize paginated displays.
翻譯背景:在大數據量的狀況下,本來很簡單的分頁若是沒有處理好,你會發現分頁的請求會消耗你大量的數據庫時間。若是你遇到了這個問題,文章給了你幾個很好的解決的方案。固然,初學者若能看完這篇文章,那麼它會指導你寫出更具備擴展性的分頁代碼。
全文概述:文中提到了分頁的辦法總結以下:
所有緩存查詢結果。把查詢結果所有緩存起來(例如文件緩存、靜態化結果頁面等)。
不詳細顯示總共有多少分頁。這裏有兩個優化的技巧。其一每次在計算總條目的時候,我就固定查詢501條,而後將前500條分頁顯示好,若是第501條確實存在,那麼給出按鈕 「查看更多...」(這種狀況會不多)。其二,在每次列表本頁面的時候,好比第一頁我要顯示1-20條,那麼我查詢出1-21條。若是第21條真的存在,我就給出"下一頁"按鈕,依次類推。
事實上google就是這樣作的。在查看第一頁搜索結果的時候google只會顯示前十頁(共100個條目),並不顯示搜索結果條目總共有多少:
查看第二頁的時候,僅僅會多顯示一頁
經過EXPLAIN的"row"列來估算結果總共有多少條目。文章中稱google是這樣估算結果集的:
全文譯文:
在實際開發中,分頁顯示是咱們最常遇到的優化問題之一。例如搜索結果、積分列表、排行榜等。分頁的通常模型:在一個排序的結果集合(較大)中咱們要顯示其中連續20條目;而且須要顯示 「下一頁」、」上一頁」的連接;有時候咱們還須要顯示,總共有多少個條目,一共分了多少頁。
要給出這樣一個完成顯示,數據庫的代價是很大的,有時候就爲了顯示這麼一個分頁,須要執行的SQL會比整個頁面顯示其餘的所有SQL消耗還要大。
我曾遇到這樣的案例:有一次在爲咱們的一個客戶作Slow Query LOG分析的時候咱們就發現:整個LOG 裏面的SQL耗時6300s,其中兩個主要的分頁查詢大約消耗了(2850 + 380)秒,佔了整個Slow Query的50%。
分頁沒有處理好就是這麼糟糕~.
咱們來分析一下分頁的通常狀況:
#典型分頁的SQL以下:
SELECT .... FROM ... ORDER BY .... LIMIT X, 20
若是ORDER BY部分沒有可以用索引的話(這樣的狀況仍是不少的),MYSQL就會作文件排序(filesort);假想若是若是知足WHERE 條件的條目共有個百萬的數量級的話,那麼MYSQL就會取出這上百萬的結果,臨時存儲、文件排序,而後再刪除大大部分的數據保留其中的20個。當用戶點擊「下一頁」的時候,上面的過程會徹底重作一遍,只是取得結果向後偏了一點。要是你還想顯示「總共有多少條目,共分多少頁面」的話,通常是這樣作(1)使用SQL_CALC_FOUND_ROWS (2)執行一個單獨的SQL去計算行數。若是用戶的每一次請求都執行以上的操做,能夠想象當你的數據量愈來愈大的時候,狀況會愈來愈糟。
事實上,有不少辦法去優化上面的過程的。(關於這一點我以前我寫過的一篇article on optimizing ranked data 。不過那篇文章裏面介紹的辦法實施起來比較困難。因此若是不是狀況複雜和重要到必定程度,就不值得那樣作。)那通常狀況怎麼辦呢?除了索引、重組數據、SQL優化,咱們還有兩個大的方面能夠考慮去作。其一,積極的把SQL的查詢結果緩存起來,從而減小SQL執行;其二就是從新考慮一下你的分頁就架構,在應用中,並非每次都須要把分頁的各個部分都完整顯示出來的。例如你把從第1到50頁的連接都給出來,不少時候用戶根本不會直接去點擊某一頁。咱們考慮的思路是指把最重要的部分先展現出來。
這樣考慮的因而就有了下面四個優化的建議來提升性能
首次查詢的時候緩存結果。這樣狀況就變得簡單了,不管是結果條目的數量,總共的頁面數量,仍是取出其中的部分條目。
不顯示總共有多少條目。Google搜索結果的分頁顯示就用了這個特性。不少時候你可能看了前幾頁,就夠了。那麼我能夠這樣,每次我都把結果限制在500條(這個數據越大 資源消耗越大)而後你每次查詢的時候,都查詢501條記錄,這樣,若是結果然有501個,那麼咱們就顯示連接 「顯示下500條記錄」。
不顯示總頁面數。只給出「下一頁」的連接,若是有下一頁的話。(若是用戶想看上一頁的話,他會經過瀏覽器來回到上一頁的)。那你可能會問我「不顯示總頁面數」怎麼知道是否是有下一頁呢?這裏有一個很好的小技巧:你在每次顯示你當前頁面條目的時候你都多查詢一條,例如你要顯示第11-20個條目時,你就取出11-21條記錄(多取一條,並不顯示這多取的內容),那麼當你發現第21條存在的時候就顯示「下一頁的連接」,不然就是末頁了。這樣你就不用每次計算總頁面數量了,特別是在作緩存很困難的時候這樣作效率很是好。
估算總結果數。Google就是這麼作的,事實證實效果很好。用EXPLAIN 來解釋你的SQL,而後經過EXPLAIN的結果來估算。EXPLAIN結果有一列」row」會給你一個大概的結果。(這個辦法不是到處都行,可是某些地方效果是很好的)這些辦法能夠很大程度上減輕數據庫的壓力,並且對用戶體驗不會有什麼影響。
這些辦法能夠很大程度上減輕數據庫的壓力,並且對用戶體驗不會有什麼影響。
在系統中須要進行分頁操做的時候,咱們一般會使用LIMIT加上偏移量的辦法實現,同時加上合適的ORDER BY子句。若是有對應的索引,一般效率會不錯,不然,MySQL須要作大量的文件排序操做。
一個很是常見又使人頭疼的問題就是,在偏移量很是大的時候,例如多是LIMIT 10000,20這樣的查詢,這時MySQL須要查詢10020條記錄後只返回最後20條,前面10000條記錄都將被拋棄,這樣的代價很是高。若是全部的頁面被訪問的頻率都相同,那麼這樣的查詢平均須要訪問半個表的數據。要優化這種查詢,要麼是在頁面中限制分頁的數量,要麼是優化大偏移量的性能。
優化此類分頁查詢的一個最簡單的辦法就是儘量地使用索引覆蓋掃描,而不是查詢全部的列。而後根據須要作一次關聯操做再返回所需的列。對於偏移量很大的時候,這樣的效率會提高很是大。考慮下面的查詢:
SELECT film_id, description FROM sakila.film ORDER BY title LIMIT 50, 5;
若是這個表很是大,那麼這個查詢最好改寫成下面的這樣子:
SELECT film.film_id, film.description FROM sakila.film
INNER JOIN (
SELECT film_id FROM sakila.film ORDER BY title LIMIT 50,5
) AS lim USING(film_id);
這裏的「延遲關聯」將大大提高查詢效率,它讓MySQL掃描儘量少的頁面,獲取須要訪問的記錄後再根據關聯列回原表查詢須要的全部列。這個技術也能夠用於優化關聯查詢中的LIMIT子句。
有時候也能夠將LIMIT查詢轉換爲已知的位置的查詢,讓MySQL經過範圍掃描得到到對應的結果。例如,若是在一個位置列上有索引,而且預先計算出了邊界值,上面的查詢就能夠改寫爲:
SELECT film_id, description FROM sakila.film
WHERE position BETWEEN 50 AND 54 ORDER BY position;
對數據進行排名的問題也與此相似,但每每還會同時和GROUP BY混合使用。在這種狀況下一般都須要預先計算並存儲排名信息。
LIMIT和OFFSET的問題,實際上是OFFSET的問題,它會致使MySQL掃描大量不須要的行而後再拋棄掉。若是可使用書籤記錄上次取數據的位置,那麼下次就能夠直接從該書籤記錄的位置開始掃描,這樣就能夠避免使用OFFSET。例如,若須要按照租借記錄作翻頁,那麼能夠根據最新一條租借記錄向後追溯,這種作法可行是由於租借記錄的主鍵是單調增加的。首先使用下面的查詢得到一組結果:
SELECT * FROM sakila.rental ORDER BY rental_id DESC LIMIT 20;
假設上面的查詢返回的是主鍵爲16049到16030的租借記錄,那麼下一頁查詢就能夠從16030這個點開始:
SELECT * FROM sakila.rental WHERE rental_id < 16030
ORDER BY rental_id DESC LIMIT 20;
該技術的好處是不管翻頁到那麼後面,其性能都會很好。
其餘優化辦法還包括使用預先計算的彙總表,或者關聯到一個冗餘表,冗餘表只包含主鍵列和須要作排序的數據列。
分頁的時候,另外一個經常使用的技巧是在LIMIT語句中加上SQL_CALC_FOUNT_ROWS提示(hint),這樣就能夠得到去掉LIMIT之後知足條件的行數,所以能夠做爲分頁的總數。看起來,MySQL作了一些很是「高深」的優化,像是經過某種方法預測了總行數。但實際上,MySQL只有在掃描了全部知足條件的行之後,纔會知道行數,因此加上這個提示之後,無論是否須要,MySQL都會掃描全部知足條件的行,而後再拋棄掉不須要的行,而不是在知足LIMIT的行數後就終止掃描。因此該提示的代價可能很是高。
一個更好的設計是將具體的頁數換成「下一頁」按鈕,假設每頁顯示20條記錄,那麼咱們每次查詢的時候都是LIMIT返回21條記錄並只顯示20條,若是第21條存在,那麼咱們就顯示「下一頁」按鈕,不然就說明沒有更多的數據,也就無須顯示「下一頁」按鈕了。
另外一種作法是先獲取並緩存較多的數據,例如,緩存1000條,而後每次分頁都從這個緩存中獲取。這樣作可讓應用程序根據結果集的大小採起不一樣的策略,若是結果集少於1000,就能夠在頁面上顯示全部的分頁連接,由於數據都在緩存中,因此這樣作性能不會有問題。若是結果集大於1000,則能夠在頁面上設計一個額外的「找到的結果多於1000條」之類的按鈕。這兩種策略都比每次生成所有結果集再拋棄掉不須要的數據的效率要高不少。
有時候也能夠考慮使用EXPLAIN的結果中的rows列的值來做爲結果集總數的近似值(實際上Google的搜索結果總數也是個近似值)。當須要精確結果的時候,再單獨使用COUNT(*)來知足需求,這時若是可以使用索引覆蓋掃描則一般也會比SQL_CALC_FOUND_ROWS快得多。