提高M(jìn)ySQL深分頁查詢效率的三種方案
開發(fā)經(jīng)常遇到分頁查詢的需求,但是當(dāng)翻頁過多的時候,就會產(chǎn)生深分頁,導(dǎo)致查詢效率急劇下降。有沒有什么辦法,能解決深分頁的問題呢?本文總結(jié)了三種優(yōu)化方案,查詢效率直接提升10倍,一起學(xué)習(xí)一下。
開發(fā)經(jīng)常遇到分頁查詢的需求,但是當(dāng)翻頁過多的時候,就會產(chǎn)生深分頁,導(dǎo)致查詢效率急劇下降。
有沒有什么辦法,能解決深分頁的問題呢?
本文總結(jié)了三種優(yōu)化方案,查詢效率直接提升10倍,一起學(xué)習(xí)一下。
1. 準(zhǔn)備數(shù)據(jù)
先創(chuàng)建一張用戶表,只在create_time字段上加索引:
CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(255) DEFAULT NULL COMMENT '姓名', `create_time` timestamp NULL DEFAULT NULL COMMENT '創(chuàng)建時間', PRIMARY KEY (`id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB COMMENT='用戶表';
然后往用戶表中插入100萬條測試數(shù)據(jù),這里可以使用存儲過程:
drop PROCEDURE IF EXISTS insertData; DELIMITER $$ create procedure insertData() begin declare i int default 1; while i <= 100000 do INSERT into user (name,create_time) VALUES (CONCAT("name",i), now()); set i = i + 1; end while; end $$ call insertData() $$
2. 驗證深分頁問題
每頁10條,當(dāng)我們查詢第一頁的時候,速度很快:
select * from user where create_time>'2022-07-03' limit 0,10;
在不到0.01秒內(nèi)直接返回了,所以沒顯示出執(zhí)行時間。
當(dāng)我們翻到第10000頁的時候,查詢效率急劇下降:
select * from user where create_time>'2022-07-03' limit 100000,10;
執(zhí)行時間變成了0.16秒,性能至少下降了幾十倍。
耗時主要花在哪里了?
- 需要掃描前10條數(shù)據(jù),數(shù)據(jù)量較大,比較耗時
- create_time是非聚簇索引,需要先查詢出主鍵ID,再回表查詢,通過主鍵ID查詢出所有字段
畫一下回表查詢流程:
1. 先通過create_time查詢出主鍵ID
2. 再通過主鍵ID查詢出表中所有字段
別問為什么B+樹的結(jié)構(gòu)是這樣的?問就是規(guī)定。
可以看一下前兩篇文章。
然后我們就針對這兩個耗時原因進(jìn)行優(yōu)化。
3. 優(yōu)化查詢
3.1 使用子查詢
先用子查詢查出符合條件的主鍵,再用主鍵ID做條件查出所有字段。
select * from user where id in ( select id from user where create_time>'2022-07-03' limit 100000,10 );
不過這樣查詢會報錯,說是子查詢中不支持使用limit。
我們加一層子查詢嵌套,就可以了:
select * from user where id in ( select id from ( select id from user where create_time>'2022-07-03' limit 100000,10 ) as t );
執(zhí)行時間縮短到0.05秒,減少了0.12秒,相當(dāng)于查詢性能提升了3倍。
為什么先用子查詢查出符合條件的主鍵ID,就能縮短查詢時間呢?
我們用explain查看一下執(zhí)行計劃就明白了:
explain select * from user where id in ( select id from ( select id from user where create_time>'2022-07-03' limit 100000,10 ) as t );
可以看到Extra列顯示子查詢中用到Using index,表示用到了覆蓋索引,所以子查詢無需回表查詢,加快了查詢效率。
3.2 使用inner join關(guān)聯(lián)查詢
把子查詢的結(jié)果當(dāng)成一張臨時表,然后和原表進(jìn)行關(guān)聯(lián)查詢。
select * from user inner join ( select id from user where create_time>'2022-07-03' limit 100000,10 ) as t on user.id=t.id;
查詢性能跟使用子查詢一樣。
3.3 使用分頁游標(biāo)(推薦)
實現(xiàn)方式就是:當(dāng)我們查詢第二頁的時候,把第一頁的查詢結(jié)果放到第二頁的查詢條件中。
例如:首先查詢第一頁
select * from user where create_time>'2022-07-03' limit 10;
然后查詢第二頁,把第一頁的查詢結(jié)果放到第二頁查詢條件中:
select * from user where create_time>'2022-07-03' and id>10 limit 10;
這樣相當(dāng)于每次都是查詢第一頁,也就不存在深分頁的問題了,推薦使用。
執(zhí)行耗時是0秒,查詢性能直接提升了幾十倍。
這樣的查詢方式雖然好用,但是又帶來一個問題,就是跳轉(zhuǎn)到指定頁數(shù),只能一頁頁向下翻。
所以這種查詢只適合特定場景,比如資訊類APP的首頁。
互聯(lián)網(wǎng)APP一般采用瀑布流的形式,比如百度首頁、頭條首頁,都是一直向下滑動翻頁,并沒有跳轉(zhuǎn)到制定頁數(shù)的需求。
不信的話,可以看一下,這是頭條的瀑布流:
傳參中帶了上一頁的查詢結(jié)果。
響應(yīng)數(shù)據(jù)中,返回了下一頁查詢條件。
所以這種查詢方式的應(yīng)用場景還是挺廣的,趕快用起來吧。
知識點總結(jié):
到此這篇關(guān)于解決MySQL深分頁低效率問題的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
mysql 5.7.18 Installer安裝下載圖文教程
這篇文章主要為大家詳細(xì)介紹了mysql 5.7.18 Installer安裝下載圖文教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-09-09MySQL查看數(shù)據(jù)庫狀態(tài)命令詳細(xì)講解
在工作中,有時候我們需要了解MySQL服務(wù)器的狀態(tài)信息,下面這篇文章主要給大家介紹了關(guān)于MySQL查看數(shù)據(jù)庫狀態(tài)命令的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03MySQL窗口函數(shù)OVER使用示例詳細(xì)講解
這篇文章主要介紹了MySQL窗口函數(shù)OVER()用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01MySQL數(shù)據(jù)庫運維之?dāng)?shù)據(jù)恢復(fù)的方法
本篇文章主要介紹了MySQL數(shù)據(jù)庫運維之?dāng)?shù)據(jù)恢復(fù)的方法,此處總結(jié)一下恢復(fù)方案,并結(jié)合數(shù)據(jù)庫的二進(jìn)制日志做下數(shù)據(jù)恢復(fù)的示范。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06MySQL 兩張表數(shù)據(jù)合并的實現(xiàn)
本文主要介紹了MySQL 兩張表數(shù)據(jù)合并的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01