1,mysql索引性能优化最佳实战
2, 使用索引查询如何避免回表查询
3,为什么查询有时候加了索引也会失效?
4,如何使用 optimizer_trace 分析sql语句
5,order by 排序优化原则有哪些?
6 mysql排序支持2种 filesort 和index 的区别
7,Mysqlfilesort 排序底层实现原理
8, 单路与双路(回表)排序底层原理。
9, 为什么阿里巴巴手册不推荐使用存储过程?
Using index: 查询的列都是加上索引,不需要回表查询。
什么是回表查询:
select * from table where name='bob'
底层先查询username索引文件查找对应的username 'bob'的主键id
在根据主键id 查询主键索引文件 对应行数据。
如果全表扫描查询效率比索引 查询要高,则使用全表扫描。
如果用name索引查找数据需要遍历name字段联合索引树,然后根据遍历出来的主键值去主键索引树里再去查找最终的数据,成本比全表扫描还高。
可以覆盖索引优化,这样系需要遍历name字段的联合索引树就可以拿到所有的结果。
误区:
1, 所有的字段加上了索引 就一定使用索引查询
2,全表扫描查询是否一定会比索引查询慢。
排序的2种算法,
1, using filesort 索引失效 没有根据索引字段排序的情况
2, using index 根据索引的字段排序
可以看到通过select出的字段是覆盖索引,MySQL底层使用了索引优化。可以使用optimizer_trace 分析sql是否有走过索引:
optimizer_trace 分析sql 语句
如果全表查询效率比索引要高,则使用全表扫描。
如果用name 索引查找数据需要遍历name字段联合索引树,然后根据遍历出来的主键值再去查找最终的数据,成本比全表扫描要高。
可以使用覆盖索引,这样只需要遍历name的字段联合索引树就可以查找到结果:
SET optimizer_trace='enabled=on',end_markers_in_json=on;
SELECT * FROM employees WHERE name > 'mayikt' ;
SELECT * FROM information_schema.OPTIMIZER_TRACE;
查询结果:
{
"steps": [
{
"join_preparation": { --第一阶段:SQl准备阶段
"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` > 'mayikt')"
}
] /* steps */
} /* join_preparation */
},
{
"join_optimization": { --第二阶段:SQL优化阶段
"select#": 1,
"steps": [
{
"condition_processing": { --条件处理
"condition": "WHERE",
"original_condition": "(`employees`.`name` > 'mayikt')",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "(`employees`.`name` > 'mayikt')"
},
{
"transformation": "constant_propagation",
"resulting_condition": "(`employees`.`name` > 'mayikt')"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "(`employees`.`name` > 'mayikt')"
}
] /* steps */
} /* condition_processing */
},
{
"substitute_generated_columns": {
} /* substitute_generated_columns */
},
{
"table_dependencies": [ --表依赖详情
{
"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": [ --预估标的访问成本
{
"table": "`employees`",
"range_analysis": { --全表扫描情况
"table_scan": {
"rows": 68511, --扫描行数
"cost": 13929 --查询成本
} /* 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": { ‐‐分析各个索引使用成本
"range_scan_alternatives": [
{
"index": "idx_name_age_position",
"ranges": [
"mayikt < name"
] /* ranges */,
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 34255, --‐‐索引扫描行数
"cost": 41107, --索引使用成本
"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": [
{
"plan_prefix": [
] /* plan_prefix */,
"table": "`employees`",
"best_access_path": {
"considered_access_paths": [
{
"rows_to_scan": 68511,
"access_type": "scan",
"resulting_rows": 68511,
"cost": 13927,
"chosen": true
}
] /* considered_access_paths */
} /* best_access_path */,
"condition_filtering_pct": 100,
"rows_for_plan": 68511,
"cost_for_plan": 13927,
"chosen": true
}
] /* considered_execution_plans */
},
{
"attaching_conditions_to_tables": {
"original_condition": "(`employees`.`name` > 'mayikt')",
"attached_conditions_computation": [
] /* attached_conditions_computation */,
"attached_conditions_summary": [
{
"table": "`employees`",
"attached": "(`employees`.`name` > 'mayikt')"
}
] /* attached_conditions_summary */
} /* attaching_conditions_to_tables */
},
{
"refine_plan": [
{
"table": "`employees`"
}
] /* refine_plan */
}
] /* steps */
} /* join_optimization */
},
{
"join_execution": { --第三阶段:SQL执行阶段
"select#": 1,
"steps": [
] /* steps */
} /* join_execution */
}
] /* steps */
}
全表扫描的成本低于索引扫描, 索引MySQL最终会选择全表扫描。
Order by 培训优化原则
1,.EXPLAIN SELECT * FROM employees WHERE name= 'meite' AND position ='ceo' order by age;
分析: 利用最左前缀法则: 中间字段不能断,因此查询到了name 索引
,从key_len=74 也能看出,age索引列用在排序过程中,因为Extra 字段里没有
using filesort 是为Using index condition
2,EXPLAIN SELECT * FROM employees WHERE name= 'meite' order by position;
从explain 的执行结果来看: key_len=74, 查询使用了name 索引,由于
用了position 进行排序,跳过了age,出现了Using filesort
3.EXPLAIN SELECT * FROM employees WHERE name= 'meite' order by age,position;
分析:
查找只用到索引 name, age 和position 用于排序。,无Using filesort.
4.EXPLAIN SELECT * FROM employees WHERE name= 'meite' order by position,age;
分析:
在 Extra中出现 Using Filesort, 因为age 为常量,在排序中被优化,所以索引未颠倒,会出现Using Filesort
5.
EXPLAIN SELECT * FROM employees WHERE name= 'meite' order by age asc ,position desc ;
分析: age字段采用升序排序,position 降序排序 导致索引的排序方式不同,从而产生 Using filesort
EXPLAIN SELECT * FROM employees WHERE name in('meite','xiaowei') order by age, position;
f
分析: 对于多个排序来说,多个相等条件也是范围查询。
7, EXPLAIN SELECT * FROM employees WHERE name>'meite' order by name;
可以优化为
EXPLAIN SELECT name,age,position FROM employees WHERE name>'meite' order by name;
排序优化总结
Mysql排序支持两种filesort和index
1.这种方式在使用explain分析时显示为using index,不需要额外的排序, 是指mysql扫描索引本身完成排序,操作效率较高
2.通过对返回数据进行排序,即filesort,所有不通过索引直接返回排序结果的排序都是filesort排序
Filesort实现原理:
filesort通过相应的排序算法,将取得的数据在sort_buffer_size系统变量设置的内存排序区中进行排序,如果内存装载不下,会将磁盘上的数据进行分块,再对各个数据块进行排序,再将各个块合并成有序的结果集。
order by满足两种情况会使用Using index。
- order by语句使用索引最左前列。
- 使用where子句与order by子句条件列组合满足索引最左前列。
3、尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
4、如果order by的条件不在索引列上,就会产生Using filesort。
5、能用覆盖索引尽量用覆盖索引