my sql explain & select
TRANSCRIPT
MySQL 的 SELECT 優化與 EXPLAIN
Present by MingIn
1
Outline
• MySQL 查詢優化• MySQL Explain• MySQL Profiles• Xboard 範例• Conclusion
2
MySQL 查詢優化
• 目標:
– 了解 MySQL 如何執行查詢– 了解查詢高低效率的原因何在– 如何充分發揮 MySQL 的優勢,避開它的弱點
3
為什麼查詢的效能不佳?
• 典型效能低落案例– 1. 查詢不必要的紀錄– 2. 多表關聯查資料的時候 (join or subqueries) ,返回全部 rows 資
料– 3. 總是取出全部的列資料– 4. 一直重複查詢相同的資料數據
4
如何評估查詢的效能?
• 1. 指令回覆時間 = 服務執行時間 + 排隊等待時間• 2. 掃描的行數&返回的行數:以 10:1 ~ 1:1 為佳 • 3. 掃描的行數&查詢的類型 (explain:type & rows)
5
查詢速度由好到壞
• 1. where 條件使用在 primary key 上 (system)• 2. where 條件使用在 index 上 (type:using index)• 3. where 條件使用在一般資料上 (type:using where)
6
一個複雜的查詢 VS 多個簡單查詢? (1/2)
• 一個複雜的查詢
– SELECT post.* – FROM tag– JOIN tag_post ON tag_post.tag_id = tag.id– JOIN post ON tag_post.post_id = post.id– WHERE tag.tag = 'mysql';
• 多個簡單查詢
– SELECT * FROM tag WHERE tag = 'mysql';– SELECT * FROM tag_post WHERE tag_id = 123;– SELECT * FROM post WHERE post_id IN (123,456,789);
7
一個複雜的查詢 VS 多個簡單查詢? (2/2)
• 分解的好處– 1. 提高指令在查詢暫存器中的命中率!– 2. 如果 table 很大,不需將 table 展開– 2. 減少 table 鎖定的競爭– 3. 可以在應用層 (application) ,進行 sql 單元的拆分,提高擴展性– 4. 減少重複查詢相同的資料– 5. 執行的方式類似 hash association ,在某些條件下將會有更好的
效率
• 分解的缺點– 1. 增加 sql 維護的複雜度– 2. table 不大或是關聯條件都是屬於 primary 或 index ,
較無使用需要8
MySQL 查詢執行的基礎 – 查詢優化器
9
mysql 優化器的一些限制 (1/3)
• 一個關聯式的查詢:– SELECT * – FROM sakila.film– WHERE film_id IN (– SELECT film_id FROM sakila.film_actor WHERE actor_id = 1– );
• 我們以為的執行方式:– Step1. SELECT film_id FROM sakila.film_actor WHERE actor_id = 1;– ANS : film_id = 1,2,3,4,5,6– Step2. SELECT * FROM sakila.film WHERE film_id IN (1,2,3,4,5,6);
10
mysql 優化器的一些限制 (2/3)
• 一個關聯式的查詢:– SELECT * – FROM sakila.film– WHERE film_id IN (– SELECT film_id FROM sakila.film_actor WHERE actor_id = 1– );
• 實際上執行的方式:– SELECT * – FROM sakila.film– WHERE EXISTS (– SELECT * – FROM sakila.film_actor – WHERE actor_id = 1 AND film_actor.film_id = film.film_id– );
11
mysql 優化器的一些限制 (3/3)
• 解決的方法 (1). 使用 INNER JOIN– SELECT film.* – FROM sakila.film– JOIN sakila.film_actor USING (film_id)– WHERE film.actor_id = 1;
• 解決的方法 (2). 使用 GROUP_CONCAT 作為 subquery 的 index– SELECT * – FROM sakila.film– WHERE film_id IN (– SELECT GROUP_CONCAT(film_id) – FROM sakila.film_actor WHERE actor_id = 1– );
12
如何優化 MIN() & MAX() ?
• MIN() 查詢案例:
– SELECT MIN(actor_id) – FROM sakila.actor WHERE first_name = 'PENELOPE';
• 分析如下:
– 因為 first_name 不是 index ,那 mysql 所以會執行全資料表掃描– 如果是 where 條件是執行 primary key 的掃描,當然就會比較快!
因為 PK index 本身就已經排序好了!
13
如何優化關連式查詢?
• 1. 確保 ON 或 USING 的子句內,使用的列 (column) 最好是 index– table-A & table-B 使用 column-C 做關聯,如果關聯的順序 B ->
A ,那這樣就只需要在 A 中建立 index 即可– 一般來說只需要在關聯順位第二個加上 index 即可
• 2. 確保 GROUP BY 或 ORDER BY 指令只牽扯到同一個資料表中的列 (column) , 這樣 mysql 才有可能會使用 index 來處理!
14
如何優化子查詢 (subquery) ?
• 若無法使用 index , mysql 會採用臨時資料表 (tempo table) ,或是直接將檔案排序 (filesort)
• 我們在寫的時候盡量以使用 index 為目標 !
• Before– SELECT actor.first_name, actor.last_name, COUNT(*)– FROM sakila.film_actor– INNER JOIN sakila.actor USING(actor_id)– GROUP BY actor.first_name, actor.last_name;
• After ( 利用演員姓名和 ID 都屬於同一筆資料的特性! )– SELECT actor.first_name, actor.last_name, COUNT(*)– FROM sakila.film_actor– INNER JOIN sakila.actor USING(actor_id)– GROUP BY film_actor.actor_id;
15
如何優化 LIMIT ? (1/2)
• 在 mysql 中, LIMIT 5000, 5 – SELECT film_id, description FROM sakila.film ORDER BY title LIMIT
5000, 5;– 需要掃描 5005 筆,但是只取 5 筆資料出來使用
• 再讀取下一批, LIMIT 5005, 5 – SELECT film_id, description FROM sakila.film ORDER BY title LIMIT
5005, 5;– 需要掃描 5010 筆,但是只取 5 筆資料出來使用
• 重複 10 回,執行的代價相當的高!
16
如何優化 LIMIT ? (2/2)
• 如何處理?盡可能是用 index 去處理這種狀況!而不要讓 mysql 掃描整份資料表
• 亦可在第一次查詢的時候取得 film_id ,然後利用 film_id排除掉不需要掃描的 rows ,以此類推~– film_id = 5005– SELECT film_id, description
FROM sakila.film WHERE film_id > 5005 ORDER BY title LIMIT 5;
17
如何優化 UNION ?
• 因為 UNION 一直以來都是以“暫存”的方式執行 SELECT查詢,所以很多依靠 index 的優化策略沒辦法很好的發揮作用
• 建議作法:是將 WHERE , LIMIT , ORDER BY 下推到 UNION 的各子查詢階段提前處理.
18
MySQL Explain
• 1. Id• 2. select_type• 3. table• 4. type• 5. poddible_keys• 6. key• 7. key_len• 8. ref• 9. rows• 10. extra
19
1. ID
• MySQL 執行的順序號碼, ID 依照大到小的的順序執• SELECT 如果有子查詢,子查詢的 ID號碼就會開始遞增
20
2. Select_type
• (1) SIMPLE : 最簡單的 SELECT( 不使用 UNION 或子查询等 )• (2) PRIMARY : 最外層的 SELECT.• (3) UNION : UNION 中的第二個或是後面的 SELECT语句 . • (4) DEPENDENT UNION :
UNION 的第二個或後面 SELECT 的來源是子查詢• (5) UNION RESULT : UNION 的结果 .• (6) SUBQUERY : 子查詢中的第一個 SELECT.
– select * from t3 where id = (select id from t3 where id=3952602 ) ;
• (7) DEPENDENT SUBQUERY : 子查詢中的第一個 SELECT– select * from t3 where id IN (select id from t3 where id=3952602 ) ;
• (8) DERIVED : FROM 子句的子查詢 ( 暫存表 )
21
3. table
• 顯示這一行的數據是處理哪張 table 的• 有時候看到的不是真實的 table 名字,看到的是 DERIVED X
( x 是一個數字,代表某一步驟 ID 的執行結果 )
22
4. type
• 最好到最差的连接类型为 : • (1) system• (2) const• (3) eq_ref• (4) ref• (5) ref_or_null• (6) index_merge• (7) unique_subquery• (8) index_subquery• (9) range• (10) index• (11) ALL
23
5.possible_keys
• possible_keys 列,裡面指出 MySQL 可以使用哪些 index ,就能在該table找到這些資料 (rows)。
24
6. key
• 顯示 MySQL 實際上真正有使用到的 index ,如果沒有使用任何的index ,結果會為 NULL
25
7. key_len
• key_len 顯示 MySQL 本次執行 key 的長度。• 如果項目 6. key 是 NULL ,則長度為 NULL。• 在不影響資料的正確性下,使用的 ken_len 長度越小越好。
26
8. ref
• 顯示哪些欄位或是哪個常數與 key 一起成為 table 的查詢條件
27
9. rows
• rows 顯示 MySQL 在執行的時候,在中 table 掃描的行數
28
10. Extra (1/2)
• (1) Distinct :當 MySQL找到相關連的資料時,就不再搜尋。• (2) Not exists :
MySQL 優化 LEFT JOIN , 一旦找到符合的 LEFT JOIN 資料後,就不再搜尋。
• (3) Range checked for each Record : MySQL 無法找到理想的索引。此為最慢的使用索引。
• (4) Using filesort : 當出現這個值時,表示此 SELECT語法需要優化。 因為 MySQL 必須進行額外的步驟來進行資料掃描, 此情況會發生在針對不同的資料進行 ORDER BY 或是 GROUP BY
• (5) Using index : 返回的資料是從 index 中取得的資料,而不是從實際的 table 中返回, 當返回的資料都出現在索引中的資料時就會發生此情況。
• (6) Using temporary : 此為 MySQL 必須建立一個暫時的資料表 (Table) 來儲存結果。
29
10. Extra (2/2)
• (7) Using where : 使用 WHERE語法中的欄位來取得結果。
• (8) eq_ref : MySQL 在關聯查詢時, 在查詢時使用主鍵 (primary key) 或唯一鍵 (unique) 的全部。
• (9) ref : 關聯查詢時使用了非唯一鍵或主鍵的 index 時。
• (10) range : 使用 index 取得一個範圍的結果。 例如:使用大於 > 或小於 < 查詢時發生。
• (11) index : 此為針對索引中的資料進行查詢。
• (12) ALL : 針對每一筆記錄進行完全掃描,此為最壞的情況,應該盡量避免。
30
EXPLAIN:Extra = filesort
• 當無法使用 index產生排序結果的時候,mysql 就需要自行排序
• 如果資料量小在暫存區內 (Temporary)• 如果數據量大到無法在暫存區內處理,則需要用到 IO 讀取硬碟
• 無論是暫存或是硬碟, extra 一律顯示 filesort
31
SHOW PROFILES & PROFILE
• SHOW PROFILE & SHOW PROFILES 可以檢視我們的 query 指令在執行過程中多種系統資源的消耗情況.
• 如 CPU、 IO、 IPC、 SWAP 等,以及發生的 PAGE FAULTS、 CONTEXT SWITCHE 等等.
32
Profiling 參數
• Profiling 是一個存在於 MySQL session 中的一個參數值,控制 Profiling功能是否開啟 / 關閉.– 查詢 profiling參數狀態,指令: SELECT @@profiling;– 預設值為 0 關閉– 設定 1 開啟,指令: SET profiling = 1;
• 當開啟 Profiling 後, MySQL 將會自動紀錄所有執行的query歷史資訊.– MySQL預設保存的查詢歷史,筆數為 15– 查詢 profiling_history_size 目前的設定,指令:
SELECT @@profiling_history_size;– 設定歷史保存的上限 ( 最高限制為 100) ,指令:
SET profiling_history_size = 30;
33
Profiles
• 開啟 Profiling 與設定好 Profiling_history_size 後,可以呼叫profiles 來顯示最新 N 件的查詢請求記錄– 指令: SHOW PROFILES;
• 顯示結果– Query_ID :查詢指令的流水號– Duration :查詢的花費時間– Query :查詢的 SQL語句
34
Profile
• 開啟 Profiling 與設定好 Profiling_history_size 後,可以呼叫 profiles 來顯示最新 N 件的查詢請求記錄– 指令: SHOW PROFILE ALL FOR QUERY 8 ; 查詢 queryID=8 的 profile 資訊
35
指令 Profile 參數 queryID 參數 參數描述
SELECT PROFILE
ALL
FOR QUERY (n)
顯示所有 profile 資訊
BLOCK IO 顯示 BLOCK input & output 的次數
CONTEXT SWITCHES 顯示自動 &非自動的 context switches 的次數
CPU 顯示 CPU 使用量
IPC 顯示訊息送出與回覆的次數
MEMORY (X) 現在沒有實作
PAGE FAULTS 顯示主要與次要的分頁錯誤次數
SOURCE
SWAPS
P
36
範例:以 Xboard 為例 (Before-1/3)
• 案例:查詢群組 <音樂 > 中,分類為 <華語音樂 > 的 content– View : vw_page_content 是 content 內容露出格式的 sql view
• 執行時間: duration = '0.17545700’ sec• 取得筆數: 234
37
範例:以 Xboard 為例 (Before-2/3)
• 案例:查詢群組 <音樂 > 中,分類為 <華語音樂 > 的 content– View : vw_page_content 是 content 內容露出格式的 sql view
• 執行時間: duration = '0.17545700’ sec• 取得筆數: 234
38
注意 subquery result 的size!
冗餘的指令!?
範例:以 Xboard 為例 (Before-3/3)
• Explain 分析結果:
39
範例:以 Xboard 為例 (After-1/2)
• 案例:查詢群組 <音樂 > 中,分類為 <華語音樂 > 的 content– View : vw_page_content 是 content 內容露出格式的 sql view
• 執行時間: duration = ‘0.00572175’ sec (之前是 0.17545700)• 取得筆數: 234
40
移除 group 指令
交換 subquuery 執行的順序較大的 table 往外搬
範例:以 Xboard 為例 (After-2/2)
• Explain 分析結果:
41
Conclusion
• 建立 table scheme 的階段,應訂定較有意義的 primary key
• SELECT 語句注意的事項:– 盡量將冗餘的指令移除– 查詢的條件盡量使用 primary , index ,避免大量掃描到 table 內的資料– 盡可能使用效率較高的 inner join 處理關聯的資料– 注意 subquery 的執行順序,
取得資料與掃瞄 table 盡量在最後要返回結果的時候再執行!– 注意 subquery 過程中 temporary table 的大小
• Explain 應注意的項目– Select_type – Type :避免出現大量的 ALL– Rows : rows 越大即代表掃瞄的筆數越多– Extra :盡力避免掉出現 ALL ,減少 Using temporary ,以及盡量讓 Using filesort 在 out result階段再執行
42
Reference
• MySQL Explain– Select_type – Type :避免出現大量的 ALL– Rows : rows 越大即代表掃瞄的筆數越多– Extra :盡力避免掉出現 ALL ,減少 Using temporary ,以及盡量讓 Using filesort 在 out
result階段再執行
• MySQL Profiles– http://dev.mysql.com/doc/refman/5.7/en/show-profile.html– http://blog.longwin.com.tw/2008/10/mysql-query-profiler-cpu-ram-time-2008
/– http://bian5399.blog.51cto.com/3848702/842165– http://blog.csdn.net/dba_waterbin/article/details/12245891
43