構達人 2019-06-02 01:12:16
最近遇到了這麼乙個情況,資料庫裡面的資料由於長期的堆積,導致資料量不斷的上公升,而後台的系統每次進行分頁查詢的時候,效率都會降低很多。後來檢視了一下之後,發現此時的分頁原理主要是採用了傳統的物理分頁 limit n,m 的方式。
為了方便演示,我特意建立了以下幾張表進行例項演練:
表分別是商品表,使用者表,使用者選購商品記錄表:
goods user g_u
三張表的關係比較簡單,user的id和goods裡面的id合併生成關聯資料,儲存在了g_u裡面。三張資料庫表的設計如下所示:
這個模擬的應用場景非常簡單,使用者和商品之間的關係維持在了一對多的關聯中。為了方便進行後續的測試,我用jmeter批量建立了1900000條測試資料,模擬一次百萬級的資料查詢場景。假設現在需求裡面有這樣的乙個業務場景,需要我們對購買記錄表裡面的資料進行分頁查詢,那麼對於常規的分頁查詢操作,常人會想到的方式可能是通過下述的語句:
select * from g_u as gu order by id limit 1850000,100
複製**
測試一下發現,查詢的時間為:
當我們搜尋的資料越靠後邊的時候,搜尋的速度就會越低下,因此這個時候,適當的建立索引就顯得比較重要了。
首先我們來做一次explain的sql檢測,檢測結果為如下所示:
由於我們查詢的時候,使用的是根據主鍵索引id進行排序,因此查詢的時候key一項為primary。
select * from g_u where id >=(select id from g_u limit 1850000,1) order by id limit 100
複製**
此時查詢有了一些許的提公升,但是依舊查詢緩慢
通過explain執行計畫分析結果可見:
子查詢用到了索引,外部查詢用到了where的輔助索引
這個時候我們不妨可以試下通過利用主鍵id來提公升我們的查詢效率:
select * from g_u as gu where gu.id>($firstid+$pagesize*$pagesize) limit 100
複製**
查詢的時間一下子大大縮短了許多:
通過explain分析一下該sql:
這裡面,sql在執行的時候借助了主鍵索引的幫助,因此效率大大提公升了。
但是這個時候,可能你會有這麼乙個疑惑。如果說資料的索引不是連續的該如何處理分頁時候每頁資料的完整性和一致性?
這裡不妨可以試試另外的一種思路,通過建立一張第三方的表g_u_index表,將原本亂序的id儲存在g_u_index中,在g_u_index一表中,我們可以通過該錶有序的g_u_index.id來對應原本相應的無序的g_u.id。建表的sql語句如下所示:
create table `g_u_index` (
`id` int(11) not null auto_increment,
`index` int(11) default null,
primary key
(`id`),
unique key `idx_id_index` (`id`,`index`) using btree
) engine=innodb auto_increment=1900024 default charset=utf8 collate=utf8_unicode_ci;
複製**
ps: 可以為id和index兩者建立一套復合索引,提公升查詢的效率。
這裡我們需要保證一點就是,g_u表中插入的資料順序需要和g_u_index表中插入的順序是一致的。然後查詢分頁指定的index時候可以這麼來查:
select g_u_index.index from g_u_index where id=($firstid+$pagesize*$pagesize) limit 1
複製**
通過執行explain分析後,結果變成如下所示:
查詢時間為:0.001s
有了第三方表的幫助下,此時分頁的sql優化可以調整為以下這種方式:
select * from g_u as gu where gu.id>(
select g_u_index.index from g_u_index where id=($firstid+$pagesize*$pagesize) limit 1
) limit 100
複製**
通過構建了第三方表之後,資料的查詢時間一下子大大縮減了:
查詢的時候為了更加人性化,通常不需要顯示這些無意義的id,需要的是商品名稱和使用者姓名,假設我們還是只採用最原始的無第三方表的方式進行查詢的話,效率會比較底下:
select gu.id,goods.`name`,`user`.username from g_u as gu ,goods ,`user`
where goods.id=gu.g_id and `user`.id=gu.u_id
order by id limit 1500000,1000
複製**
結果:
因此如果借助了第三方表查詢的話,sql可以調整成下方這種型別:
select goods.`name`,`user`.username from g_u as gu ,goods ,`user`
where goods.id=gu.g_id and `user`.id=gu.u_id
and
gu.id>=(
select g_u_index.index from g_u_index where id=(9+1000*1900) limit 1
) limit 100
複製**
查詢的時間會大大減少:
通過explain執行計畫分析之後,結果如下:
在實際的業務場景中,一張原來就有上百萬資料的表要做出這樣的id拆分,並且同步到第三方表的確實不太容易,這裡推薦一種思路,可以借助阿里的中介軟體canal來實現對於資料庫日誌的訂閱,然後自定義進行資料的同步操作。
對於sql的優化需要結合實際的業務需求來開展,總的來說,這部分還是需要有一定的實戰演練才能變強。
1.資料量大的時候,應盡量避免全表掃瞄,應考慮在 where及 order by 涉及的列上建立索引,建索引可以大大加快資料的檢索速度。
2.適當的使用explain可以對sql進行相應的深入分析。
3.當只要一行資料時使用limit 1。
4.在使用索引字段作為條件時,如果該索引是復合索引,那麼必須使用到該索引中的第乙個字段作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應盡可能的讓字段順序與索引順序相一致。
5.不要在 where子句中的「=」左邊進行函式、算術運算或其他表示式運算,否則系統將可能無法正確使用索引。
6.適當的時候採用覆蓋索引可以提高查詢的效率。
寫在最後:
碼字不易看到最後了,那就點個關注唄,只收藏不點關注的都是在耍流氓!
MySQL百萬級資料分頁查詢優化方案
當需要從資料庫查詢的表有上萬條記錄的時候,一次性查詢所有結果會變得很慢,特別是隨著資料量的增加特別明顯,這時需要使用分頁查詢。對於資料庫分頁查詢,也有很多種方法和優化的點。下面簡單說一下我知道的一些方法。準備工作 為了對下面列舉的一些優化進行測試,下面針對已有的一張表進行說明。表名 order hi...
mysql百萬級資料分頁查詢緩慢優化 實戰
畫面是這樣的 大概是這樣的 select count id from 查出總數 select from limit m,n 查出分頁資料 和前端的互動是這樣的 你傳給我查詢條件和pagenum pagesize 我給你返回total resultlist 恩,可以開始預估開發時間了 面對這麼多的資料...
mysql百萬級資料分頁查詢緩慢優化方法
參考 1 修改需求 一 與產品商討 修改需求 畫面是這樣的 大概是這樣的select count id from 查出總數 select from limit m,n 查出分頁資料 和前端的互動是這樣的你傳給我查詢條件和pagenum pagesize 我給你返回total resultlist 恩...