天天看點

mysql語句性能分析案例

寫法不一樣而功能完全相同的兩條 SQL 的在性能方面的差異。

示例一

需求:取出某個 group(假設 id 為 100)下的使用者編号(id),使用者昵稱(nick_name)、使用者性别

( sexuality ) 、 用 戶 簽 名 ( sign ) 和 用 戶 生 日 ( birthday ) , 并 按 照 加 入 組 的 時 間

(user_group.gmt_create)來進行倒序排列,取出前 20 個。

解決方案一、

SELECT id,nick_name

FROM user,user_group

WHERE user_group.group_id = 1

and user_group.user_id = user.id

limit 100,20;

解決方案二、

SELECT user.id,user.nick_name

FROM (

SELECT user_id

FROM user_group

ORDER BY gmt_create desc

limit 100,20) t,user

WHERE t.user_id = user.id;

我們先來看看執行計劃:

sky@localhost : example 10:32:13> explain

-> SELECT id,nick_name

-> FROM user,user_group

-> WHERE user_group.group_id = 1

-> and user_group.user_id = user.id

-> ORDER BY user_group.gmt_create desc

-> limit 100,20\G

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: user_group

type: ref

possible_keys: user_group_uid_gid_ind,user_group_gid_ind

key: user_group_gid_ind

key_len: 4

ref: const

rows: 31156

Extra: Using where; Using filesort

*************************** 2. row ***************************

table: user

type: eq_ref

possible_keys: PRIMARY

key: PRIMARY

ref: example.user_group.user_id

rows: 1

Extra:

sky@localhost : example 10:32:20> explain

-> SELECT user.id,user.nick_name

-> FROM (

-> SELECT user_id

-> FROM user_group

-> ORDER BY gmt_create desc

-> limit 100,20) t,user

-> WHERE t.user_id = user.id\G

select_type: PRIMARY

table: <derived2>

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 20

ref: t.user_id

*************************** 3. row ***************************

id: 2

select_type: DERIVED

possible_keys: user_group_gid_ind

Extra: Using filesort

執行計劃對比分析:

解決方案一中的執行計劃顯示 MySQL 在對兩個參與 Join 的表都利用到了索引,user_group 表利用了

user_group_gid_ind 索 引 ( key: user_group_gid_ind ) , user 表 利 用 到 了 主 鍵 索 引 ( key:

PRIMARY),在參與 Join 前 MySQL 通過 Where 過濾後的結果集與 user 表進行 Join,最後通過排序取出

Join 後結果的“limit 100,20”條結果傳回。

解決方案二的 SQL 語句利用到了子查詢,是以執行計劃會稍微複雜一些,首先可以看到兩個表都和

解決方案 1 一樣都利用到了索引(所使用的索引也完全一樣),執行計劃顯示該子查詢以 user_group 為

驅動,也就是先通過 user_group 進行過濾并馬上進行這一論的結果集排序,也就取得了 SQL 中的

“limit 100,20”條結果,然後與 user 表進行 Join,得到相應的資料。這裡可能有人會懷疑在自查詢中

從 user_group 表所取得與 user 表參與 Join 的記錄條數并不是 20 條,而是整個 group_id=1 的所有結果。

那麼清大家看看該執行計劃中的第一行,該行内容就充分說明了在外層查詢中的所有的 20 條記錄全部被

傳回。

通過比較兩個解決方案的執行計劃,我們可以看到第一中解決方案中需要和 user 表參與 Join 的記錄

數 MySQL 通過統計資料估算出來是 31156,也就是通過 user_group 表傳回的所有滿足 group_id=1 的記錄

數(系統中的實際資料是 20000)。而第二種解決方案的執行計劃中,user 表參與 Join 的資料就隻有 20

條,兩者相差很大,通過本節最初的分析,我們認為第二中解決方案應該明顯優于第一種解決方案。

下面我們通過對比兩個解決覺方案的 SQL 實際執行的 profile 詳細資訊,來驗證我們上面的判斷。由

于 SQL 語句執行所消耗的最大兩部分資源就是 IO 和 CPU,是以這裡為了節約篇幅,僅列出 BLOCK IO 和 CPU

兩項 profile 資訊(Query Profiler 的詳細介紹将在後面章節中獨立介紹):

先打開 profiling 功能,然後分别執行兩個解決方案的 SQL 語句:

sky@localhost : example 10:46:43> set profiling = 1;

Query OK, 0 rows affected (0.00 sec)

sky@localhost : example 10:46:50> SELECT id,nick_name

-> limit 100,20;

+--------+-----------+

| id | nick_name |

| 990101 | 990101 |

| 990102 | 990102 |

| 990103 | 990103 |

| 990104 | 990104 |

| 990105 | 990105 |

| 990106 | 990106 |

| 990107 | 990107 |

| 990108 | 990108 |

| 990109 | 990109 |

| 990110 | 990110 |

| 990111 | 990111 |

| 990112 | 990112 |

| 990113 | 990113 |

| 990114 | 990114 |

| 990115 | 990115 |

| 990116 | 990116 |

| 990117 | 990117 |

| 990118 | 990118 |

| 990119 | 990119 |

| 990120 | 990120 |

20 rows in set (1.02 sec)

sky@localhost : example 10:46:58> SELECT user.id,user.nick_name

-> WHERE t.user_id = user.id;

20 rows in set (0.96 sec)

檢視系統中的 profile 資訊,剛剛執行的兩個 SQL 語句的執行 profile 資訊已經記錄下來了:

sky@localhost : example 10:47:07> show profiles\G

Query_ID: 1

Duration: 1.02367600

Query: SELECT id,nick_name

ORDER BY user_group.gmt_create desc

limit 100,20

Query_ID: 2

Duration: 0.96327800

Query: SELECT user.id,user.nick_name

WHERE t.user_id = user.id

2 rows in set (0.00 sec)

sky@localhost : example 10:47:34> SHOW profile CPU,BLOCK IO io FOR query 1;

mysql語句性能分析案例

16 rows in set (0.00 sec)

sky@localhost : example 10:47:40> SHOW profile CPU,BLOCK IO io FOR query 2;

mysql語句性能分析案例

我們先看看兩條 SQL 執行中的 IO 消耗,兩者差別就在于“Sorting result”,我們回

顧一下前面執行計劃的對比,兩個解決方案的排序過濾資料的時機不一樣,排序後需要取

得的資料量一個是 20000,一個是 20,正好和這裡的 profile 資訊吻合,第一種解決方案的

“Sorting result”的 IO 值是第二種解決方案的将近 500 倍。

然後再來看看 CPU 消耗,所有消耗中,消耗最大的也是“Sorting result”這一項,第

一個消耗多出的緣由和上面 IO 消耗差異是一樣的。

結論:

通過上面兩條功能完全相同的 SQL 語句的執行計劃分析,以及通過實際執行後的

profile 資料的驗證,都證明了第二種解決方案優于第一種解決方案。