Mysql索引优化一

秒速五厘米 2023-01-15 02:24 241阅读 0赞

Mysql系列文章目录

我们常常写的sql语句不知道为什么不走索引,接下来咱们谈谈Sql索引优化


文章目录

  • Mysql系列文章目录
  • 一、Mysql如何选择合适的索引?
    • 1.联合索引示例
  • Mysql如何选择合适的索引
  • 常见sql深入优化
    • 案例1:
    • 案例2:
    • 案例3:
    • 案例4:
    • 案例5:
    • 案例6:
    • 案例7:
    • 案例8:
      • 优化总结:
      • Using filesort文件排序原理详解
  • 索引设计原则

一、Mysql如何选择合适的索引?

1.联合索引示例

建表

  1. CREATE TABLE `employees` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  4. `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  5. `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  6. `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  7. PRIMARY KEY (`id`),
  8. KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
  9. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';
  10. INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());
  11. INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW());
  12. INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());
  13. -- 插入一些示例数据
  14. drop procedure if exists insert_emp;
  15. delimiter ;;
  16. create procedure insert_emp()
  17. begin
  18. declare i int;
  19. set i=1;
  20. while(i<=100000)do
  21. insert into employees(name,age,position) values(CONCAT('qjc',i),i,'dev');
  22. set i=i+1;
  23. end while;
  24. end;;
  25. delimiter ;
  26. call insert_emp();

举一个例子:
1、联合索引第一个字段用范围不会走索引

  1. EXPLAIN SELECT * FROM employees
  2. WHERE name > 'LiLei' AND age = 22 AND position ='manager';

在这里插入图片描述
结论:联合索引第一个字段就用范围查找不会走索引,mysql内部可能觉得第一个字段就用范围,结果集应该很大,回表效率不高,还不如就全表扫描,最左前缀法则,使用 where 语句来处理结果,并且查询的列未被索引覆盖,所以如何优化?走索引?
优化:

  1. EXPLAIN SELECT name,age,position FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';

在这里插入图片描述
咱们看上面代码,分析一下原因,因为name是范围查找,第一条sql走了全表,第二条sql咱们走了覆盖索引,为什么走覆盖索引,因为我能在二级索引的树种拿到需要的结果,不需要走聚簇索引,相当于也走了二级索引全部索引,只根据name查找,但是name没有走索引,我是怎么知道的哪?上一章咱们说了计算规则, name 算法 3* 24 +2 =74 所以只查了name走了最左前缀匹配

2、强制走索引

  1. EXPLAIN SELECT * FROM employees
  2. force index(idx_name_age_position) WHERE name > 'LiLei' AND age = 22 AND position ='manager';

在这里插入图片描述
结论:虽然使用了强制走索引让联合索引第一个字段范围查找也走索引,扫描的行rows看上去也少了点,但是最终查找效率不一定比全表扫描高,因为回表效率不高
接下来实验一下:

  1. -- 关闭查询缓存
  2. set global query_cache_size=0;
  3. set global query_cache_type=0;
  4. -- 执行时间0.914s
  5. SELECT * FROM employees WHERE name > 'LiLei';
  6. -- 执行时间0.1.841s
  7. SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'LiLei';

事实证明,强制索引不一定比扫聚簇索引全表快,因为mysql底层算法会计算cost时间,mysql会自动选择最优的,但是你走强制索引,但是先去走了二级索引树,然后在回表查询聚簇索引扫描,我还不如直接扫描聚簇索引表来的快!!!!

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

  1. EXPLAIN SELECT * FROM employees
  2. WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';

在这里插入图片描述
如上图我的表里有60w数据还是走了索引,经过我个人测试in的数量导致全表扫描如果in的过多的条件,mysql底层算法会计算,会走全表扫描

  1. EXPLAIN SELECT * FROM employees
  2. WHERE (name = 'LiLei' or name = 'HanMeimei') AND age = 22 AND position ='manager';

在这里插入图片描述
做一个小实验,将employees 表复制一张employees_copy的表,里面保留两三条记录

  1. EXPLAIN SELECT * FROM employees_copy
  2. WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';

在这里插入图片描述
他会走全表,因为数据量小,走二级索引,还不如走聚簇索引来的快也不用回表操作

  1. EXPLAIN SELECT * FROM employees_copy
  2. WHERE (name = 'LiLei' or name = 'HanMeimei') AND age = 22 AND position ='manager';

在这里插入图片描述
明明可以走索引的,为什么没有走索引,还是一句话我表里就三条数据,我还不如走全表!!!!

4、like KK% 一般情况都会走索引

  1. EXPLAIN SELECT * FROM employees
  2. WHERE name like 'LiLei%' AND age = 22 AND position ='manager';

在这里插入图片描述

  1. EXPLAIN SELECT * FROM employees_copy
  2. WHERE name like 'LiLei%' AND age = 22 AND position ='manager';

在这里插入图片描述
为什么like会走索引?它符合我们的最左前缀查询类似于name=Lilei所以会走索引,并不是范围查询,Using index condition:查询的列不完全被索引覆盖,where条件中是一个前导列的范围;
这里给大家补充一个概念,索引下推(Index Condition Pushdown,ICP), like KK%其实就是用到了索引下推优化
在这里插入图片描述
什么是索引下推了?

对于辅助的联合索引(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这两个字段的值是否符合。回表拿到最底下的id然后去找主键索引回表操作,然后再继续这样反复执行!! MySQL
5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。使用了索引下推优化后,上面那个查询在联合索引里匹配到名字是 ‘LiLei’ 开头的索引之后,同时还会在索引里过滤age和position这两个字段,拿着过滤完剩下的索引对应的主键id再回表查整行数据。 索引下推会减少回表次数,对于innodb引擎的表索引下推只能用于二级索引,innodb的主键索引(聚簇索引)树叶子节点上保存的是全行数据,所以这个时候索引下推并不会起到减少查询全行数据的效果。

为什么范围查找Mysql没有用索引下推优化?
估计应该是Mysql认为范围查找过滤的结果集过大,like KK% 在绝大多数情况来看,过滤后的结果集比较小,所以这里Mysql选择给 like KK% 用了索引下推优化,当然这也不是绝对的,有时like KK% 也不一定就会走索引下推。如果数据量过大,mysql底层计算,我还不如走主键索引全表扫描,不然还会有回表操作

Mysql如何选择合适的索引

  1. EXPLAIN select * from employees where name > 'a';

在这里插入图片描述
possible——keys进行计算估算出要走索引,但是经过计算之后,没有走索引,如果用name索引需要遍历name字段联合索引树,然后还需要根据遍历出来的主键值去主键索引树里再去查出最终数据,成本比全表扫描还高,可以用覆盖索引优化,这样只需要遍历name字段的联合索引树就能拿到所有结果,如下:

  1. EXPLAIN select name,age,position from employees where name > 'a' ;

在这里插入图片描述
着走了覆盖索引,我在二级索引树,我就可以拿到结果集,没必要回表操作,没必要全表

  1. EXPLAIN select * from employees where name > 'zzz' ;

在这里插入图片描述
对于上面这两种 name>‘a’ 和 name>‘zzz’ 的执行结果,mysql最终是否选择走索引或者一张表涉及多个索引,mysql最终如何选择索引,我们可以用trace工具来一查究竟,开启trace工具会影响mysql性能,所以只能临时分析sql使用,用完之后立即关闭,查询的列不完全被索引覆盖,where条件中是一个前导列的范围
trace工具用法:

  1. set session optimizer_trace="enabled=on",end_markers_in_json=on; --开启trace
  2. select * from employees where name > 'a' order by position;
  3. SELECT * FROM information_schema.OPTIMIZER_TRACE; --立马执行
  4. 查看trace字段:
  5. {
  6. "steps": [
  7. {
  8. "join_preparation": { --第一阶段:SQL准备阶段,格式化sql
  9. "select#": 1,
  10. "steps": [
  11. {
  12. "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`"
  13. }
  14. ] /* steps */
  15. } /* join_preparation */
  16. },
  17. {
  18. "join_optimization": { --第二阶段:SQL优化阶段
  19. "select#": 1,
  20. "steps": [
  21. {
  22. "condition_processing": { --条件处理
  23. "condition": "WHERE",
  24. "original_condition": "(`employees`.`name` > 'a')",
  25. "steps": [
  26. {
  27. "transformation": "equality_propagation",
  28. "resulting_condition": "(`employees`.`name` > 'a')"
  29. },
  30. {
  31. "transformation": "constant_propagation",
  32. "resulting_condition": "(`employees`.`name` > 'a')"
  33. },
  34. {
  35. "transformation": "trivial_condition_removal",
  36. "resulting_condition": "(`employees`.`name` > 'a')"
  37. }
  38. ] /* steps */
  39. } /* condition_processing */
  40. },
  41. {
  42. "substitute_generated_columns": {
  43. } /* substitute_generated_columns */
  44. },
  45. {
  46. "table_dependencies": [ --表依赖详情
  47. {
  48. "table": "`employees`",
  49. "row_may_be_null": false,
  50. "map_bit": 0,
  51. "depends_on_map_bits": [
  52. ] /* depends_on_map_bits */
  53. }
  54. ] /* table_dependencies */
  55. },
  56. {
  57. "ref_optimizer_key_uses": [
  58. ] /* ref_optimizer_key_uses */
  59. },
  60. {
  61. "rows_estimation": [ --预估表的访问成本
  62. {
  63. "table": "`employees`",
  64. "range_analysis": {
  65. "table_scan": { --全表扫描情况
  66. "rows": 10123, --扫描行数
  67. "cost": 2054.7 --查询成本
  68. } /* table_scan */,
  69. "potential_range_indexes": [ --查询可能使用的索引
  70. {
  71. "index": "PRIMARY", --主键索引
  72. "usable": false,
  73. "cause": "not_applicable"
  74. },
  75. {
  76. "index": "idx_name_age_position", --辅助索引
  77. "usable": true,
  78. "key_parts": [
  79. "name",
  80. "age",
  81. "position",
  82. "id"
  83. ] /* key_parts */
  84. }
  85. ] /* potential_range_indexes */,
  86. "setup_range_conditions": [
  87. ] /* setup_range_conditions */,
  88. "group_index_range": {
  89. "chosen": false,
  90. "cause": "not_group_by_or_distinct"
  91. } /* group_index_range */,
  92. "analyzing_range_alternatives": { --分析各个索引使用成本
  93. "range_scan_alternatives": [
  94. {
  95. "index": "idx_name_age_position",
  96. "ranges": [
  97. "a < name" --索引使用范围
  98. ] /* ranges */,
  99. "index_dives_for_eq_ranges": true,
  100. "rowid_ordered": false, --使用该索引获取的记录是否按照主键排序
  101. "using_mrr": false,
  102. "index_only": false, --是否使用覆盖索引
  103. "rows": 5061, --索引扫描行数
  104. "cost": 6074.2, --索引使用成本
  105. "chosen": false, --是否选择该索引
  106. "cause": "cost"
  107. }
  108. ] /* range_scan_alternatives */,
  109. "analyzing_roworder_intersect": {
  110. "usable": false,
  111. "cause": "too_few_roworder_scans"
  112. } /* analyzing_roworder_intersect */
  113. } /* analyzing_range_alternatives */
  114. } /* range_analysis */
  115. }
  116. ] /* rows_estimation */
  117. },
  118. {
  119. "considered_execution_plans": [
  120. {
  121. "plan_prefix": [
  122. ] /* plan_prefix */,
  123. "table": "`employees`",
  124. "best_access_path": { --最优访问路径
  125. "considered_access_paths": [ --最终选择的访问路径
  126. {
  127. "rows_to_scan": 10123,
  128. "access_type": "scan", --访问类型:为scan,全表扫描
  129. "resulting_rows": 10123,
  130. "cost": 2052.6,
  131. "chosen": true, --确定选择
  132. "use_tmp_table": true
  133. }
  134. ] /* considered_access_paths */
  135. } /* best_access_path */,
  136. "condition_filtering_pct": 100,
  137. "rows_for_plan": 10123,
  138. "cost_for_plan": 2052.6,
  139. "sort_cost": 10123,
  140. "new_cost_for_plan": 12176,
  141. "chosen": true
  142. }
  143. ] /* considered_execution_plans */
  144. },
  145. {
  146. "attaching_conditions_to_tables": {
  147. "original_condition": "(`employees`.`name` > 'a')",
  148. "attached_conditions_computation": [
  149. ] /* attached_conditions_computation */,
  150. "attached_conditions_summary": [
  151. {
  152. "table": "`employees`",
  153. "attached": "(`employees`.`name` > 'a')"
  154. }
  155. ] /* attached_conditions_summary */
  156. } /* attaching_conditions_to_tables */
  157. },
  158. {
  159. "clause_processing": {
  160. "clause": "ORDER BY",
  161. "original_clause": "`employees`.`position`",
  162. "items": [
  163. {
  164. "item": "`employees`.`position`"
  165. }
  166. ] /* items */,
  167. "resulting_clause_is_simple": true,
  168. "resulting_clause": "`employees`.`position`"
  169. } /* clause_processing */
  170. },
  171. {
  172. "reconsidering_access_paths_for_index_ordering": {
  173. "clause": "ORDER BY",
  174. "steps": [
  175. ] /* steps */,
  176. "index_order_summary": {
  177. "table": "`employees`",
  178. "index_provides_order": false,
  179. "order_direction": "undefined",
  180. "index": "unknown",
  181. "plan_changed": false
  182. } /* index_order_summary */
  183. } /* reconsidering_access_paths_for_index_ordering */
  184. },
  185. {
  186. "refine_plan": [
  187. {
  188. "table": "`employees`"
  189. }
  190. ] /* refine_plan */
  191. }
  192. ] /* steps */
  193. } /* join_optimization */
  194. },
  195. {
  196. "join_execution": { --第三阶段:SQL执行阶段
  197. "select#": 1,
  198. "steps": [
  199. ] /* steps */
  200. } /* join_execution */
  201. }
  202. ] /* steps */
  203. }

结论:全表扫描的成本低于索引扫描,所以mysql最终选择全表扫描,具体best_access_path 他选择了全表扫描

  1. select * from employees where name > 'zzz' order by position;
  2. SELECT * FROM information_schema.OPTIMIZER_TRACE;
  3. 查看trace字段可知索引扫描的成本低于全表扫描,所以mysql最终选择索引扫描
  4. set session optimizer_trace="enabled=off"; --关闭trace

常见sql深入优化

Order by与Group by优化

案例1:

在这里插入图片描述
分析:
利用最左前缀法则:中间字段不能断,因此查询用到了name索引,从key_len=74也能看出,age索引列用在排序过程中,因为Extra字段里没有using filesort 将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。这种情况下一般也是要考虑使用索引来优化的。

案例2:

在这里插入图片描述
分析:
从explain的执行结果来看:key_len=74,查询使用了name索引,由于用了position进行排序,跳过了age,出现了Using filesort将用外部排序而不是索引排序。

案例3:

在这里插入图片描述
分析:
查找只用到索引name,age和position用于排序,无Using filesort。因为索引本身存储的时候就是排好序的,无需再进行读取到内存,或用磁盘排序

案例4:

在这里插入图片描述
分析:
和案例 3中explain的执行结果一样,但是出现了Using filesort,因为索引的创建顺序为name,age,position,但是排序的时候age和position颠倒位置了。

案例5:

在这里插入图片描述
分析:
与Case 4对比,在Extra中并未出现Using filesort,因为age为常量,在排序中被优化,所以索引未颠倒,不会出现Using filesort。

案例6:

在这里插入图片描述
分析:
虽然排序的字段列与索引顺序一样,且order by默认升序,这里position desc变成了降序,导致与索引的排序方式不同,从而产生Using filesort。Mysql8以上版本有降序索引可以支持该种查询方式。

案例7:

在这里插入图片描述
分析:
对于排序来说,多个相等条件也是范围查询

案例8:

在这里插入图片描述
可以用覆盖索引优化
在这里插入图片描述

优化总结:

  1. MySQL支持两种方式的排序filesort和index,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, 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 ,那么使用 双路排序模·式。

示例验证下各种排序方式:
在这里插入图片描述
查看下这条sql对应trace结果如下(只展示排序部分):

  1. set session optimizer_trace="enabled=on",end_markers_in_json=on; --开启trace
  2. select * from employees where name = 'zhuge' order by position;
  3. select * from information_schema.OPTIMIZER_TRACE;
  4. trace排序部分结果:
  5. "join_execution": { --Sql执行阶段
  6. "select#": 1,
  7. "steps": [
  8. {
  9. "filesort_information": [
  10. {
  11. "direction": "asc",
  12. "table": "`employees`",
  13. "field": "position"
  14. }
  15. ] /* filesort_information */,
  16. "filesort_priority_queue_optimization": {
  17. "usable": false,
  18. "cause": "not applicable (no LIMIT)"
  19. } /* filesort_priority_queue_optimization */,
  20. "filesort_execution": [
  21. ] /* filesort_execution */,
  22. "filesort_summary": { --文件排序信息
  23. "rows": 10000, --预计扫描行数
  24. "examined_rows": 10000, --参与排序的行
  25. "number_of_tmp_files": 3, --使用临时文件的个数,这个值如果为0代表全部使用的sort_buffer内存排序,否则使用的磁盘文件排序
  26. "sort_buffer_size": 262056, --排序缓存的大小,单位Byte
  27. "sort_mode": "<sort_key, packed_additional_fields>" --排序方式,这里用的单路排序
  28. } /* filesort_summary */
  29. }
  30. ] /* steps */
  31. } /* join_execution */

我们先看单路排序的详细过程:

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

    set max_length_for_sort_data = 10; —employees表所有字段长度总和肯定大于10字节
    select from employees where name = ‘zhuge’ order by position;
    select
    from information_schema.OPTIMIZER_TRACE;

    trace排序部分结果:
    “join_execution”: {

    1. "select#": 1,
    2. "steps": [
    3. {
    4. "filesort_information": [
    5. {
    6. "direction": "asc",
    7. "table": "`employees`",
    8. "field": "position"
    9. }
    10. ] /* filesort_information */,
    11. "filesort_priority_queue_optimization": {
    12. "usable": false,
    13. "cause": "not applicable (no LIMIT)"
    14. } /* filesort_priority_queue_optimization */,
    15. "filesort_execution": [
    16. ] /* filesort_execution */,
    17. "filesort_summary": {
    18. "rows": 10000,
    19. "examined_rows": 10000,
    20. "number_of_tmp_files": 2,
    21. "sort_buffer_size": 262136,
    22. "sort_mode": "<sort_key, rowid>" --排序方式,这里用的双路排序
    23. } /* filesort_summary */
    24. }
    25. ] /* steps */
    26. } /* join_execution */
  1. set session optimizer_trace="enabled=off"; --关闭trace

我们再看下双路排序的详细过程:

  1. 从索引 name 找到第一个满足 name = ‘zhuge’ 的主键id
  2. 根据主键 id 取出整行,把排序字段 position 和主键 id 这两个字段放到 sort buffer 中
  3. 从索引 name 取下一个满足 name = ‘zhuge’ 记录的主键 id
  4. 重复 3、4 直到不满足 name = ‘zhuge’
  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、不要在小基数字段上建立索引
索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,其值不是男就是女,那么该字段的基数就是2。

如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没法进行快速的二分查找,那用索引就没有太大的意义了。

一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。

4、长字符串我们可以采用前缀索引
尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会比较小,此时你在搜索的时候性能也会比较好一点。

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

对于这种 varchar(255) 的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于 KEY 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去设计索引,还是针对order by设计索引?到底是让where去用上索引,还是让order by用上索引?

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

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

6、基于慢sql查询做优化
可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。监控执行慢的sql 10秒

发表评论

表情:
评论列表 (有 0 条评论,241人围观)

还没有评论,来说两句吧...

相关阅读

    相关 索引优化()

    对于任何一个后端的开发人员来说,索引优化是必备的技能,接下来会通过两篇文章来介绍如何优化索引以及如何设计好的索引。 一、数据准备 创建一个员工表,除了主键索引外,还额外

    相关 MySQL索引优化

    1. 联合索引和模糊查询的常见问题 1.1 联合索引第一个字段用范围导致不会走索引的情况 联合索引第一个字段就用范围查找不会走索引,mysql内部可能觉得第一个字段