索引

索引是帮助 MySQL 高效获取数据数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查询算法,这种数据结构就是索引。

优缺点:

优点:

  • 提高数据检索效率,降低数据库的IO成本
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

缺点:

  • 索引列也是要占用空间的
  • 索引大大提高了查询效率,但降低了更新的速度,比如 INSERT、UPDATE、DELETE (树的结果需要发生调整,变化,需要进行重新的排序)

索引结构

索引结构

描述

B+Tree

最常见的索引类型,大部分引擎都支持B+树索引

Hash

底层数据结构是用哈希表实现,只有精确匹配索引列的查询才有效,不支持范围查询

R-Tree(空间索引)

空间索引是 MyISAM 引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少

Full-Text(全文索引)

是一种通过建立倒排索引,快速匹配文档的方式,类似于 Lucene, Solr, ES

索引

InnoDB

MyISAM

Memory

B+Tree索引

支持

支持

支持

Hash索引

不支持

不支持

支持

R-Tree索引

不支持

支持

不支持

Full-text

5.6版本后支持

支持

不支持

树类索引结构的比较(二叉树、红黑树、b树、b+树)

二叉树 

(极端情况下,可能出现单向链表的形式出现,这样会导致查询的时间复杂度 降低到 O(n))

一个层级 只能够包含两个结点(容纳太少)导致树高过高(IO 成本大幅度提高)

简述索引类型及存储结构 索引结构的优缺点_联合索引

        需要解决问题:      

  1.  需要降低树高 —— 解决办法(使用红黑树进行解决 (红黑树实现自平衡)) 动态调整
  2. 结点的容纳数 (虽然使用红黑树,能够避免出现链表的形式,但是还是存在着结点数过少)

简述索引类型及存储结构 索引结构的优缺点_数据库_02

(红黑树的本质:任然是二叉树) (多路平衡二叉树) 

B- Tree(多路平衡数)

        以一棵最大度数(max-degree,指一个节点的子节点个数)为5(5阶)的 b-tree 为例(每个节点最多存储4个key,5个指针)

简述索引类型及存储结构 索引结构的优缺点_字段_03

B+Tree (b+树的变种 :)

结构图: 

简述索引类型及存储结构 索引结构的优缺点_数据库_04

区别

  • 所有的数据都会出现在叶子节点 ,非叶子结点:存放索引的指针
  • 叶子节点形成一个双向链表 (循环链表)mysql对b+树进行优化  
  • 目的是为了支持范围查找 
  • 叶子结点是有顺序的排序

MySQL 索引数据结构对经典的 B+Tree 进行了优化。在原 B+Tree 的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的 B+Tree,提高区间访问的性能。

Hash索引

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。

简述索引类型及存储结构 索引结构的优缺点_字段_05

  • Hash索引只能用于对等比较(=、in),不支持范围查询(betwwn、>、<、…)
  • 无法利用索引完成排序操作
  • 查询效率高,通常只需要一次检索就可以了,效率通常要高于 B+Tree 索引
  • 出现hash碰撞后,则需要去进行判断(具体方法:开放地址法、再hash法)
  • Memory(hash 索引只使用与menory)
  • InnoDB: 具有自适应hash功能,hash索引是存储引擎根据 B+Tree 索引在指定条件下自动构建的

面试题

  1. 为什么 InnoDB 存储引擎选择使用 B+Tree 索引结构?
  • 相对于二叉树,层级更少,搜索效率高 (IO 效率 每多一层,就需要的翻页)
  • 对于 B-Tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针也跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低】
  • 存储的页数的大小
  • 相对于 Hash 索引,B+Tree 支持范围匹配及排序操作

索引分类

分类

含义

特点

关键字

主键索引

针对于表中主键创建的索引

默认自动创建,只能有一个

PRIMARY

唯一索引

避免同一个表中某数据列中的值重复

可以有多个

UNIQUE

常规索引

快速定位特定数据

可以有多个

全文索引

全文索引查找的是文本中的关键词,而不是比较索引中的值

可以有多个

FULLTEXT

        可以拥有多个是指 对于字段的类型可以选择多个不同的字段,而主键索引,选择完 一个字段后,就不可以去选择其他的类型

在 InnoDB 存储引擎中,根据索引的存储形式,又可以分为以下两种:

分类

含义

特点

聚集(簇)索引(Clustered Index)

将数据存储与索引放一块,索引结构的叶子节点(date)保存了行数据

必须有,而且只有一个

二级索引(Secondary Index)

非聚簇索引

将数据与索引分开存储,索引结构的叶子节点(date)关联的是对应的主键

可以存在多个

聚簇索引的选取规则

  1. 若存在主键索引,则主键索引就是聚簇索引
  2. 若不存在主键,则使用第一个唯一的唯一索引作为聚簇索引

回表操作

        

简述索引类型及存储结构 索引结构的优缺点_简述索引类型及存储结构_06

索引

语法

创建索引:
CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name, ...); 如果不加 CREATE 后面不加索引类型参数,则创建的是常规索引  全文suo

查看索引:
SHOW INDEX FROM table_name;

删除索引:
DROP INDEX index_name ON table_name;

查询表中已经存在的索引字段

        show  index from 表名 

创建联合索引的时候,需要注意相应的字段的顺序(最左前缀匹配原则)

删除索引

        drop index  索引名称 

SQL 性能分析

        首先进行sql 进行查询,优化查询语句

慢查询日志

(默认是没有开启的,需要进行手动开启) 

开启慢查询: 判断配置参数中的信息

        show_query_log = 1. 开启 慢查询日志 (默认是设置为 0)

        long_qyery_time = 2 设置慢查询日志的最长执行时间。若超过这个时间,则表示是慢sql语句 


查看是否有开启日志:show variable like 'slow_query_log' 

expire 进行想要的查看具体的执行细则

解释

EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。
语法:

  1. # 直接在select语句之前加上关键字 explain / desc
  2. EXPLAIN SELECT 字段列表 FROM 表名 HWERE 条件;


EXPLAIN 各字段含义:

  • id:select 查询的序列号,表示查询中执行 select 子句或者操作表的顺序(id相同,执行顺序从上到下;id不同,值越大越先执行)
  • select_type:表示 SELECT 的类型,常见取值有 SIMPLE(简单表,即不适用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等
  • type:表示连接类型,性能由好到差的连接类型为 NULL、system、const、eq_ref、ref、range、index、all
  • possible_key:可能应用在这张表上的索引,一个或多个
  • Key:实际使用的索引,如果为 NULL,则没有使用索引
  • Key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好
  • rows:MySQL认为必须要执行的行数,在InnoDB引擎的表中,是一个估计值,可能并不总是准确的
  • filtered:表示返回结果的行数占需读取行数的百分比,filtered的值越大越好

索引使用的具体规则


使用规则


如果索引关联了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。
如果跳跃某一列,索引将部分失效(后面的字段索引失效)。

联合索引中,出现范围查询(<, >),范围查询右侧的列索引失效。可以用>=或者<=来规避索引失效问题。

索引失效情况

  1. 在索引列上进行运算操作,索引将失效。
  1. 如:explain select * from tb_user where substring(phone, 10, 2) = '15';
  1. 字符串类型字段使用时,不加引号,索引将失效。如:,此处phone的值没有加引号
  1. explain select * from tb_user where phone = 17799990015;
  1. 模糊查询中,如果仅仅是尾部模糊匹配,索引不会是失效;如果是头部模糊匹配,索引失效。如:,前后都有 % 也会失效。
  1. explain select * from tb_user where profession like '%工程';
  2. explain select * from tb_user where profession like '软件%';(不会失效
  3. explain select * from tb_user where profession like '%科学%';(失效)
  1. 用或 分割开的条件,如果 or 其中一个条件的列没有索引,那么涉及的索引都不会被用到。只有两侧都有索引,才有效。

如果 MySQL 评估使用索引比全表更慢,则不使用索引。(SQL的自优化(优化器))

SQL 提示

是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。(加入人为的提示,让SQL出现多种选择的情况时,能够走相应的人为的命令)

例如,使用索引:user index (index_name)

不使用哪个索引:ignore index(index_name)

必须使用哪个索引:explain select * from tb_user use index(idx_user_pro) where profession="软件工程";explain select * from tb_user ignore index(idx_user_pro) where profession="软件工程";explain select * from tb_user force index(idx_user_pro) where profession="软件工程";

use 是建议,实际使用哪个索引 MySQL 还会自己权衡运行速度去更改。(建议不一定执行)

force就是无论如何都强制使用该索引。(强制命令的使用)

覆盖索引&回表查询

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能找到),减少 select *。

explain 中 extra 字段含义:
:查找使用了索引,但是需要回表查询数据
:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询using index conditionusing where; using index;

如果在聚集索引中直接能找到对应的行,则直接返回行数据,只需要一次查询,哪怕是select *;如果在辅助索引中找聚集索引,如,也只需要通过辅助索引(name)查找到对应的id,返回name和name索引对应的id即可,只需要一次查询;如果是通过辅助索引查找其他字段,则需要回表查询,如select id, name from xxx where name='xxx';select id, name, gender from xxx where name='xxx';

所以尽量不要用,容易出现回表查询,降低效率,除非有联合索引包含了所有字段select *

面试题:一张表,有四个字段(id, username, password, status),由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案:
select id, username, password from tb_user where username='itcast';

解:给用户名和password字段建立联合索引,则不需要回表查询,直接覆盖索引 联合索引的date域中存储的是唯一索引(主键) ,因此可以直接使用上。从而达到覆盖索引的作用

前缀索引

当字段类型为字符串(varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率,此时可以只降字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

语法:
前缀长度:可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
求选择性公式:create index idx_xxxx on table_name(columnn(n));

与建立一般的索引的区别是:在后面是字段名称加上(n) 表示提取字段的前n个字段进行构建索引

  1. select count(distinct email) / count(*) from tb_user;
  2. select count(distinct substring(email, 1, 5)) / count(*) from tb_user;

show index 里面的sub_part可以看到接取的长度

单列索引-联合索引

单列索引:即一个索引只包含单个列
联合索引:即一个索引包含了多个列
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

单列索引情况:

这句只会用到phone索引字段explain select id, phone, name from tb_user where phone = '17799990010' and name = '韩信';

注意事项

  • 多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询

索引的设计原则

设计原则

  1. 针对于数据量较大(百万级别、千万级别),且查询(query)比较频繁的表建立索引
  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引 (字段后都会建立索引、使用索引)
  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
  4. 如果是字符串类型的字段,字段长度较长,可以针对于字段的特点,建立前缀索引(注意前缀的区分度)
  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价就越大,会影响增删改的效率(索引修改会导致树的结点的调整,影响性能。并且索引本身也会占据一定的空间)
  7. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询 (使用not null 方便SQL优化器进行优化判断)