索引是对数据库表的一列或多列判断值进行排序的一种结构,使用索引可以快速访问数据库表中的特定信息。索引就好比书的目录,通过目录可以快速搜索到想要查找的内容。
聚集索引其实是一种组织形式,索引键值的逻辑顺序决定了表数据行的物理存储顺序。聚集索引叶子节点存放表中所有行数据记录的信息,所以经常会说数据即索引,索引即数据,这是针对聚集索引来说的,我们在创建一张表时,要显式为表创建一个主键(聚集索引),如果不主动创建主键,那么InnonDB会选择第一个不包含有null值的唯一索引作为主键。如果连唯一索引都没有,InnonDB就会为该表默认生成一个6字节的rowid作为主键。
主键索引就是聚集索引,每张表有且仅有一个主键,可以由表中一个或多个字段组成。主键索引必须满足三个条件,主键值必须唯一;不能包含null值;一定要保证该值是自增属性。使用自增列做主键,可以保证写入的数据也是自增的,这就在很大程度上提高了存取效率。
创建语句:
alter table table_name add primary key(column);
唯一索引是约束条件的一种,其实就是不允许有重复的值,但是可以允许有null值,上面说过表中只能有一个主键,但唯一索引可以有多个。创建唯一约束,会自动创建一个同名的唯一索引,该索引不能单独删除,删除约束会自动删除索引。唯一约束是通过唯一索引来实现数据的唯一。
创建语句:
alter table table_name add unique(column);
SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据。在执行计划中的extra字段会出现 Using index的关键字。
select id from user where name='张三';
注意:如果使用覆盖索引,一定要让select列出所需要的列。坚决不可以直接使用 select*。
对于BLOB、TEXT或者很长的VARCHAR类型的列,为它们前几个字符建立的索引,这样的索引就叫前缀索引。这样建立起来的索引更小。所以查询速度更快。但前缀索引也有它的坏处,它不能在ORDER BY或GROUP Y中使用索引,也不能把它们用作覆盖索引。
创建语句:
alter table table_name add key(column_name(prefix_length));
注意:这里是最关键的参数就是prefix_length,这个值需要根据实际表的内容,来得到合适的索引选择性。
联合索引又叫复合索引,是在表中两个或两个以上创建的索引,利用索引中的附加列,可以缩小检索的段池范围,更快地搜索到数据。创建语法跟普通索引的创建一样,例如,为表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。
哈希索引采用哈希算法,把键值换算成新的哈希值,这里需要注意哈希索引只能进行等值查询,不能进行排序、模糊查找、范围查询等。检索时不需要像B+tree那样从根结点到叶子结点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,查询速度非常快。例如,select *from zs where city_id=100这样一条语句。哈希过程如图6-10所示。
最近运维反应,我们库的SQL有CPU占用过高的报警,看风险类型应该是全盘扫描。
于是到生产打开执行计划看有没有使用上了索引,有没有做全表扫描。使用explain命令查看。
各列的含义如下:
字段 | 描述 |
---|---|
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
type
依次从最优到最差分别为:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
Extra
这一列展示的是额外信息。常见的重要值如下:
对照下面字段,可以发现进行了全盘扫描,使用的索引都为null,一共扫描了3558行。这里就要建立索引来进行优化。
建立索引前:
建立索引后:
优化后可以看到type为ref,即用了普通索引,读取与结果占比也达到了百分百,只扫描了结果部分。提升了查询效率。
参考
《MySQL王者晋级之路》
MySQL explain详解