mysql巢狀分頁 MySQL分頁優化

2021-10-17 21:58:10 字數 2794 閱讀 3158

最近,幫同事重寫了乙個mysql sql語句,該sql語句涉及兩張表,其中一張表是字典表(需返回乙個字段),另一張表是業務表(本身就有150個字段,需全部返回),當然,欄位的個數是否合理在這裡不予評價。平時,返回的資料大概5w左右,系統尚能收到資料。但12月31日那天,資料量大概20w,導致sql執行時間過長,未能在規定的時間內反饋結果,於是系統直接報錯。

一般的思路是用mysql的分頁功能,即直接在原sql語句後面增加limit子句。但請注意,雖然你看到的反饋結果只是limit後面指定的數量,於是想當然的以為mysql只是檢索了指定數量的資料,然後給予返回。其實,mysql內部實現的原理是,檢索所有符合where條件的記錄,然後返回指定數量的記錄。從這個角度來看,直接在原sql語句後面新增limit子句只能說是一種可以實現功能的方案,但未必最優。

具體在本例中,首先我們來看一下150個字段的表的統計資訊:

一行大概就佔2k,而innodb預設頁的大小為16k,這意味著,乙個頁中最多可儲存8行的資料。隨機讀的可能性大大增加。而這無疑會對資料庫系統的io造成極大的壓力。

優化前如果採用上述方案,即直接在原sql語句後面增加limit子句,下面,我們來看看它的執**況。

首先,直接新增limit子句後的sql語句如下(已省略a1表的150個字段和a2中的乙個字段):

from upay_csys_scquery_txn_log_his a1  left join upay_csys_trans_code a2 on(a1.int_trans_code=a2.trans_code) where status<>'00' and settle_date=20151230 limit 50000,10000;

其執行時間如下:

大概執行了32s,絕大部分都花費到sending data上了。sending data指的是伺服器檢索資料,讀取資料,並將資料返回給客戶端的時間。

關於上述執行結果,有以下幾點需要說明:

1. 這是sql語句多次執行後的結果,這樣就可以排除結果快取的影響,事實上,每次查詢的時長都是32s左右。

2. 為什麼選用的是limit 50000,10000,而不是0,10000,這個主要是考慮到對於limit子句來說,越到後面,分頁的成本越高。基於此,選擇了中間值來作為分頁的結果。

該語句的執行計畫如下:

優化後:

優化的思路:

只對該錶的主鍵進行分頁,然後用返回的主鍵作為子查詢的結果,來檢索該錶其它欄位的值。

改寫後的sql語句如下:

from upay_csys_scquery_txn_log_his a1  left join upay_csys_trans_code a2 on(a1.int_trans_code=a2.trans_code) where seq_id in (select seq_id from (select seq_id from upay_csys_scquery_txn_log_his a1  where status<>'00' and settle_date=20151230 order by 1 limit 50000,10000) as t);

其執行時間如下:

大概3s多,比第一種方案快了差不多10倍,效果顯著。

下面來看看其執行計畫(explain extended)

總結:1. 改寫後的語句原本如下:

from upay_csys_scquery_txn_log_his a1  left join upay_csys_trans_code a2 on(a1.int_trans_code=a2.trans_code) where seq_id in (select seq_id from upay_csys_scquery_txn_log_his a1  where status<>'00' and settle_date=20151230 order by 1 limit 50000,10000);

但mysql報以下錯誤:

error 1235 (42000): this version of mysql doesn't yet support 'limit & in/all/any/some subquery'

需再增加乙個巢狀子查詢,

比如這樣的語句是不能正確執行的。

select * from table where id in (select id from table limit 12);

但是,只要你再加一層就行。如:

select * from table where id in (select t.id from (select * from table limit 12)as t)

這樣就可以繞開limit子查詢的問題。

問題解決。

2. 如果想檢視mysql查詢優化器等價改寫後的sql語句,可首先通過explain extended得到具體的執行計畫,然後通過show warnings檢視。

具體在本例中,等價改寫後的sql語句如下:

與設想中的執行順序一致~

3. 如何檢視mysql語句各步驟的執行時間。

MySQL 優化巢狀查詢和分頁查詢

巢狀查詢 子查詢 可以使用select語句來建立乙個單列的查詢結果,然後把這個結果作為過濾條件用在另乙個查詢中。巢狀查詢寫起來簡單,也容易理解。但是,有時候可以被更有效率的連線 join 替代。現在假如要找出從來沒有在 中消費的客戶,也就是查詢在客戶customer表中但是不在支付payment表中...

MySQL 優化巢狀查詢和分頁查詢

巢狀查詢 子查詢 可以使用select語句來建立乙個單列的查詢結果,然後把這個結果作為過濾條件用在另乙個查詢中。巢狀查詢寫起來簡單,也容易理解。但是,有時候可以被更有效率的連線 join 替代。現在假如要找出從來沒有在 中消費的客戶,也就是查詢在客戶customer表中但是不在支付payment表中...

mysql分頁概念 MySQL 分頁

分頁的基本原理 mysql explain select from message order by id desc limit 10000,20 1.row id 1 select type table message type index possible keys null key prima...