重温索引优化

1. 索引失效

1.1 普通索引

场景一:SELECT * FROM user_info WHERE id + 1 = 2;

索引的列如果是表达式的一部分或者是函数的参数,则失效。

场景二:SELECT * FROM user_info WHERE name LIKE '%ook';

like查询前面部分未输入,以%开头无法命中索引。
可以使用覆盖索引(type=index)。

场景三:SELECT * FROM user_info WHERE id <> 1;

查询条件使用不等式(<>或者!=)。

场景四:SELECT * FROM user_info WHERE name = 1;

查询条件类型不一致。
字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。

场景五:

mysql查询单表时,查询得到的结果集占数据总量很大比例30%,mysql会认为全表扫描会优于索引,则不走索引。
不要给’性别’等增加索引。如果某个数据列里包含了均是"0/1"或“Y/N”等值,即包含着许多重复的值,就算为它建立了索引,索引效果不会太好,还可能导致全表扫描。

场景六:

使用or连接的两个字段,如果两个字段都是索引字段索引才会生效(index_merge),否则索引无效。

场景七:

使用NOT IN的时候索引可能会失效,进而使用全表查询。

1.2 复合索引

场景一:

不使用索引首列当查询条件(最左前缀)。


2. EXPLAIN语句

CREATE TABLE `user_info` (
  `id`   BIGINT(20)  NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL DEFAULT '',
  `age`  INT(11)              DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name_index` (`name`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;
CREATE TABLE `order_info` (
  `id`           BIGINT(20)  NOT NULL AUTO_INCREMENT,
  `user_id`      BIGINT(20)           DEFAULT NULL,
  `product_name` VARCHAR(50) NOT NULL DEFAULT '',
  `productor`    VARCHAR(30)          DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4;

2.1 select_type

select_type 表示了查询的类型, 它的常用取值有:

2.2 type

type 字段比较重要, 它提供了判断查询是否高效的重要依据依据. 通过 type 字段, 我们判断此次查询是 全表扫描 还是 索引扫描 等。

  • system: 表中只有一条数据. 这个类型是特殊的 const 类型.
  • const: 针对主键或唯一索引的等值查询扫描, 最多只返回一行数据. const 查询速度非常快, 因为它仅仅读取一次即可.
  • eq_ref: 此类型通常出现在***多表的 join 查询***, 表示对于前表的每一个结果, 都只能匹配到后表的一行结果. 并且查询的比较操作通常是 =, 查询效率较高. 针对主键/唯一键
    重温索引优化_第1张图片
  • ref: 此类型通常出现在**[多表的 join 查询], 针对于非唯一或非主键索引等值查询, 或者是使用了 最左前缀 规则索引的查询.**
小表全表扫描,大表使用索引(可以是非唯一索引)。

重温索引优化_第2张图片

  • range: 表示使用索引范围查询, 通过索引字段范围获取表中部分数据记录. 这个类型通常出现在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中.当 type 是 range 时, 那么 EXPLAIN 输出的 ref 字段为 NULL, 并且 key_len 字段是此次查询中使用到的索引的最长的那个.
    重温索引优化_第3张图片
  • index: 表示全索引扫描(full index scan), 和 ALL 类型类似, 只不过 ALL 类型是全表扫描, 而 index 类型则仅仅扫描所有的索引, 而不扫描数据.index 类型通常出现在: 所要查询的数据直接在索引树中就可以获取到, 而不需要扫描数据. 当是这种情况时, Extra 字段 会显示 Using index.
    重温索引优化_第4张图片
  • ALL: 表示全表扫描, 这个类型的查询是性能最差的查询之一. 通常来说, 我们的查询不应该出现 ALL 类型的查询, 因为这样的查询在数据量大的情况下, 对数据库的性能是巨大的灾难. 如一个查询是 ALL 类型查询, 那么一般来说可以对相应的字段添加索引来避免.下面是一个全表扫描的例子, 可以看到, 在全表扫描时, possible_keys 和 key 字段都是 NULL, 表示没有使用到索引, 并且 rows 十分巨大, 因此整个查询效率是十分低下的.
    重温索引优化_第5张图片
    通常来说, 不同的 type 类型的性能关系如下:
ALL < index < range ~ index_merge < ref < eq_ref < const < system

ALL 类型因为是全表扫描, 因此在相同的查询条件下, 它是速度最慢的.
而 index 类型的查询虽然不是全表扫描, 但是它扫描了所有的索引, 因此比 ALL 类型的稍快.
后面的几种类型都是利用了索引来查询数据, 因此可以过滤部分或大部分数据, 因此查询效率就比较高了.

2.3 key

此字段是 MySQL 在当前查询时所真正使用到的索引.

2.4 key_length

表示查询优化器使用了索引的字节数. 这个字段可以评估组合索引是否完全被使用, 或只有最左部分字段被使用到.

2.5 ref

索引中查找值用到的列/常量(const)

2.6 rows

rows 也是一个重要的字段. MySQL 查询优化器根据统计信息, 估算 SQL 要查找到结果集需要扫描读取的数据行数.
这个值非常直观显示 SQL 的效率好坏, 原则上 rows 越少越好.

2.7 extra

  • using filesort:表示MySQL需额外排序操作, 不能通过索引顺序达到排序效果
    ,对order by进行优化
  • using where
  • using index

3. 优化建议

  • SELECT语句务必指明字段名称,可能使用到覆盖索引。
  • 当只需要一条数据的时候,使用limit 1

使EXPLAIN中type列达到const类型。

  • 如果排序字段没有用到索引,就尽量少排序,否则需要额外进行排序。
  • 如果限制条件中其他字段没有索引,尽量少用or

很多时候使用 union all 或者是union(必要的时候)的方式来代替“or”会得到更好的效果。

  • 尽量用union all代替union

union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。

  • 区分in和exists, not in和not exists
select * from 表A where id in (select id from 表B)
上面sql语句相当于

select * from 表A where exists(select * from 表B where 表B.id=表A.id)
区分inexists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。
  • 使用合理的分页方式以提高分页的效率
select id,name from product limit 866613, 20
使用上述sql语句做分页的时候,可能有人会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。

优化的方法如下:可以取前一页的最大行数的id,然后根据这个最大的id来限制下一页的起点。比如此列中,上一页最大的id是866612sql可以采用如下的写法:

select id,name from product where id> 866612 limit 20
或者 使用between语句
  • 避免在 where 子句中对字段进行 null 值判断

对于null的判断会导致引擎放弃使用索引而进行全表扫描,在where子句中使用 IS NULL 或 IS NOT NULL 判断,索引将被放弃使用,会进行全表查询。
设置默认值

  • 关于JOIN优化

LEFT JOIN A表为驱动表
INNER JOIN MySQL会自动找出那个数据少的表作用驱动表
RIGHT JOIN B表为驱动表
尽量使用inner join,避免left join
利用小表去驱动大表

你可能感兴趣的:(MySQL,mysql,索引,sql)