優化分頁查詢
我們一般使用分頁都是使用limit來完成的,如果資料量小的話還可以,但是當資料量非常大的時候,不建立索引,通過全表查詢,將會非常耗時,效能將受到很大的影響。
第一種優化方式
在索引上完成排序分頁的操作,最後根據主鍵關聯回原表查詢所需要的其他列內容
例:我想對我之前的分頁進行優化,沒有優化前的sql語句
<select id="queryNewsByPage" resultType="news"> SELECT news_id, news_title, news_content FROM news <if test="start != null and size != null"> limit #{start}, #{size} </if> </select>
對其進行優化:
1)我首先在news_title上建立了一個索引
2)修改sql語句
修改以後:
<select id="queryNewsByPage" resultType="news"> SELECT a.news_id, a.news_title, a.news_content FROM news a INNER JOIN (SELECT news_id FROM news ORDER BY news_title <if test="start != null and size != null"> limit #{start}, #{size} </if> ) b ON a.news_id = b.news_id </select>
感覺自己是為了強行使用索引優化而改的,因為做了測試效能並沒有提升。也有可能是自己資料庫的資料量太少了,只有100行
第一種索引優化是利用了二級索引的特點,二級索引的葉子結點存放的是自定義索引+主鍵(這裡為news_title+news_id)先通過索引排序分頁,在索引上進行排序是很快的,其實根本就不用排了,索引是順序儲存的,然後再利用主鍵進行表關聯
通過explain檢視執行情況
優化前:select * from news order by news_title limit 10,5
mysql> explain select * from news order by news_title limit 10,5 \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: news partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 93 filtered: 100.00 Extra: Using filesort 1 row in set, 1 warning (0.00 sec)
優化後:select a.* from news a inner join (select news_id from news order by news_title limit 20,5) b using(news_id)、
mysql> explain select a.* from news a inner join (select news_id from news order by news_title limit 10,5) b using(news_id)\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 15
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: PRIMARY
table: a
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: b.news_id
rows: 1
filtered: 100.00
Extra: NULL
*************************** 3. row ***************************
id: 2
select_type: DERIVED
table: news
partitions: NULL
type: index
possible_keys: NULL
key: news_title
key_len: 767
ref: NULL
rows: 15
filtered: 100.00
Extra: Using index
3 rows in set, 1 warning (0.00 sec)
mysql> explain select * from news limit 10,5\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: news
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 93
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
優化前通過檔案排序,檔案排序是非常浪費時間和空間的,並且其是通過全表進行排序的,掃描的資料量非常多
優化後,使用索引排序,並且可以看到雖然子查詢表中是全表掃描,但是也做到了掃描儘可能的行
第二種優化方式
把limit查詢轉化成某個位置的查詢
在查詢的過程中需要記錄上一次查詢到的地方
在繼續對上面的例子進行優化
<select id="queryNewsByPageOptimization" resultType="news">
SELECT news_id, news_title, news_content
FROM news WHERE news_id > #{lastPageRecord} ORDER BY news_id limit #{size}
</select>
這種優化方式只適合排序欄位不會出現重複值的特定場景,如果排序欄位出現大量重複值,會造成分頁結果的丟失。
如果第二種方式可以使用的話,則第二種優化的方式比第一種的效率更高,通過explain,第二種type為range,而第一種type為index。range的效能要比index好。
mysql> explain select * from tt where id>5 order by id limit 5\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: tt
partitions: NULL
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 15
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)