1. 程式人生 > >優化分頁查詢

優化分頁查詢

我們一般使用分頁都是使用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)