1. 程式人生 > 實用技巧 >MySQL實驗 子查詢優化雙引數limit

MySQL實驗 子查詢優化雙引數limit

MySQL實驗 子查詢優化雙引數limit

沒想到雙引數limit還有優化的餘地,為了親眼見到,今天來親自實驗一下。

 

實驗準備

使用MySQL官方的大資料庫employees進行實驗,匯入該示例庫見此

準備使用其中的employees表,先檢視一下表結構和表內的記錄數量

mysql> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
mysql> select count(*) from employeed;
ERROR 1146 (42S02): Table 'employees.employeed' doesn't exist
mysql> select count(*) from employees;
+----------+
| count(*) |
+----------+
| 300024 |
+----------+
1 row in set (0.05 sec)

我們可以看到,只有主鍵emp_no有索引

 

實驗過程

MySQL5.7官網對Explain各項引數的解釋

explain引數5.7版本推薦參考部落格

老版本explain推薦參考部落格(即新版本預設explain extended)

關於explain引數的拓展連結

MySQL explain key值的解釋

 

使用未優化雙引數limit

執行一般情況下的雙引數limit並explain:

mysql> select * from employees limit 200000,10;
+--------+------------+------------+------------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+------------+--------+------------+
| 299976 | 1952-12-08 | Kristian | Kampfer | M | 1994-12-28 |
| 299977 | 1956-09-30 | Zsolt | Benveniste | M | 1994-08-15 |
| 299978 | 1956-08-08 | Anneli | Kitai | F | 1994-08-09 |
| 299979 | 1953-03-18 | Satoru | Kornyak | F | 1991-06-16 |
| 299980 | 1953-05-26 | Marsal | Lovengreen | M | 1988-05-09 |
| 299981 | 1960-06-22 | Claudi | Mamelak | M | 1986-07-13 |
| 299982 | 1955-06-21 | Juichirou | Hiraishi | M | 1989-12-17 |
| 299983 | 1964-11-19 | Bezalel | Iacovou | M | 1998-02-22 |
| 299984 | 1961-11-03 | Frazer | Birch | M | 1986-12-31 |
| 299985 | 1961-01-04 | Miomir | Nergos | F | 1996-07-07 |
+--------+------------+------------+------------+--------+------------+
10 rows in set (0.06 sec)
mysql> explain select * from employees limit 200000,10;
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
| 1 | SIMPLE | employees | NULL | ALL | NULL | NULL | NULL | NULL | 299468 | 100.00 | NULL |
+----+-------------+-----------+------------+------+---------------+------+---------+------+--------+----------+-------+
1 row in set, 1 warning (0.00 sec)

我們對explain進行分析:

  • type為ALL,全表掃描,也就是說沒有用索引,因此key和key_len都為NULL

  • 表之間沒有引用因此ref為NULL,這裡是單表查詢

  • partition為null說明沒有使用/訪問分割槽表

  • 掃描了299468行(limit不會過濾null和空值,為什麼是這個資料還望看官解答)

  • 這裡的filtered指,從儲存引擎經過server層過濾後剩下有N%的資料滿足查詢條件,100%表示未對行進行篩選 。

  • EXTRA額外解釋:Using filesort,排序時無法使用到索引時

說明雙引數limit就是在排序後一直掃描到偏移量的所指的地方(這裡是第100001行),然後讀取10行再扔掉前100000行。

 

子查詢優化limit

優化思路:先在子查詢中利用“覆蓋索引”的方式先找出要選取的第一行資料的主鍵值,然後再從這裡根據主鍵值選取10條資料

mysql> select * from employees where emp_no >= (select emp_no from employees limit 200000,1) limit 10;
+--------+------------+------------+------------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+------------+--------+------------+
| 299976 | 1952-12-08 | Kristian | Kampfer | M | 1994-12-28 |
| 299977 | 1956-09-30 | Zsolt | Benveniste | M | 1994-08-15 |
| 299978 | 1956-08-08 | Anneli | Kitai | F | 1994-08-09 |
| 299979 | 1953-03-18 | Satoru | Kornyak | F | 1991-06-16 |
| 299980 | 1953-05-26 | Marsal | Lovengreen | M | 1988-05-09 |
| 299981 | 1960-06-22 | Claudi | Mamelak | M | 1986-07-13 |
| 299982 | 1955-06-21 | Juichirou | Hiraishi | M | 1989-12-17 |
| 299983 | 1964-11-19 | Bezalel | Iacovou | M | 1998-02-22 |
| 299984 | 1961-11-03 | Frazer | Birch | M | 1986-12-31 |
| 299985 | 1961-01-04 | Miomir | Nergos | F | 1996-07-07 |
+--------+------------+------------+------------+--------+------------+
10 rows in set (0.03 sec)

可以看到,查詢速度提高了一倍

mysql> explain select * from employees where emp_no >= (select emp_no from employees limit 200000,1) limit 10;
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
| 1 | PRIMARY | employees | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 149734 | 100.00 | Using where |
| 2 | SUBQUERY | employees | NULL | index | NULL | PRIMARY | 4 | NULL | 299468 | 100.00 | Using index |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+--------+----------+-------------+
2 rows in set, 1 warning (0.03 sec)

分析explain:

  • 第二行subquery指的是子查詢,那麼我們先從子查詢看起

    type為index,說明我們使用了索引樹加速查詢

    key為primary key,說明我們使用了主鍵索引,子查詢直接在主鍵索引樹上進行了查詢,避免了回表,減少了磁碟I/O

  • 第一行則是外部的查詢

    type為range說明是範圍查詢,然後也使用了主鍵索引樹

    而Using index是指,僅使用索引樹中的資訊從表中檢索列資訊,而無需執行其他查詢即可讀取實際行。

 

小結

對於不需要order by的直接的雙引數limit,我們可以藉助覆蓋索引的方式優化查詢效率。

優化order by+limit見此處

 

最後的補充

僅對於雙引數limit的優化,除了子查詢外還有以下方法:

  • 倒排表優化法

    倒排表法類似建立索引,用一張表來維護頁數,然後通過高效的連線得到資料。缺點:只適合資料數固定的情況,資料不能刪除,維護頁表困難

  • 反向查詢優化法

    當偏移超過一半記錄數的時候,先用排序,這樣偏移就反轉了

    缺點:order by優化比較麻煩,要增加索引,索引影響資料的修改效率,並且要知道總記錄數

    偏移大於資料的一半

    limit偏移演演算法:

    正向查詢: (當前頁 – 1) * 頁長度

    反向查詢: 總記錄 – 當前頁 * 頁長度

有時間在進行實驗