高性能mysql(5)-创建高性能索引

1.高性能索引策略

1.1独立的列

“独立的列”是指索引不能是表达式的一部分也不能使函数的参数

select name from actor where id+1=5;

这种情况索引失效。我们应该简化where条件始终将单独的列放在比较符号的一侧。

1.2前缀索引和索引选择性

当索引是很长的字符列的时候,会让索引变得大且慢,除了模拟哈希索引的方法,通常选择索引的部分开始字符,从而提高索引效率。但是这样会降低索引的选择性,索引的选择性是指,不重复的索引值(也称为基数,cardinality)和数据表总记录的比值,基数越大查询效率越高,唯一索引的选择性是1,这是最好的选择性,性能也是最好的。
前缀索引的选择应当保证较高的选择性同时又不能太长。

  • 计算列的完整性
select count(distinct rowname)/count(*) from table;
  • 统计不同长度前缀的选择性
select count(distinct left(rowname,num))/count(*) as selnum,
... 
from table;
  • 创建前缀索引
alter table tablename add key (rowname(num));

在mysql中不能用前缀索引做order by 和 group by,也没有办法做覆盖扫描。

1.3 多列索引

当where有多个and,or条件的时候,应当创建多列索引而不是单列索引,应当将or改写为union更加合适,当explain中的extra 出现using union证明使用了索引合并,说明索引建立的很糟糕。

1.4选择合适的索引列顺序

当不排序或者分组的时候,将选择性最高的列放在前面通常的结果可能是不错的。

1.4聚簇索引

在InnoDB引擎中表数据没有什么聚集,应当尽量的使用自增主键(单调递增的聚簇主键)按主键顺序去插入数据,避免大量的随机IO。

1.5覆盖索引

如果一个索引包含所有需要查询的字段值,我们就称之为覆盖索引。只有B树索引能做覆盖索引,因为覆盖索引必须存储索引列的值。并且不是所有的存储引擎都支持覆盖索引,例如menory就不支持覆盖索引。使用覆盖索引会带来较大的性能提升,因为索引的条目要远远小于数据行大小,可以转化随机IO为顺序IO,减少IO的次数。在InnoDB引擎中二级索引包含了主键值,所以即使索引字段不包含主键,依然能对主键值做覆盖索引。

CREATE TABLE products (
 id int(11) NOT NULL AUTO_INCREMENT ,
 actor  varchar(30)   NOT NULL ,
 sur_name varchar(30) DEFAULT NULL ,
 name  varchar(30)   NULL DEFAULT NULL ,
 title  varchar(30)  S NOT NULL ,
 PRIMARY KEY (id),
 INDEX key_1 (actor, title) USING BTREE 
);

查询

explain select * from products where actor='tom' and title='%asd%';
图片.png

此时代表通过索引访问表,看是否需要全表扫描,这时索引的信息推迟使用,必要时读全表。因为虽然索引覆盖了where中的字段,但是没有覆盖整个查询的字段。所以还是会读表,而且"%%"导致只能使用第一列索引。

优化:

explain select id from products where actor='tom' and title like '%asd%';

图片.png

虽然id没有在where条件的索引范围之内但是依然使用了覆盖索引。
正如上面所说 在InnoDB引擎中二级索引包含了主键值,所以即使索引字段不包含主键,依然能对主键值做覆盖索引。

EXPLAIN SELECT  * FROM products
JOIN (
    SELECT
        id
    FROM
        products
    WHERE
        actor = 'tom'
    AND title LIKE '%asd%'
) AS t1 ON (t1.id = products.id);

这里采用的方式是延迟关联 。

高性能mysql(5)-创建高性能索引_第1张图片
图片.png

这里虽然外层查询的type是ALL但是如果 内层查询的结果集足够小的话。性能会有很大的提升(内层查询前提是结果集足够小),但是如果本身的数据量就不大的话,使用延迟关联反而会因为子查询而带来额外的开销,性能反而下降。

1.6使用索引扫描做排序

当explain的type列值为index则说明使用索引扫描做排序。
限制:

  • 只有当索引列顺序和order by子句顺序完全一致,并且所有列的排序方向一致时,才能使用索引对结果做排序。
  • 如果查询要关联多张表,只有当order by子句的字段全部为第一张表时才能用索引做排序。
  • order by 子句也需要满足最左前缀要求,否则不能做排序。
  • 范围查询可能会导致索引排序失效
    当索引为覆盖索引时按索引排序的效率是高的。否则性能将会下降。

总结

  1. 通常情况下我们使用我们使用选择性高的字段作为索引的第一列,
    但是有时候某一个字段的选择性不高但是却经常出现在where子句中,例如sex,这时我们可以选用sex做为索引字段的第一列,在不需要性别的时候通过sex in ('w','m'),过滤这个索引。但是要注意的是in()的列表不能太长。
  2. 对于范围查询应当避免多个范围条件,而使用IN(),但是滥用IN()会导致优化器组合成指数形式增加,范围条件应当建立在索引的最后一列。
  3. 在同时使用order by 和 limit 查询的时候即使使用了索引在翻页到最后的面的时候,需要花费大量的时间丢弃数据,是一个严重的问题。
select  from profiles where sex='m' order by rating limit 10000,10;

优化此类索引应当使用延迟关联

 select  from profiles 
 join(
   select  from profiles 
   where sex='m' order by rating limit 10000,10;

 ) as t1 on(t1.=profiles.); 
  1. 减少索引和数据碎片
optimize table

对于不支持optimize table的存储引擎可以先改为InnoDB执行后,再修改回去。

你可能感兴趣的:(高性能mysql(5)-创建高性能索引)