mysql分頁查詢優化,大數據量優化

傳統的mysql分頁查詢

select * from table limit n , m
MySQL 執行此類SQL時需要先分頁(默認一頁1000條數據)通過全表掃描到N行,然後再去取M行。對於此類操作,獲取前面少數幾行數據會很快,但是隨着掃描的記錄數越多,SQL的性能就會越差,因爲N的值越大,MySQL需要掃描越多的數據來定位到具體的N行,這樣耗費大量的 IO 成本和時間成本。
特別是上線後數據量積累比較快,必須重視SQL優化,否則影響系統運行和用戶使用體驗
性能實驗

  1. 直接用limit start, count分頁語句, 也是我程序中用的方法:
select * from table limit start, count

當起始頁較小時,查詢沒有性能問題,我們分別看下從10, 100, 1000, 10000,
30000開始分頁的執行時間(每頁取10條)。
如下:

select * from table limit 10, 10   0.000秒
select * from table limit 100, 10   0.000秒
select * from table limit 1000, 10   0.005秒
select * from table limit 10000,10   0.038秒
select * from table limit 30000, 10   1.160

可以告訴大家,做性能試驗的表數據在30W+條,大家可以看出隨着查詢條數的增加,相對查詢所需時間也增加了,這說明分頁語句limit跟起始頁碼是有很大關係的,同樣大家也看到查詢起始頁小時,查詢時間爲0秒,爲什麼會出現這樣的情況?

這需要引入innoDB的緩存池概念

查看InnoDB緩存池大小:
show global variables like ‘innodb_buffer_pool_size’;
-------------------------±------------+
| Variable_name | Value |
±------------------------±------------+
| innodb_buffer_pool_size | 34359738368 |
±------------------------±------------+
默認InnoDB緩存池大小爲128M
InnoDB緩存池用來存儲各種數據的緩存,包括:
數據頁,插入緩存,自適應索引哈希,索引頁,鎖信息,數據字典信息等。
InnoDB緩存池會把查詢過的熱點數據資源加載到緩存池,只要加載過到緩存池的數據,再次查詢的時候直接從緩存池獲取而不需要通過mysql查詢,所以查詢時間會出現0秒的情況.
相關的緩存知識,會單獨開闢一篇博客

言歸正傳,在查詢數據起始頁越多的情況下,查詢效率越慢,我們從緩存池配置知道,通過增加緩存池大小,把經常查詢的數據加載到緩存池,可以起到優化查詢的作用,但是數據本身的查詢效率慢是否可以優化呢?

利用表的覆蓋索引來加速分頁查詢

查看錶索引:
show index from table;

+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| lv_seal |          0 | PRIMARY     |            1 | id          | A         |      318834 |     NULL | NULL   |      | BTREE      |         |               |
| lv_seal |          0 | seal_no     |            1 | seal_no     | A         |      322299 |     NULL | NULL   |      | BTREE      |         |               |
| lv_seal |          1 | name        |            1 | name        | A         |      292690 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik1 |            1 | uuid        | A         |      305924 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik2 |            1 | status      | A         |           9 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik3 |            1 | create_date | A         |      248352 |     NULL | NULL   | YES  | BTREE      |         |               |
| lv_seal |          1 | lv_seal_ik4 |            1 | update_date | A         |      245174 |     NULL | NULL   | YES  | BTREE      |         |               |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我們都知道,利用了索引查詢的語句中如果只包含了那個索引列(覆蓋索引),那麼這種情況會查詢很快。
因爲利用索引查找有優化算法,且數據就在查詢索引上面,不用再去找相關的數據地址了,這樣節省了很多時間。加上Mysql中的索引緩存,在併發高的時候利用緩存就效果更好了。
在我們的例子中,我們知道id字段是主鍵,自然就包含了默認的主鍵索引。現在讓我們看看利用覆蓋索引的查詢效果如何。
這次我們之間查詢最後一頁的數據(利用覆蓋索引,只包含id列),如下:

select id from table limit 300000,10   0.09

相對於查詢了所有列的1.16秒,提升了很多
那麼如果我們也要查詢所有列,有兩種方法,
方式一:d>=的形式,

SELECT * FROM table WHERE ID > =(select id from table limit 300000, 1) limit 10

查詢時間爲0.09秒!
方式二:通過內鏈接查詢

SELECT * FROM table a JOIN (select id from table limit 300000, 10) b ON a.ID = b.id

查詢時間0.10也很短!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章