問題描述
在MySQL中,LIMIT OFFSET偏移量特別大時,效率會非常低,要設法避免或者在覆蓋索引上便宜
如果說LIMIT 1000,10,一個偏移量很小的值,一般是沒有問題的。但是,比如說,LIMIT 10000000,10時就有些費勁了,讓你等到花兒都謝了是沒有問題的。
原因分析:
究其原因,是因為MySQL的查詢並非先跳過10000000條,再查詢10條,而是先查詢再跳過。所以上面例子,要先查詢出10000000行之後,再取10條,速度當然很慢,並且跳過的行數越多,會越慢。
解決方案:
方法A,使用索引
可以使用 id > 10000000的方式誘導MySQL使用主鍵索引。
這樣的Where語句MySQL是非常喜歡並且樂於執行的,因為你給到MySQL的是一個範圍,它最容易執行的就是有序和範圍的查詢,這對它來說易如反掌。
當然,使用這個方法需要注意,id最好是連續的,中間的記錄沒有被物理刪除過。如果其中有資料被物理刪除過,用在分頁場景的話,就看起來不是那麼的優雅。當然,大部分資料庫的參與者都傾向於邏輯刪除。即便是物理刪除的話,也可以使用程式去做區分顯示,這不是問題。
方法B,迂迴戰術-延遲關聯技巧
這個方法在《高效能MySQL》中也有提到,所謂延遲關聯(deferred join),就是透過使用覆蓋索引查詢返回需要的主鍵,再根據主鍵關聯原表獲得需要的資料。原理還是想方設法利用索引的速度,方可以柔克剛。
假設,有一張表儲存了學生的名字,然後要執行這樣的sql:
select id,name from student limit 1000000,10;
這樣執行的效率必然會很慢
將SQL改寫:
SELECT id,name FROM student INNER JOIN (SELECT id FROM student LIMIT 1000000,10) stu ON student.id = stu.id;
可以看到,在上述語句的子查詢中,“SELECT id FROM student LIMIT 1000000,10”只查詢了主鍵id一個欄位,對於這樣的索引覆蓋情況,查詢速度還是可以接受的。就等於說,該子查詢只返回了10個id給上級查詢。
上級查詢接收到這10個id之後,迅速的查詢出了“name”欄位,速度得到了極大提升。