日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

MySQL - 使用trace工具来窥探MySQL是如何选择执行计划的

發(fā)布時(shí)間:2025/3/21 数据库 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL - 使用trace工具来窥探MySQL是如何选择执行计划的 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 生猛干貨
  • Pre
  • 演示Demo
  • trace工具使用
  • Trace分析
  • Trace解讀
  • 搞定MySQL


生猛干貨

帶你搞定MySQL實(shí)戰(zhàn),輕松對(duì)應(yīng)海量業(yè)務(wù)處理及高并發(fā)需求,從容應(yīng)對(duì)大場(chǎng)面試


Pre

有的時(shí)候,明明某個(gè)字段有索引,那我們一般認(rèn)為走索引好一些,結(jié)果mysql走了全表掃描 , 那怎么看mysql是怎么選擇的呢? 來(lái) 今天來(lái)看一看MySQL是如何循著合適的執(zhí)行計(jì)劃的?


演示Demo

還是那個(gè)老表 employees

CREATE TABLE `employees` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',`age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',`position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位',`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時(shí)間',PRIMARY KEY (`id`),KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='員工記錄表';

來(lái)看個(gè)執(zhí)行計(jì)劃

mysql> EXPLAIN select * from employees where name > 'a'; +----+-------------+-----------+------------+------+-----------------------+------+---------+------+--------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+--------+----------+-------------+ | 1 | SIMPLE | employees | NULL | ALL | idx_name_age_position | NULL | NULL | NULL | 100175 | 50 | Using where | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+--------+----------+-------------+ 1 row in set

推測(cè)一下,為何走了全表掃描

MySQL - 索引優(yōu)化案例實(shí)操 # Case 1 : 聯(lián)合索引第一個(gè)字段用范圍不一定會(huì)走索引

name > ‘a(chǎn)’ , 一般都不會(huì)用到索引。 如果用name索引需要遍歷name字段聯(lián)合索引樹(shù),然后還需要根據(jù)遍歷出來(lái)的主鍵值去主鍵索引樹(shù)里再去查出最終數(shù)據(jù),成本比全表掃描還高 。


優(yōu)化呢?

  • 強(qiáng)制走索引 (不一定效率高)
  • 使用覆蓋索引, 這樣只需要遍歷name字段的聯(lián)合索引樹(shù)就能拿到所有結(jié)果 , 來(lái)看下
  • mysql> EXPLAIN select name,age,position from employees where name > 'a' ; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 74 | NULL | 50087 | 100 | Using where; Using index | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+-------+----------+--------------------------+ 1 row in setmysql>

    再來(lái)看個(gè)執(zhí)行計(jì)劃 ,僅僅把比對(duì)的value 換一下

    mysql> EXPLAIN select * from employees where name > 'zzz' ; +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | employees | NULL | range | idx_name_age_position | idx_name_age_position | 74 | NULL | 1 | 100 | Using index condition | +----+-------------+-----------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+ 1 row in set

    搞得我一臉懵逼。。。。。 這樣也行?
    很明顯,第二個(gè) > zzz , key_len=74 = 3 * 24(表定義的varch長(zhǎng)度) +2 , 使用了聯(lián)合索引中的name , Extra也可以知一二,使用了部分索引條件。

    那咋辦? 到底是為啥? 僅僅是因?yàn)関alue的變化,導(dǎo)致mysql選擇了不同的執(zhí)行計(jì)劃?


    trace工具使用

    開(kāi)啟trace工具會(huì)影響mysql性能,所以只能臨時(shí)分析sql使用,用完之后立即關(guān)閉 .

    mysql> set session optimizer_trace="enabled=on",end_markers_in_json=on; --開(kāi)啟trace mysql> select * from employees where name > 'a' order by position; --- 執(zhí)行SQL mysql> SELECT * FROM information_schema.OPTIMIZER_TRACE; ---查看trace mysql> set session optimizer_trace="enabled=off"; --關(guān)閉trace

    第二條和第三條 一起執(zhí)行 ,切記


    Trace分析

    把結(jié)果copy出來(lái)

    三大步

  • 準(zhǔn)備階段
  • 優(yōu)化階段
  • 執(zhí)行階段
  • 重點(diǎn)呢 就是第二步 優(yōu)化階段


    Trace解讀

    {"steps": [{"join_preparation": { ---優(yōu)化準(zhǔn)備工作 "select#": 1,"steps": [{"expanded_query": "/* select#1 */ select `employees`.`id` AS `id`,`employees`.`name` AS `name`,`employees`.`age` AS `age`,`employees`.`position` AS `position`,`employees`.`hire_time` AS `hire_time` from `employees` where (`employees`.`name` > 'a') order by `employees`.`position`"}] /* steps */} /* join_preparation */},{"join_optimization": { ---- 優(yōu)化階段"select#": 1,"steps": [{"condition_processing": { ---- 條件處理 "condition": "WHERE","original_condition": "(`employees`.`name` > 'a')","steps": [{"transformation": "equality_propagation","resulting_condition": "(`employees`.`name` > 'a')"},{"transformation": "constant_propagation","resulting_condition": "(`employees`.`name` > 'a')"},{"transformation": "trivial_condition_removal","resulting_condition": "(`employees`.`name` > 'a')"}] /* steps */} /* condition_processing */},{"substitute_generated_columns": {} /* substitute_generated_columns */},{"table_dependencies": [ ------表依賴(lài)情況分析 {"table": "`employees`","row_may_be_null": false,"map_bit": 0,"depends_on_map_bits": [] /* depends_on_map_bits */}] /* table_dependencies */},{"ref_optimizer_key_uses": [] /* ref_optimizer_key_uses */},{"rows_estimation": [ --- 預(yù)估表的訪問(wèn)成本 {"table": "`employees`","range_analysis": {"table_scan": { ---全表掃描"rows": 100175, --- 掃描的行數(shù)"cost": 20390 ---COST查詢(xún)成本} /* table_scan */,"potential_range_indexes": [ --- 潛在的可以使用的索引{"index": "PRIMARY", ---主鍵索引"usable": false, "cause": "not_applicable"},{"index": "idx_name_age_position", ---- 輔助索引"usable": true, "key_parts": ["name","age","position","id"] /* key_parts */}] /* potential_range_indexes */,"setup_range_conditions": [] /* setup_range_conditions */,"group_index_range": {"chosen": false,"cause": "not_group_by_or_distinct"} /* group_index_range */,"analyzing_range_alternatives": { --- 分析各個(gè)索引的使用成本"range_scan_alternatives": [{"index": "idx_name_age_position","ranges": ["a < name"] /* ranges */,"index_dives_for_eq_ranges": true,"rowid_ordered": false, --使用該索引獲取的記錄是否按照主鍵排序"using_mrr": false, "index_only": false, --- 是否使用覆蓋索引"rows": 50087, ---索引掃描行數(shù)"cost": 60105, ---所用COST成本"chosen": false, ---是否選擇該索引"cause": "cost" ---原因}] /* range_scan_alternatives */,"analyzing_roworder_intersect": {"usable": false,"cause": "too_few_roworder_scans"} /* analyzing_roworder_intersect */} /* analyzing_range_alternatives */} /* range_analysis */}] /* rows_estimation */},{"considered_execution_plans": [ -----建議執(zhí)行計(jì)劃{"plan_prefix": [] /* plan_prefix */,"table": "`employees`","best_access_path": { ---- 最優(yōu)訪問(wèn)路徑 "considered_access_paths": [ ---- 最終選擇的訪問(wèn)路徑{"rows_to_scan": 100175,"access_type": "scan", --- 訪問(wèn)類(lèi)型 scan 即全表掃描"resulting_rows": 100175,"cost": 20388,"chosen": true, ------ true 確定選擇"use_tmp_table": true}] /* considered_access_paths */} /* best_access_path */,"condition_filtering_pct": 100,"rows_for_plan": 100175,"cost_for_plan": 20388,"sort_cost": 100175,"new_cost_for_plan": 120563,"chosen": true}] /* considered_execution_plans */},{"attaching_conditions_to_tables": {"original_condition": "(`employees`.`name` > 'a')","attached_conditions_computation": [] /* attached_conditions_computation */,"attached_conditions_summary": [{"table": "`employees`","attached": "(`employees`.`name` > 'a')"}] /* attached_conditions_summary */} /* attaching_conditions_to_tables */},{"clause_processing": {"clause": "ORDER BY","original_clause": "`employees`.`position`","items": [{"item": "`employees`.`position`"}] /* items */,"resulting_clause_is_simple": true,"resulting_clause": "`employees`.`position`"} /* clause_processing */},{"reconsidering_access_paths_for_index_ordering": {"clause": "ORDER BY","steps": [] /* steps */,"index_order_summary": {"table": "`employees`","index_provides_order": false,"order_direction": "undefined","index": "unknown","plan_changed": false} /* index_order_summary */} /* reconsidering_access_paths_for_index_ordering */},{"refine_plan": [{"table": "`employees`"}] /* refine_plan */}] /* steps */} /* join_optimization */},{"join_execution": { ----- 階段三 執(zhí)行SQL "select#": 1,"steps": [{"filesort_information": [{"direction": "asc","table": "`employees`","field": "position"}] /* filesort_information */,"filesort_priority_queue_optimization": {"usable": false,"cause": "not applicable (no LIMIT)"} /* filesort_priority_queue_optimization */,"filesort_execution": [] /* filesort_execution */,"filesort_summary": {"rows": 100003,"examined_rows": 100003,"number_of_tmp_files": 31,"sort_buffer_size": 262056,"sort_mode": "<sort_key, packed_additional_fields>"} /* filesort_summary */}] /* steps */} /* join_execution */}] /* steps */ }

    MySQL認(rèn)為 全表掃描的成本低于索引掃描,所以mysql最終選擇全表掃描 。


    同樣的 看看看 zzz的

    mysql> set session optimizer_trace="enabled=on",end_markers_in_json=on; --開(kāi)啟trace mysql> select * from employees where name > 'zzz' order by position; --- 執(zhí)行SQL mysql> SELECT * FROM information_schema.OPTIMIZER_TRACE; ---查看trace mysql> set session optimizer_trace="enabled=off"; --關(guān)閉trace

    同樣的套路,我相信你也能看出一二


    搞定MySQL

    總結(jié)

    以上是生活随笔為你收集整理的MySQL - 使用trace工具来窥探MySQL是如何选择执行计划的的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。