問題: 我們經常會使用到分頁操作,這裡有個問題,在偏移量非常大的時候,它會導致MySQL掃描大量不需要的行然後再拋棄掉。如: 上述這條SQL語句需要查詢10020條記錄然後只返回最後20條。前面的10000條記錄都將被拋棄,這樣代價非常高。 方法一、延遲關聯 優化此類分類查詢的一個最簡單的辦法就是盡 ...
問題:
我們經常會使用到分頁操作,這裡有個問題,在偏移量非常大的時候,它會導致MySQL掃描大量不需要的行然後再拋棄掉。如:
SELECT id, name FROM A ORDER BY id DESC LIMIT 10000, 20;
上述這條SQL語句需要查詢10020條記錄然後只返回最後20條。前面的10000條記錄都將被拋棄,這樣代價非常高。
方法一、延遲關聯
優化此類分類查詢的一個最簡單的辦法就是儘可能地使用索引覆蓋掃描(如果一個索引包含(或者說覆蓋)所有需要查詢的欄位的值,我們就稱之為“覆蓋索引”。可以使用explain查看extra列信息,如果看到“Using index”的信息則說明使用到了覆蓋索引。),而不是查詢所有的列。然後根據需要做一次關聯操作再返回所需的列。對於偏移量很大的時候,這樣做的效率會提升非常大。
如上述SQL語句可以修改為:
SELECT id, name FROM A INNER JOIN ( SELECT id FROM A ORDER BY id DESC LIMIT 10000, 20 ) AS tmp USING(id);
這裡的“延遲關聯”將大大提升查詢效率,它讓MySQL掃描儘可能少的頁面,獲取需要訪問的記錄後再根據關聯列回原表查詢需要的所有列。
方法二、使用書簽記錄上次取數據的位置
可以使用書簽記錄上次取數據的位置,那麼下次就可以直接從該書簽記錄的位置開始掃描,這樣就可以避免使用OFFSET。
如上一次記錄到10000為止,則可以修改為:
SELECT id, name FROM A WHERE id < 10000 ORDER BY id DESC LIMIT 20;
這種方法的好處是無論翻頁到多麼後面,其性能都會很好。
方法三:使用between and
有時候也可以將LIMIT查詢轉換為已知位置的查詢,讓MySQL通過範圍掃描獲得到對應的結果。
如知道邊界值為10000,10020後上述語句可以修改為:
SELECT id , name FROM A WHERE id BETWEEN 10000 AND 10020 ORDER BY id DESC;
除上述方法外,還有一些其它方法,如:
- 使用預先計算的彙總表
- 關聯到一個冗餘表,冗餘表只包含主鍵列和需要做排序的數據列
- 使用Sphinx優化一些搜索操作