1. 程式人生 > >mysql 執行計劃分析三看, explain,profiling,optimizer_trace

mysql 執行計劃分析三看, explain,profiling,optimizer_trace

roc var you time field 表之間 origin 依賴 nod

http://blog.csdn.net/xj626852095/article/details/52767963

step 1

使用explain 查看執行計劃, 5.6後可以加參數 explain format=json xxx 輸出json格式的信息

step 2

使用profiling詳細的列出在每一個步驟消耗的時間,前提是先執行一遍語句

#打開profiling 的設置
SET profiling = 1;
SHOW VARIABLES LIKE ‘%profiling%‘;

#查看隊列的內容
show profiles;  
#來查看統計信息
show profile block io,cpu for query 3;


step 3

Optimizer trace是MySQL5.6添加的新功能,可以看到大量的內部查詢計劃產生的信息, 先打開設置,然後執行一次sql,最後查看`information_schema`.`OPTIMIZER_TRACE`的內容

#打開設置
SET optimizer_trace=‘enabled=on‘;  
#最大內存根據實際情況而定, 可以不設置
SET OPTIMIZER_TRACE_MAX_MEM_SIZE=1000000;
SET END_MARKERS_IN_JSON=ON;
SET optimizer_trace_limit = 1;
SHOW VARIABLES LIKE ‘%optimizer_trace%‘;

#執行所需sql後,查看該表信息即可看到詳細的執行過程
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`;

MySQL索引選擇不正確並詳細解析OPTIMIZER_TRACE格式

http://blog.csdn.net/melody_mr/article/details/48950601

一 表結構如下:

CREATE TABLE t_audit_operate_log (
Fid bigint(16) AUTO_INCREMENT,
Fcreate_time int(10) unsigned NOT NULL DEFAULT ‘0‘,
Fuser varchar(50) DEFAULT ‘‘,
Fip bigint(16) DEFAULT NULL,
Foperate_object_id bigint(20) DEFAULT ‘0‘,

PRIMARY KEY (Fid),
KEY indx_ctime (Fcreate_time),
KEY indx_user (Fuser),
KEY indx_objid (Foperate_object_id),
KEY indx_ip (Fip)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

執行查詢:

MySQL> explain select count(*) from t_audit_operate_log where [email protected] and Fcreate_time>=1407081600 and Fcreate_time<=1407427199\G

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

id: 1

select_type: SIMPLE

table: t_audit_operate_log

type: ref

possible_keys: indx_ctime,indx_user

key: indx_user

key_len: 153

ref: const

rows: 2007326

Extra: Using where

發現,使用了一個不合適的索引, 不是很理想,於是改成指定索引:

mysql> explain select count(*) from t_audit_operate_log use index(indx_ctime) where [email protected] and Fcreate_time>=1407081600 and Fcreate_time<=1407427199\G

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

id: 1

select_type: SIMPLE

table: t_audit_operate_log

type: range

possible_keys: indx_ctime

key: indx_ctime

key_len: 5

ref: NULL

rows: 670092

Extra: Using where

實際執行耗時,後者比前者快了接近10

問題: 很奇怪,優化器為何不選擇使用 indx_ctime 索引,而選擇了明顯會掃描更多行的 indx_user 索引。

分析2個索引的數據量如下: 兩個條件的唯一性對比:

select count(*) from t_audit_operate_log where [email protected];
+----------+
| count(*) |
+----------+
| 1238382 |
+----------+

select count(*) from t_audit_operate_log where Fcreate_time>=1407254400 and Fcreate_time<=1407427199;
+----------+
| count(*) |
+----------+
| 198920 |
+----------+

顯然,使用索引indx_ctime好於indx_user,但MySQL卻選擇了indx_user. 為什麽?

於是,使用 OPTIMIZER_TRACE進一步探索.

二 OPTIMIZER_TRACE的過程說明

以本處事例簡要說明OPTIMIZER_TRACE的過程.

查看OPTIMIZER_TRACE方法:

1.set optimizer_trace=‘enabled=on‘; --- 開啟trace

2.set optimizer_trace_max_mem_size=1000000; --- 設置trace大小

3.set end_markers_in_json=on; --- 增加trace中註釋

4.select * from information_schema.optimizer_trace\G;

[plain] view plain copy
  1. {\
  2. "steps": [\
  3. {\
  4. "join_preparation": {\ ---優化準備工作
  5. "select#": 1,\
  6. "steps": [\
  7. {\
  8. "expanded_query": "/* select#1 */ select count(0) AS `count(*)` from `t_audit_operate_log` where ((`t_audit_operate_log`.`Fuser` = [email protected]) and (`t_audit_operate_log`.`Fcreate_time` >= 1407081600) and (`t_audit_operate_log`.`Fcreate_time` <= 1407427199))"\
  9. }\
  10. ] /* steps */\
  11. } /* join_preparation */\
  12. },\
  13. {\
  14. "join_optimization": {\ ---優化工作的主要階段,包括邏輯優化和物理優化兩個階段
  15. "select#": 1,\
  16. "steps": [\ ---優化工作的主要階段, 邏輯優化階段
  17. {\
  18. "condition_processing": {\ ---邏輯優化,條件化簡
  19. "condition": "WHERE",\
  20. "original_condition": "((`t_audit_operate_log`.`Fuser` = [email protected]) and (`t_audit_operate_log`.`Fcreate_time` >= 1407081600) and (`t_audit_operate_log`.`Fcreate_time` <= 1407427199))",\
  21. "steps": [\
  22. {\
  23. "transformation": "equality_propagation",\ ---邏輯優化,條件化簡,等式處理
  24. "resulting_condition": "((`t_audit_operate_log`.`Fuser` = [email protected]) and (`t_audit_operate_log`.`Fcreate_time` >= 1407081600) and (`t_audit_operate_log`.`Fcreate_time` <= 1407427199))"\
  25. },\
  26. {\
  27. "transformation": "constant_propagation",\ ---邏輯優化,條件化簡,常量處理
  28. "resulting_condition": "((`t_audit_operate_log`.`Fuser` = [email protected]) and (`t_audit_operate_log`.`Fcreate_time` >= 1407081600) and (`t_audit_operate_log`.`Fcreate_time` <= 1407427199))"\
  29. },\
  30. {\
  31. "transformation": "trivial_condition_removal",\ ---邏輯優化,條件化簡,條件去除
  32. "resulting_condition": "((`t_audit_operate_log`.`Fuser` = [email protected]) and (`t_audit_operate_log`.`Fcreate_time` >= 1407081600) and (`t_audit_operate_log`.`Fcreate_time` <= 1407427199))"\
  33. }\
  34. ] /* steps */\
  35. } /* condition_processing */\
  36. },\ ---邏輯優化,條件化簡,結束
  37. {\
  38. "table_dependencies": [\ ---邏輯優化, 找出表之間的相互依賴關系. 非直接可用的優化方式.
  39. {\
  40. "table": "`t_audit_operate_log`",\
  41. "row_may_be_null": false,\
  42. "map_bit": 0,\
  43. "depends_on_map_bits": [\
  44. ] /* depends_on_map_bits */\
  45. }\
  46. ] /* table_dependencies */\
  47. },\
  48. {\
  49. "ref_optimizer_key_uses": [\ ---邏輯優化, 找出備選的索引
  50. {\
  51. "table": "`t_audit_operate_log`",\
  52. "field": "Fuser",\
  53. "equals": "[email protected]",\
  54. "null_rejecting": false\
  55. }\
  56. ] /* ref_optimizer_key_uses */\
  57. },\
  58. {\
  59. "rows_estimation": [\ ---邏輯優化, 估算每個表的元組個數. 單表上進行全表掃描和索引掃描的代價估算. 每個索引都估算索引掃描代價
  60. {\
  61. "table": "`t_audit_operate_log`",\
  62. "range_analysis": {\
  63. "table_scan": {\---邏輯優化, 估算每個表的元組個數. 單表上進行全表掃描的代價
  64. "rows": 8150516,\
  65. "cost": 1.73e6\
  66. } /* table_scan */,\
  67. "potential_range_indices": [\ ---邏輯優化, 列出備選的索引. 後續版本字符串變為potential_range_indexes
  68. {\
  69. "index": "PRIMARY",\---邏輯優化, 本行表明主鍵索引不可用
  70. "usable": false,\
  71. "cause": "not_applicable"\
  72. },\
  73. {\
  74. "index": "indx_ctime",\---邏輯優化, 索引indx_ctime
  75. "usable": true,\
  76. "key_parts": [\
  77. "Fcreate_time",\
  78. "Fid"\
  79. ] /* key_parts */\
  80. },\
  81. {\
  82. "index": "indx_user",\---邏輯優化, 索引indx_user
  83. "usable": true,\
  84. "key_parts": [\
  85. "Fuser",\
  86. "Fid"\
  87. ] /* key_parts */\
  88. },\
  89. {\
  90. "index": "indx_objid",\---邏輯優化, 索引
  91. "usable": false,\
  92. "cause": "not_applicable"\
  93. },\
  94. {\
  95. "index": "indx_ip",\---邏輯優化, 索引
  96. "usable": false,\
  97. "cause": "not_applicable"\
  98. }\
  99. ] /* potential_range_indices */,\
  100. "setup_range_conditions": [\ ---邏輯優化, 如果有可下推的條件,則帶條件考慮範圍查詢
  101. ] /* setup_range_conditions */,\
  102. "group_index_range": {\---邏輯優化, 如帶有GROUPBY或DISTINCT,則考慮是否有索引可優化這種操作. 並考慮帶有MIN/MAX的情況
  103. "chosen": false,\
  104. "cause": "not_group_by_or_distinct"\
  105. } /* group_index_range */,\
  106. "analyzing_range_alternatives": {\---邏輯優化,開始計算每個索引做範圍掃描的花費(等值比較是範圍掃描的特例)
  107. "range_scan_alternatives": [\
  108. {\
  109. "index": "indx_ctime",\ ---[A]
  110. "ranges": [\
  111. "1407081600 <= Fcreate_time <= 1407427199"\
  112. ] /* ranges */,\
  113. "index_dives_for_eq_ranges": true,\
  114. "rowid_ordered": false,\
  115. "using_mrr": true,\
  116. "index_only": false,\
  117. "rows": 688362,\
  118. "cost": 564553,\ ---邏輯優化,這個索引的代價最小
  119. "chosen": true\ ---邏輯優化,這個索引的代價最小,被選中. (比前面的table_scan 和其他索引的代價都小)
  120. },\
  121. {\
  122. "index": "indx_user",\
  123. "ranges": [\
  124. "[email protected] <= Fuser <= [email protected]"\
  125. ] /* ranges */,\
  126. "index_dives_for_eq_ranges": true,\
  127. "rowid_ordered": true,\
  128. "using_mrr": true,\
  129. "index_only": false,\
  130. "rows": 1945894,\
  131. "cost": 1.18e6,\
  132. "chosen": false,\
  133. "cause": "cost"\
  134. }\
  135. ] /* range_scan_alternatives */,\
  136. "analyzing_roworder_intersect": {\
  137. "usable": false,\
  138. "cause": "too_few_roworder_scans"\
  139. } /* analyzing_roworder_intersect */\
  140. } /* analyzing_range_alternatives */,\---邏輯優化,開始計算每個索引做範圍掃描的花費. 這項工作結算
  141. "chosen_range_access_summary": {\---邏輯優化,開始計算每個索引做範圍掃描的花費. 總結本階段最優的.
  142. "range_access_plan": {\
  143. "type": "range_scan",\
  144. "index": "indx_ctime",\
  145. "rows": 688362,\
  146. "ranges": [\
  147. "1407081600 <= Fcreate_time <= 1407427199"\
  148. ] /* ranges */\
  149. } /* range_access_plan */,\
  150. "rows_for_plan": 688362,\
  151. "cost_for_plan": 564553,\
  152. "chosen": true\ -- 這裏看到的cost和rows都比 indx_user 要來的小很多---這個和[A]處是一樣的,是信息匯總.
  153. } /* chosen_range_access_summary */\
  154. } /* range_analysis */\
  155. }\
  156. ] /* rows_estimation */\ ---邏輯優化, 估算每個表的元組個數. 行估算結束
  157. },\
  158. {\
  159. "considered_execution_plans": [\ ---物理優化, 開始多表連接的物理優化計算
  160. {\
  161. "plan_prefix": [\
  162. ] /* plan_prefix */,\
  163. "table": "`t_audit_operate_log`",\
  164. "best_access_path": {\
  165. "considered_access_paths": [\
  166. {\
  167. "access_type": "ref",\ ---物理優化, 計算indx_user索引上使用ref方查找的花費,
  168. "index": "indx_user",\
  169. "rows": 1.95e6,\
  170. "cost": 683515,\
  171. "chosen": true\
  172. },\ ---物理優化, 本應該比較所有的可用索引,即打印出多個格式相同的但索引名不同的內容,這裏卻沒有。推測是bug--沒有遍歷每一個索引.
  173. {\
  174. "access_type": "range",\---物理優化,猜測對應的是indx_time(沒有實例可進行調試,對比5.7的跟蹤信息猜測而得)
  175. "rows": 516272,\
  176. "cost": 702225,\---物理優化,代價大於了ref方式的683515,所以沒有被選擇
  177. "chosen": false\ -- cost比上面看到的增加了很多,但rows沒什麽變化 ---物理優化,此索引沒有被選擇
  178. }\
  179. ] /* considered_access_paths */\
  180. } /* best_access_path */,\
  181. "cost_for_plan": 683515,\ ---物理優化,匯總在best_access_path 階段得到的結果
  182. "rows_for_plan": 1.95e6,\
  183. "chosen": true\ -- cost比上面看到的竟然小了很多?雖然rows沒啥變化 ---物理優化,匯總在best_access_path 階段得到的結果
  184. }\
  185. ] /* considered_execution_plans */\
  186. },\
  187. {\
  188. "attaching_conditions_to_tables": {\---邏輯優化,盡量把條件綁定到對應的表上
  189. } /* attaching_conditions_to_tables */\
  190. },\
  191. {\
  192. "refine_plan": [\
  193. {\
  194. "table": "`t_audit_operate_log`",\---邏輯優化,下推索引條件"pushed_index_condition";其他條件附加到表上做為過濾條件"table_condition_attached"
  195. }\
  196. ] /* refine_plan */\
  197. }\
  198. ] /* steps */\
  199. } /* join_optimization */\ \---邏輯優化和物理優化結束
  200. },\
  201. {\
  202. "join_explain": {} /* join_explain */\
  203. }\
  204. ] /* steps */\

三 其他一個相似問題
單表掃描,使用ref和range從索引獲取數據一例  
http://blog.163.com/li_hx/blog/static/183991413201461853637715/


四 問題的解決方式

遇到單表上有多個索引的時候,在MySQL5.6.20版本之前的版本,需要人工強制使用索引,以達到最好的效果.

mysql 執行計劃分析三看, explain,profiling,optimizer_trace