MySQL索引优化

示例

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 '入职时间',PRIMARY KEY (`id`),KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';-- 插入十万条假数据
INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());
INSERT INTO employees (name, age, position, hire_time)
SELECTCONCAT('番茄炒蛋', id+1) AS name,FLOOR(RAND() * 40 + 20) AS age,CASE FLOOR(RAND() * 4)WHEN 0 THEN 'manager'WHEN 1 THEN 'engineer'WHEN 2 THEN 'dev'WHEN 3 THEN 'assistant'END AS position,DATE_ADD(NOW(), INTERVAL -FLOOR(RAND() * 365 * 10) DAY) AS hire_time
FROM(SELECTn1.n + n10.n * 10 + n100.n * 100 + n1000.n * 1000 AS idFROM(SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n1CROSS JOIN(SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n10CROSS JOIN(SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n100CROSS JOIN(SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n1000CROSS JOIN(SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n10000) AS numbers;-- 新建一张与上表一摸一样的表;数据量只插几条CREATE TABLE `employees_copy` (`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 '入职时间',PRIMARY KEY (`id`),KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';-- 插入假数据
INSERT INTO employees_copy(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());
INSERT INTO employees_copy(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW());
INSERT INTO employees_copy(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());
INSERT INTO employees_copy (name, age, position, hire_time)
  1. 联合索引第一个字段用范围不会走索引

    explain select * from employees where name > 'LiLei' and age = 22 and position = 'dev'
    

    结论: 联合索引第一个字段就用范围查找不会走索引,mysql内部可能觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不如就全表扫描

  2. 强制走索引

    explain select * from employees force index(idx_name_age_position) where name > 'LiLei' and age = 22 and position = 'dev'
    

    结论: 虽然强制走索引让联合索引第一个字段范围查找也走索引,扫描的行rows看上去也少了点,但最终查找效率不一定比全表扫描高,因为回表的效率不高

    做个小实验:

    -- 关闭查询缓存
    set global query_cache_size = 0;
    set global query_cache_type = 0;
    -- 执行时间0.205s
    select * from employees where name > 'LiLei'
    -- 执行时间0.437s
    select * from employees force index(idx_name_age_position) where name > 'LiLei'
    
  3. 覆盖索引优化

    explain select name,age,position from employees where name > 'LiLei' and age = 22 and position = 'manager'
    

    在这里插入图片描述

  4. in和or在表数据量比较大的情况会走索引,在表记录不多的情况下会选择全表扫描

    数据量比较大时:

    explain select * from employees where name in('LiLei','HanMeimei','Lucy') and age = 22 and position = 'manager';
    explain select * from employees where (name = 'LiLei' or name = 'HanMeimie') and age = 22 and position = 'manager';
    

    在这里插入图片描述
    在这里插入图片描述

    数据量比较小时:

    explain select * from employees_copy where name in('LiLei','HanMeimei','Lucy') and age = 22 and position = 'manager';
    explain select * from employees_copy where (name = 'LiLei' or name = 'HanMeimie') and age = 22 and position = 'manager';
    

    在这里插入图片描述
    在这里插入图片描述

  5. like KK%一般情况都会走索引

    explain select * from employees where name like 'LiLei%' and age = 22 and position = 'manager';
    explain select * from employees_copy where name like 'LiLei%' and age = 22 and position = 'manager';
    

    在这里插入图片描述
    在这里插入图片描述

    like KK%用到的是索引下推(Index Condition Pushdown,ICP)

    对于辅助的联合索引(name,age,position),正常情况按照最左前缀原则,select * from employees where name like ‘LiLei%’ and age = 22 and position = 'manager’这种情况只会走name字段索引,因为根据name字过滤完,得到的索引行里的age和position是无序的,无法很好的利用索引

    在mysql5.6之前的版本,这个查询只能在联合索引里匹配到名字是’LiLei’开头的索引,然后拿这些索引对应的主键逐个回表,到主键索引上找出相应的记录,再对比age和position这两个字段是否符合.

    mysql5.6引入了索引下推优化, 可以再索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数. 使用索引下推优化后,上面那个查询再联合索引里匹配到名字是’LiLei’开头的索引之后,同时还会再索引里过滤age和position这两个字段,拿着过滤完剩下的索引对应的主键id再回表查整行数据

    索引下推会减少回表次数,对于innodb引擎的表索引下推只能用于二级索引,innodb的主键索引(聚簇索引)树叶子节点保存的全是整行数据,所以这个时候索引下推并不会起到减少查询全行数据的效果

    为什么范围查找mysql没有用索引下推优化?

    应该是mysql认为范围查找过滤的结果集过大,like KK%在绝大多数情况来看,过滤后的结果集比较小,所以这里mysql选择给like KK%用了索引下推优化,当然这也不是绝对的,有时like KK%也不一定就会走索引下推

    MySQL如何选择合适的索引

    explain select * from employees where name > 'a';
    

    在这里插入图片描述

    如果用name索引需要遍历name字段联合索引树,然后还需要根据遍历出来的主键值去主键索引树里再去查出最终数据,成本比全表扫描还高,可以使用覆盖索引优化,这样只需要遍历name字段的联合索引树就能拿到所有结果.如下:

    explain select name,age,position from employees where name > 'a';
    

    在这里插入图片描述

    explain select * from employees where name < 'a';
    

    在这里插入图片描述

    对于上面这两种name>'a’和name<'a’的执行结果,mysql最终是否选择走索引或者一张表涉及多个索引,mysql最终如何选择索引,我们可以用trace工具来一查究竟,开启trace工具会影响MySQL的性能.只能用于临时分析sql,用完之后立即关闭

    trace工具用法

    set session optimizer_trace='enabled=on',end_markers_in_json=on; -- 开启trace工具
    set session optimizer_trace='enabled=off'; -- 关闭trace工具
    
    • name>‘a’

      select * from employees where name > 'a' order by position;
      select * from information_schema.OPTIMIZER_TRACE;-- trace字段内容
      {"steps": [{"join_preparation": { -- 第一阶段:SQL准备阶段,格式化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` > 'a') order by `employees`.`position`"}] /* steps */} /* join_preparation */},{"join_optimization": { -- 第二阶段:SQL优化阶段"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": [ -- 表依赖详情{"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": 100009, -- 扫描行数"cost": 20421 -- 查询成本} /* 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": ["a < name" -- 索引使用范围] /* ranges */,"index_dives_for_eq_ranges": true,"rowid_ordered": false, -- 使用该索引获取的记录是否按照主键排序"using_mrr": false,"index_only": false, -- 是否使用覆盖索引"rows": 50004, -- 索引扫描的行数"cost": 60006, -- 索引使用成本"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": 100009,"access_type": "scan", -- 访问类型:scan为全表扫描"resulting_rows": 100009,"cost": 20419,"chosen": true, -- 确认选择"use_tmp_table": true}] /* considered_access_paths */} /* best_access_path */,"condition_filtering_pct": 100,"rows_for_plan": 100009,"cost_for_plan": 20419,"sort_cost": 100009,"new_cost_for_plan": 120428,"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": { -- 第三阶段: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": 34,"sort_buffer_size": 262056,"sort_mode": "<sort_key, packed_additional_fields>"} /* filesort_summary */}] /* steps */} /* join_execution */}] /* steps */
      }
      

      结论: 使用索引idx_name_age_position的成本为60006,全表扫描的成本为20421,全表扫描的成本低于索引.所以使用全表扫描

    • name<‘a’

      select * from employees where name < 'a' order by position;
      select * from information_schema.OPTIMIZER_TRACE;-- trace字段
      {"steps": [{"join_preparation": { -- 第一阶段:sql准备阶段,格式化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` < 'a') order by `employees`.`position`"}] /* steps */} /* join_preparation */},{"join_optimization": { -- 第二阶段:sql优化阶段"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": [ -- 表依赖详情{"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": 100009, -- 扫描行数"cost": 20421 -- 查询成本} /* 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": ["name < a" -- 索引使用范围] /* ranges */,"index_dives_for_eq_ranges": true,"rowid_ordered": false, -- 使用该索引获取的记录是否按照主键排序"using_mrr": false,"index_only": false, -- 使用使用覆盖所i一年"rows": 1, -- 索引扫描行数"cost": 2.21, -- 索引使用成本"chosen": true -- 是否使用该索引}] /* range_scan_alternatives */,"analyzing_roworder_intersect": {"usable": false,"cause": "too_few_roworder_scans"} /* analyzing_roworder_intersect */} /* analyzing_range_alternatives */,"chosen_range_access_summary": {"range_access_plan": {"type": "range_scan","index": "idx_name_age_position","rows": 1,"ranges": ["name < a"] /* ranges */} /* range_access_plan */,"rows_for_plan": 1,"cost_for_plan": 2.21,"chosen": true} /* chosen_range_access_summary */} /* range_analysis */}] /* rows_estimation */},{"considered_execution_plans": [{"plan_prefix": [] /* plan_prefix */,"table": "`employees`","best_access_path": { -- 最优访问路径"considered_access_paths": [ -- 最终选择的访问路径{"rows_to_scan": 1,"access_type": "range", -- 访问类型:range"range_details": {"used_index": "idx_name_age_position" -- 使用到的索引} /* range_details */,"resulting_rows": 1,"cost": 2.41,"chosen": true, -- 确定选择"use_tmp_table": true}] /* considered_access_paths */} /* best_access_path */,"condition_filtering_pct": 100,"rows_for_plan": 1,"cost_for_plan": 2.41,"sort_cost": 1,"new_cost_for_plan": 3.41,"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": "idx_name_age_position","plan_changed": false} /* index_order_summary */} /* reconsidering_access_paths_for_index_ordering */},{"refine_plan": [{"table": "`employees`","pushed_index_condition": "(`employees`.`name` < 'a')","table_condition_attached": null}] /* refine_plan */}] /* steps */} /* join_optimization */},{"join_execution": { -- 第三阶段: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": 0,"examined_rows": 0,"number_of_tmp_files": 0,"sort_buffer_size": 262056,"sort_mode": "<sort_key, packed_additional_fields>"} /* filesort_summary */}] /* steps */} /* join_execution */}] /* steps */
      }
      

      总结: 全表扫描的成本为20421,使用索引idx_name_age_position的成本为2.41,索引成本低于全表扫描.所以使用索引.

    常见sql深入优化

    常见优化

    • case1

      explain select * from employees where name = 'LiLei' and position = 'dev' order by age;
      

      在这里插入图片描述

      总结: 利用最左前缀法则:中间字段不能断,因此查询只用到了name索引,从key_len=74也能看出,age索引列用在排序过程中,因此Extra字段里没有 Using filesort

    • case2

      explain select * from employees where name = 'LiLei' order by position;
      

      在这里插入图片描述

      总结: 从explain的执行结果来看:key_len=74,查询使用了name索引,由于用了position进行了排序,跳过了age,Extra字段出现了 Using filesort

    • case3

      explain select * from employees where name = 'LiLei' order by age, position;
      

      在这里插入图片描述

      总结: 查询只用到索引name,age和position用于排序,Extra字段无 Using filesort

    • case4

      explain select * from employees where name = 'LiLei' order by position,age;
      

      在这里插入图片描述

      总结: 和case3中explain的执行结果一样,但是出现了Using filesort,因为索引的创建顺序为name,age,position,但是排序的时候age和position颠倒位置了.

    • case5

      explain select * from employees where name = 'LiLei' and age = 20 order by position,age;
      

      在这里插入图片描述

      总结: 与case4对比,在Extra并未出现Using filesort,因为age为常量,在排序中被优化,所以索引未颠倒,不会出现Using filesort

    • case6

      explain select * from employees where name = 'LiLei' order by age asc, position desc;
      

      在这里插入图片描述

      总结: 虽然排序的字段列与索引顺序一样,且order by 默认升序,这里position desc变成了降序,导致与索引的排序方式不同,从而产生Using filesort,mysql8以上版本有降序索引可以支持该查询方式

    • case7

      explain select * from employees where name in ('LiLei','HanMeimei') order by age, position;
      

      在这里插入图片描述

      总结: 对于排序来说,多个相等条件也是范围查询

    • case8

      explain select * from employees where name > 'a' order by name;
      

      在这里插入图片描述

      考虑用覆盖索引优化:

      explain select name,age,position from employees where name > 'a' order by name;
      

      在这里插入图片描述

    优化总结

    1. MySQL支持两种方式的排序 filesortindex,Using index是指MySQL 扫描索引本身完成排序.index效率高,filesort效率低.
    2. order by满足两种情况会使用Using index
      1. order by 语句使用索引最左前列
      2. 使用where子句与order by子句 条件列组合满足索引最左前列
    3. 尽量在 索引列上完成排序,遵循 **索引建立(索引创建的顺序)**时的最左前缀法则.
    4. 如果order by的条件不在索引列就会产生Using filesort
    5. 能用覆盖索引尽量使用覆盖索引
    6. group by和order by很类似,其实质是 先排序后分组,遵照 索引创建顺序的最左前缀法则.对于group by的优化如果不需要排序的可以加上order by null禁止排序.注意,where 高于having,能写在where中的限定条件就不要去having限定了

    Using filesort文件排序原理

    filesort排序方式
    • 单路排序:是一次性取出满足条件行的所有字段,然后再sort buffer中进行排序;用trace工具可以看到sort_mode信息里显示< sort_key,additional_fields >或者< sort_key,packed_additional_fields >
    • 双路排序(回表排序):是首先根据相应的条件取出相应的 排序字段可以直接定位行数据的ID,然后再sort buffer中进行排序,排序后需要再次取回其他需要的字段;用trace工具可以看到sort_mode信息里显示< sort_key,rowid >

    MySQL通过比较系统变量max_length_for_sort_data(默认1024字节)的大小和需要查询的字段总大小来判断使用那种排序模式

    • 如果字段总长度小于max_length_for_sort_data,那么使用单路排序模式
    • 如果字段总长度大于max_length_for_sort_data,那么使用双路排序模式
    示例验证下两种排序方式
    • 单路排序

      select * from employees where name = 'LiLei' order by position;
      select * from information_schema.OPTIMIZER_TRACE;-- trace字段(只展示排序信息){"join_execution": {"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": 1, -- 预计扫描行数"examined_rows": 1, -- 参与排序的行"number_of_tmp_files": 0, -- 使用临时文件的个数,这个值如果为0代表全部使用的sort_buffer内存排序,否则使用磁盘文件排序"sort_buffer_size": 262056, -- 排序的缓存大小,单位byte"sort_mode": "<sort_key, packed_additional_fields>" -- 排序方式,这里使用的单路排序} /* filesort_summary */}] /* steps */} /* join_execution */}
      
    • 双路排序

      双路排序需要调整max_length_for_sort_data的值

      set max_length_for_sort_data = 10; -- 表中长度肯定超过10字节
      select * from employees where name = 'LiLei' order by position;
      select * from information_schema.OPTIMIZER_TRACE;-- trace字段(只展示排序信息){"join_execution": {"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": 1, -- 预计扫描行数"examined_rows": 1, -- 参与排序的行"number_of_tmp_files": 0, -- 使用临时文件的个数,这个值如果为0代表全部使用sort_buffer内存排序,否则使用磁盘文件排序"sort_buffer_size": 262136, -- 排序缓存的大小,单位是byte"sort_mode": "<sort_key, rowid>" -- 排序方式,这里使用的是双路排序} /* filesort_summary */}] /* steps */} /* join_execution */}
      

    单路排序详细过程:

    1. 从索引name找到第一个满足name = 'LiLei’条件的主键id
    2. 根据主键id去取整条记录,取出所有字段的值,存入sort_buffer中
    3. 从索引name找到下一个满足name = 'LiLei’条件的主键id
    4. 重复步骤2,3直到不满足name = ‘LiLei’
    5. 对sort_buffer中的数据按照字段position进行排序
    6. 返回结果给客户端

    双路排序详细过程:

    1. 从索引name找到第一个满足name = 'LiLei’条件的主键id
    2. 根据主键id去取整条记录,把排序字段position和主键id这两个字段放到sort_buffer中
    3. 从索引name找到下一个满足 name = 'LiLei’条件的主键id
    4. 重复步骤2,3直到不满足name = ‘LiLei’
    5. 对sort_buffer中的字段position和主键id按照字段position进行排序
    6. 遍历排序号的id和字段position,按照id的值 回到原表中取出所有字段的值返回给客户端

    其实对比两个排序模式,单路排序会把所有需要查询的字段都放到sort_buffer中,而双路排序只会把主键和需要排序的字段放到sort_buffer中进行排序,然后再通过主键回到原表查询需要的字段

    如果MySQL 排序内存sort_buffer 配置的比较小并且没有条件继续增加了,可以适当的把max_length_for_sort_data配置小一点,让优化器选择使用 双路排序,可以在sort_buffer中一次排序更多记录,只是需要再根据主键回到原表取数据.

    如果MySQL排序内存有条件可以配置比较大,可以适当增大,max_length_for_sort_data的值,让优化器优先选择使用 单路排序,把需要的字段放到sort_buffer中,这样排序后就会直接从内存里返回查询结果了

    所以,MySQL通过 max_length_for_sort_data这个参数来控制排序,在不同场景使用不同的排序模式,从而提升排序效率.

    注意: 如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但是不能因为这个就随便增大sort_buffer(默认1M),MySQL很多参数设置都是做过优化的,不要轻易调整.

    索引设计原则

    1. 代码先行,索引后上

      不要建完表马上就建立索引,一般应该等到主体业务功能开发完毕,把设计到该表相关sql都要拿出来分析之后再建立索引

    2. 联合索引尽量覆盖条件

      比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where,order by,group by的字段,还要确保这些联合索引顺序尽量满足sql查询的最左前缀原则

    3. 不要再小基数字段上建立索引

      索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共有100W行记录,其中性别字段,其值不是男就是女,那么该字段的基数就是2.如果对这种小基数字段建立索引的话,效率还不如全表扫描,因为你的索引树里就包含男和女两种值,根本没办法进行快速的二分查找,那用索引就没有太大的意义了.一般建立索引,尽量使用那些基数比较大的字段,就是不同的值比较多的字段,那么才能发挥出B+树快速二分查找的优势来.

    4. 长字符串尽量采用前缀索引

      尽量对字段类型较小的列设计索引,因为字段类型较小的话,占用的磁盘空间也会比较小,此时你再搜索的时候性能也会较好

      当然,这个所谓字段类型较小的列,也不使绝对的,很多时候就是要对varchar(255)这种字段建立索引,哪怕多占用一些磁盘空间也是有必要的

      对于这种varchar(255)的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个字符建立索引,就是说对这个字段的每个值的前20个字符放在索引树里,类似于index(name(20),age,position)

      此时在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会先到索引树里根据name的前20个字符去搜索,定位到之后前20个字符的前缀匹配的部分数据之后,再回到聚簇索引提取出来完整的name字段值进行匹配

      假如order by name,那么此时你的name因为再索引树里仅仅包含了前20个字符,所以这个排序是没办法用上索引的,group by也是同理.

    5. where与order by冲突时优先where

      再where和order by出现索引设计冲突时,一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序

      因为大多数情况基于索引进行where筛选往往可以最快速度筛选中你要的少部分数据,然后做排序的成本可能会小很多

    6. 基于慢sql查询做优化

      可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/763258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch深度学习:如何提升遥感影像的地物分类精度?

我国高分辨率对地观测系统重大专项已全面启动&#xff0c;高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成&#xff0c;将成为保障国家安全的基础性和战略性资源。未来10年全球每天获取的观测数据将超过10PB&#xff0c;遥感大数据时…

【算法杂货铺】分治

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 快速排序 &#x1f4c2;75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; &#x1f4c2; 912. 排序数组 - 力扣&#xff08;LeetCode&#xff09; &#x1f4c2; 215. 数组中的第K个最大元素 - 力扣&#xff08;Lee…

突然发现!原来微信批量自动加好友这么简单!

你知道如何更好地管理和利用微信资源&#xff0c;实现客户拓展和沟通吗&#xff1f;下面就教大家一招&#xff0c;帮助大家实现统一管理多个微信号以及批量自动加好友。 想要统一管理多个微信号&#xff0c;不妨试试微信管理系统&#xff0c;不仅可以多个微信号同时登录&#…

数据分析概述、Conda环境搭建及JupyterLab的搭建

1. 数据分析职责概述 当今世界对信息技术的依赖程度在不断加深&#xff0c;每天都会有大量的数据产生&#xff0c;我们经常会感到数据越来越多&#xff0c;但是要从中发现有价值的信息却越来越难。这里所说的信息&#xff0c;可以理解为对数据集处理之后的结果&#xff0c;是从…

【Selenium(五)】

一、鼠标事件 from selenium import webdriver # 导入ActionChains类进行鼠标悬停操作 from selenium.webdriver.common.action_chains import ActionChains import time# 打开一个浏览器 # 法一、添加环境变量重启电脑 # 法二、填写浏览器驱动的绝对路径 driver webdriver.E…

vmare17 安装不可启动的iso镜像系统

由于要测试一个软件&#xff0c;要安装一个Windows11_InsiderPreview_Client_x64_zh-cn_26058.iso 于是在虚拟机里捣鼓一下。但是这个iso好像不能直接启动 这样就无法直接安装了&#xff0c;怎么办呢&#xff0c;可以先用个pe系统引导进去&#xff0c;再在PE系统里安装这个iso…

【免费】如何考取《鲸鸿动能广告初级优化师》认证(详细教程)

鲸鸿动能广告初级优化师认证考试PC网址 初级&#xff1a;鲸鸿动能广告初级优化师认证-华为开发者学堂 (huawei.com) 注&#xff1a;免费认证&#xff0c;里面包含免费的课程&#xff0c;浏览器用Edge。 文章目录 鲸鸿动能广告初级优化师认证考试网址 前言 一、备考流程 二…

软考 网络工程师 每日学习打卡 2024/3/21

学习内容 第8章 网络安全 本章主要讲解网络安全方面的基础知识和应用技术。针对考试应该掌握诸如数据加密、报文认 证、数字签名等基本理论&#xff0c;在此基础上深入理解网络安全协议的工作原理&#xff0c;并能够针对具体的 网络系统设计和实现简单的安全解决方案。 本章共有…

python云上水果超市的设计与实现flask-django-php-nodejs

伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对云上水果超市进行规范而严格是十分有必要的&#xff0c;所以许许多多的信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套云上水果超市&#xff0c;帮助商家进行商品信…

如何为您的网站压缩图像

今天碰到一个客户反馈&#xff0c;他在hostease购买了虚拟主机&#xff0c;创建的WordPress站点图片比较多&#xff0c;后来访问网站&#xff0c;页面上大量的图片加载时间较长&#xff0c;咨询网站图像如何压缩。我们为用户提供网站图像压缩&#xff0c;用户很快完成了设置。在…

【Pt】新建项目时的设置

新建项目时需要在如下界面做一些设置。 一、模板与文件 模板通常选择“PBR - Metallic Roughness Alpha-blend” 文件可以选择fbx&#xff0c;abc&#xff0c;obj等格式的三维模型文件 二、项目设置 2.1 文件分辨率 指的是在软件中的预览效果&#xff0c;分辨率越高预览效果…

使用npm创建一个全局的cli命令,就像vue-cli一样

我们用过vue-cli等工具包&#xff0c;全局安装之后&#xff0c;我们可以直接使用vue create等命令&#xff0c;实际上能够这样使用的原因&#xff0c;就是使用了package.json里面的bin字段注册命令。接下来就以一个脚本文件为例子为大家演示一下bin是如何发挥作用的。 创建项目…

跳过mysql权限验证来修改密码-GPT纯享版

1.打开 MySQL 的配置文件&#xff0c;通常是 my.ini 或 my.cnf。 2.找到 [mysqld] 部分&#xff0c;如果没有则添加。 3.在 [mysqld] 部分中添加一行&#xff1a;skip-grant-tables&#xff0c;这个选项告诉 MySQL 服务器跳过权限验证&#xff0c;允许任何用户连接而不需要密码…

java数据结构与算法基础-----排序------堆排序

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 堆排序是利用堆&#xff08;数据结构&#xff09;设计的排序算法…

基于SSM+Jsp+Mysql的KTV点歌系统

基于SSMJspMysql的KTV点歌系统 基于SSMJspMysql的KTV点歌系统的设计与实现 开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工…

SRC中的一些信息收集姿势

前言 前前后后挖了四个月的EDUSRC&#xff0c;顺利从路人甲升到了网络安全专家&#xff0c;从提交的内容来看大部分还是以中低危为主&#xff0c;主打的就是弱口令和未授权。 在这过程中还是比较浮躁的&#xff0c;因此接下来的时间还是要好好沉淀一下自身的技术&#xff0c;学…

undo log

从这篇「执行一条 SQL 查询语句&#xff0c;期间发生了什么&#xff1f; (opens new window)」中&#xff0c;我们知道了一条查询语句经历的过程&#xff0c;这属于「读」一条记录的过程&#xff0c;如下图&#xff1a; 那么&#xff0c;执行一条 update 语句&#xff0c;期间发…

vue3 报错 require is not defined

问题 require is not defined 原因 vite 不支持require的用法&#xff0c; webpack是支持的 解决 方法一&#xff1a; 更改vite使用语法 vite官网 方法二 安装转换插件vite-plugin-require-transform 仓库地址 参考 关于Vite不能使用require问题 方法二Vite 踩坑 —— …

鸿蒙开发-UI-动画-页面间动画

鸿蒙开发-UI-组件导航-Navigation 鸿蒙开发-UI-组件导航-Tabs 鸿蒙开发-UI-图形-图片 鸿蒙开发-UI-图形-绘制几何图形 鸿蒙开发-UI-图形-绘制自定义图形 鸿蒙开发-UI-图形-页面内动画 鸿蒙开发-UI-图形-组件内转场动画 鸿蒙开发-UI-图形-弹簧曲线动画 文章目录 前言 一、放大缩…

【TD3思路及代码】【自用笔记】

1 组成&#xff08;Target Network Delayed Training&#xff09; Actor网络&#xff1a;这个网络负责根据当前的状态输出动作值。在训练过程中&#xff0c;Actor网络会不断地学习和优化&#xff0c;以输出更合适的动作。Critic网络&#xff1a;TD3中有两个Critic网络&#xff…