limit m,n 的意義是在選擇、查詢得到的結果中,從第m條開始,拿連續的n條作為結果返回。根據它的原理可以知道,select ....limit m,n時要掃描得到的數據條數是m+n條。這就導致m特別大的時候,select執行完成會花費很多時間。但是limit大都數情況下又是必須要用的,因為limit可以讓數據庫只返回服務器真正需要的數據條數,減少了網絡中傳輸的開銷。現在有表table1:
id value type
-------------------------------------
1 100 0
2 2009 1
..... ...... ....
id為遞增主鍵,value建立了索引。這個表有幾百萬的數據,現在執行sql:
select * from table1 where value = 100 limit 300000,10
上述sql很簡單,人肉解析: 從為value建立的索引上找到300010條value=100的記錄的id,再拿這300010個id去主鍵索引找到對應的葉子結點,拿到每個id對應的數據返回。這種方式下,sql的執行非常耗時。
解決辦法1,使用索引覆蓋得到含300010條數據的子表,與原表做連接,再select:
select * from table1 A inner join ( select id from table1 where value = 100 limit 300000,10 ) as B on A.id = B.id
變化就是 select id from table1 where value = 100 limit 300000,10 在value的索引上查詢時只是把id查出來,由於這個索引上本來就有id,因此這時用到了索引覆蓋,速度非常快。在得到300010個id的后10個后,與原表用id做一個連接,再select * 即可完成原有的功能,速度非常快。
解決辦法2:有些情況可以 使用 limit n 代替 limit m,n
如果條件允許,即我們知道下一次查詢第一條數據的篩選條件,那就應該使用這個條件使得:where查出的數據第一條就是我們需要的,只需從第一條連續取n條即可:
select id from table1 where value = 100 and id > xxx and xxx .... limit 10
當然,上面的方法雖然是執行速度特別快,但只是在我們知道下一條數據的條件時才能做到。
總結就是:
1. 如果limit m, n時 m 特別大,select 的列又沒能用上索引覆蓋,就可以考慮先select某個列以用上索引覆蓋並把結果作為一個子表,再用原表與子表做連接,最后select出所有列。
2. 如果我們知道下一條我們需要的數據的查詢條件,就可以考慮用where語句時用上這個條件,然后使用 limit n 從第一條取連續的 n 條,避免查詢大量的無效行。
歸根結底就是:減少MySQL 從磁盤讀取數據頁的數量,InnoDB每個頁一般16KB。