1、
-- 優化前SQL
SELECT 各種字段
FROM `table_name`
WHERE 各種條件
LIMIT 0,10;
2、
-- 優化後SQL
SELECT 各種字段
FROM `table_name` main_tale
RIGHT JOIN
(
SELECT 子查詢只查主鍵
FROM `table_name`
WHERE 各種條件
LIMIT 0,10;
) temp_table ON temp_table.主鍵 = main_table.主鍵
3、mysql版本5.7
mysql> desc test;
+--------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| val | int(10) unsigned | NO | MUL | 0 | |
| source | int(10) unsigned | NO | | 0 | |
+--------+---------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
id爲自增主鍵,val爲非唯一索引。灌入大量數據,共500萬:
mysql> select count(*) from test;
+----------+
| count(*) |
+----------+
| 5242882 |
+----------+
1 row in set (4.25 sec)
我們知道,當limit offset rows中的offset很大時,會出現效率問題:
mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id | val | source |
+---------+-----+--------+
| 3327622 | 4 | 4 |
| 3327632 | 4 | 4 |
| 3327642 | 4 | 4 |
| 3327652 | 4 | 4 |
| 3327662 | 4 | 4 |
+---------+-----+--------+
5 rows in set (15.98 sec)
爲了達到相同的目的,我們一般會改寫成如下語句:
mysql> select * from test a
inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id | val | source | id |
+---------+-----+--------+---------+
| 3327622 | 4 | 4 | 3327622 |
| 3327632 | 4 | 4 | 3327632 |
| 3327642 | 4 | 4 | 3327642 |
| 3327652 | 4 | 4 | 3327652 |
| 3327662 | 4 | 4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (0.38 sec)
時間相差很明顯。
爲什麼會出現上面的結果?我們看一下select * from test where val=4 limit 300000,5;的查詢過程:
查詢到索引葉子節點數據。根據葉子節點上的主鍵值去聚簇索引上查詢需要的全部字段值。
像上面這樣,需要查詢300005次索引節點,查詢300005次聚簇索引的數據,最後再將結果過濾掉前300000條,取出最後5條。MySQL耗費了大量隨機I/O在查詢聚簇索引的數據上,而有300000次隨機I/O查詢到的數據是不會出現在結果集當中的。
4、
SELECT t.*
FROM (
SELECT id
FROM myTable
ORDER BY
id
LIMIT 1000000, 30
) q
JOIN myTable t
ON t.id = q.id
- 子查詢只用到了索引列,沒有取實際的數據,所以不涉及到磁盤IO,所以即使是比較大的 offset,查詢速度也不會太差。