简介

索引是对数据库表的一列或多列判断值进行排序的一种结构,使用索引可以快速访问数据库表中的特定信息。索引就好比书的目录,通过目录可以快速搜索到想要查找的内容。

索引类型
1.B+Tree结构索引
聚集索引和非聚集索引

聚集索引其实是一种组织形式,索引键值的逻辑顺序决定了表数据行的物理存储顺序。聚集索引叶子节点存放表中所有行数据记录的信息,所以经常会说数据即索引,索引即数据,这是针对聚集索引来说的,我们在创建一张表时,要显式为表创建一个主键(聚集索引),如果不主动创建主键,那么InnonDB会选择第一个不包含有null值的唯一索引作为主键。如果连唯一索引都没有,InnonDB就会为该表默认生成一个6字节的rowid作为主键。

1.1 主键索引和唯一索引

主键索引就是聚集索引,每张表有且仅有一个主键,可以由表中一个或多个字段组成。主键索引必须满足三个条件,主键值必须唯一;不能包含null值;一定要保证该值是自增属性。使用自增列做主键,可以保证写入的数据也是自增的,这就在很大程度上提高了存取效率。

创建语句:

alter table table_name add primary key(column);

唯一索引是约束条件的一种,其实就是不允许有重复的值,但是可以允许有null值,上面说过表中只能有一个主键,但唯一索引可以有多个。创建唯一约束,会自动创建一个同名的唯一索引,该索引不能单独删除,删除约束会自动删除索引。唯一约束是通过唯一索引来实现数据的唯一。

创建语句:

alter table table_name add unique(column);
1.2 覆盖索引

SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。在执行计划中的extra字段会出现 Using index的关键字。

select id from user where name='张三';

注意:如果使用覆盖索引,一定要让select列出所需要的列。坚决不可以直接使用 select*。

1.3 前缀索引

对于BLOB、TEXT或者很长的VARCHAR类型的列,为它们前几个字符建立的索引,这样的索引就叫前缀索引。这样建立起来的索引更小。所以查询速度更快。但前缀索引也有它的坏处,它不能在ORDER BY或GROUP Y中使用索引,也不能把它们用作覆盖索引。

创建语句:

alter table table_name add key(column_name(prefix_length));

注意:这里是最关键的参数就是prefix_length,这个值需要根据实际表的内容,来得到合适的索引选择性。

1.4 联合索引

联合索引又叫复合索引,是在表中两个或两个以上创建的索引,利用索引中的附加列,可以缩小检索的段池范围,更快地搜索到数据。创建语法跟普通索引的创建一样,例如,为表t的c1、c2字段。

创建一个联合索引语句:

create index idx_c1_c2 on t(c1,c2);

联合索引的使用过程中,必须要满足最左前缀匹配原则。一般是选择性高的列放在前面。一条查询语句可以使用索引中的一部分,但必须从最左侧开始。

可以用到c1索引和c1,c2索引。

一下查询可以使用到索引:

select*from t where c1=某值;
select*from t where c2=某值 and c1=某值;
select*from t where c1=某值 and c2 in(某值,某值);
select*from t order by c1,c2;
select*from t where c1=某值 order by c2;

反之,使用不到的情况:

select*from t where c2=某值;
select*from t where c2=某值 order by c1;

还有一种特殊的情况:

select*from t where c1=某值 or c2=某值;

虽然c1字段在前,但是这种情况是不能使用到索引的。

这种情况下可以在c1、c2字段上面建两个单列索引。

注意:尽量在生产环境中,让程序端多做一些判断,不要让数据库做各种运算。尽量避免在SQL语句中出现or关键字。多列中可以考虑使用union。

2.哈希索引

哈希索引采用哈希算法,把键值换算成新的哈希值,这里需要注意哈希索引只能进行等值查询,不能进行排序、模糊查找、范围查询等。检索时不需要像B+tree那样从根结点到叶子结点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,查询速度非常快。例如,select *from zs where city_id=100这样一条语句。哈希过程如图6-10所示。



mysql 聚合 where在列表中_主键

调优

最近运维反应,我们库的SQL有CPU占用过高的报警,看风险类型应该是全盘扫描。

mysql 聚合 where在列表中_主键_02

于是到生产打开执行计划看有没有使用上了索引,有没有做全表扫描。使用explain命令查看。

mysql 聚合 where在列表中_数据库_03

各列的含义如下:



字段

描述

id

SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符

selecet_type

select查询的类型

table

表名

partitions

查询将从中匹配记录的分区,对于非分区 table,该值为NULL

type

访问类型 ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)

possible_keys

可能会用到的索引,若查询的字段存在索引,则会被列出,不一定被查询使用

key

key列显示MySQL实际决定使用的键(索引),必然包含在possible_keys中

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度

ref

显示了在key列记录的索引中,表查找值所用到的列或常量,值:const,func,NULL,字段名

rows

表扫描行数

filtered

返回结果的行占需要读到的行(rows列的值)的百分比

Extra

关于MYSQL如何解析查询的额外信息

重点使用列解释

select_type

  • simple:简单查询。查询不包含子查询和union
  • primary:复杂查询中最外层的 select
  • subquery:包含在 select 中的子查询(不在 from 子句中)
  • derived:包含在 from 子句中的子查询。MySQL会将结果存放在一个临时表中,也称为派生表(derived的英文含义)
  • union:在 union 中的第二个和随后的 select
  • union result:从 union 临时表检索结果的 select

type

依次从最优到最差分别为:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

  • system:该 table 只有一行(=系统 table)
  • const :const 该 table 最多具有一个匹配行,该行在查询开始时读取。因为只有一行,所以优化器的其余部分可以将这一行中列的值视为常量。
  • eq_ref:primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录。这可能是在 const 之外最好的联接类型了,简单的 select 查询不会出现这种 type。
  • ref:而是使用普通索引或者唯一性索引的部分前缀
  • fulltext:进行全文索引检索
  • ref_or_null :类似ref,但是可以搜索值为NULL的行。
  • index_merge:表示使用了索引合并的优化方法
  • unique_subquery:查询中同时使用两个(或更多)索引,然后对索引结果进行合并(merge),再读取表数据。
  • index_subquery:子查询中的返回结果字段组合是一个索引(或索引组合),但不是一个主键或唯一索引。
  • range:范围扫描通常出现在 in(), between ,> ,<, >= 等操作中。使用一个索引来检索给定范围的行。
  • index:和ALL一样,不同就是mysql只需扫描索引树,这通常比ALL快一些
  • ALL:即全表扫描,意味着mysql需要从头到尾去查找所需要的行。通常情况下这需要增加索引来进行优化了

Extra

这一列展示的是额外信息。常见的重要值如下:

  • distinct:一旦mysql找到了与行相联合匹配的行,就不再搜索了
  • Using index:这发生在对表的请求列都是同一索引的部分的时候,返回的列数据只使用了索引中的信息,而没有再去访问表中的行记录。是性能高的表现。
  • Using where:mysql服务器将在存储引擎检索行后再进行过滤。就是先读取整行数据,再按 where 条件进行检查,符合就留下,不符合就丢弃。
  • Using temporary:mysql需要创建一张临时表来处理查询。出现这种情况一般是要进行优化的,首先是想到用索引来优化。
  • Using filesort:mysql 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。此时mysql会根据联接类型浏览所有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。这种情况下一般也是要考虑使用索引来优化的。

对照下面字段,可以发现进行了全盘扫描,使用的索引都为null,一共扫描了3558行。这里就要建立索引来进行优化。

建立索引前:

mysql 聚合 where在列表中_mysql_04

 建立索引后:

mysql 聚合 where在列表中_b树_05

优化后可以看到type为ref,即用了普通索引,读取与结果占比也达到了百分百,只扫描了结果部分。提升了查询效率。

总结
合理创建索引的三个经常:
  1. 经常被查询的列(一般放在where条件后面)。
  2. 经常用于表连接的列。
  3. 经常排序分组的列。(order by 或者 group by 后面的字段)。
索引创建的四个不要:
  1. 选择性低的字段不要创建索引(例如,性别sex,状态status)。
  2. 很少查询的列不要创建索引(项目初期就有确定好)。
  3. 大数据类型字段不要创建索引。
  4. 尽量避免不要使用NULL,应该指定列为 NOT NULL(在MySQL中。含有空值的列很难进行查询优化,它们会使得索引、索引的统计信息及比较运算更加复杂,可以使用空字符串代替空值)。
索引的优点:
  1. 提高数据检索效率。
  2. 提高聚合函数效率。
  3. 提高排序效率
  4. 使用覆盖索引可避免回表
使用不到索引的情况:
  1. 通过索引扫描的行记录数超过全表的30%,优化器就不会走索引,而变成全表扫描。
  2. 联合索引中,第一个查询条件不是最左索引列。
  3. 联合索引中,第一个索引列使用范围查询,只能使用到部分索引,有ICP出现(范围查询是指<、=、<=、BETWEEN and)。
  4. 联合索引中,第一个查询条件不是最左前缀列。
  5. 模糊查询条件列最左以通配符%开始(可以考虑放到子查询里面)。
  6. 两个单列索引,一个用于检索,一个用于排序。这种情况下只能使用到一个索引。因为查询语句中最多只能使用一个索引,考虑建立联合索引。
  7. 查询字段上面有索引,但是使用了函数运算。