MySQL官方对索引的定义为: 索引 (Index) 是帮助MySQL高效获取数据的数据结构。
提取句子主干,就可以得到索引的本质:索引是数据结构。
索引是一种用于快速查询和检索数据的数据结构,帮助mysql提高查询效率的数据结构,而且是排好序的数据结构,存储在磁盘文件里。
索引的作用是在不读取整个表的情况下,使得数据库应用程序可以更快地查找数据,用户无法看到索引,只能被用来加速检索或查询。
优点:
缺点:
但是,使用索引一定能提高查询性能吗?
大多数情况下,索引查询都是比全表扫描要快的。但是如果数据库的数据量不大,那么使用索引也不一定能够带来很大提升。
索引建立的原则:
数据表的主键列使用的就是主键索引。(唯一标识,主键不可重复,只能有一个列作为主键)
一张数据表有只能有一个主键,并且主键不能为 null,不能重复。(可以理解为一种特殊的唯一索引)
在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在null值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。
二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。
唯一索引,普通索引,前缀索引等索引属于二级索引。
B 树也称 B-树,全称为 多路平衡查找树 ,B+ 树是 B 树的一种变体。B 树和 B+树中的 B 是 Balanced (平衡)的意思。
目前大部分数据库系统及文件系统都采用 B-Tree 或其变种 B+Tree 作为索引结构。
B/B+Tree更适合文件系统的索引/更适合硬盘上查询的数据结构。
为什么不是一般二叉树?
二者存储数据的结构可以看出,二叉树随着数据的增加,树的高度会越来越高,而B+树是越来越胖。
树的高度越来越高,增加了I/O次数,导致查询效率减低。而B+树随着数据量的增加,树的宽度越来越大,这样空间利用率更高,可减少I/O次数,查询效率较快。
我们知道,在内存比在磁盘的数据,查询效率快得多。如果树这种数据结构作为索引,那我们每查找一次数据就需要从磁盘中读取一个节点,也就是我们说的一个磁盘块,但是平衡二叉树可是每个节点只存储一个键值和数据的,如果是B树,可以存储更多的节点数据,每个节点包含多个数据,树的高度也会降低,因此读取磁盘的次数就降下来啦,查询效率就快啦。
为什么不是B树而是B+树呢?
B+Tree在两种存储引擎的实现方式是不同的。
在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的key存在,则取出其data域的值,然后以data域的值为地址读取相应的数据记录。这被称为“非聚簇索引”
相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,「树的叶节点data域保存了完整的数据记录」。
这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。
Hash索引的缺点
InnoDB引擎有一个特殊额功能叫做“自适应哈希索引”,当 InnoDB注意到某些索引值被使用得非常频繁时,它会在内存中基于B-Tree索引之上再创建一个哈希索引,这样就让B-Tree索引页具有哈希索引的一些优点,比如快速的哈希查找。
聚集索引即索引结构和数据一起存放的索引。主键索引属于聚集索引。
聚集索引是依据主键创建的索引,除了主键以外的其他索引,都是非聚集索引。
不是单独的索引类型,是一种数据存储方式。
在InnoDB引擎里,一张表的数据对应的物理文件本身是按照B+树来组织的,而聚集索引就是按照每张表的一个主键来构建这样一个B+树,叶子节点里面存储了表里面的每一行数据记录,基于这样一个特征,聚集索引不仅仅是一种索引类型,还是一种数据存储方式。
同时意味着每张表里必须有主键,没有主键的话innodb会默认添加一个隐藏列作为主键索引来存储这个表的数据行。一般情况建议使用自增id作为主键,因为id本身具有连续性,对应数据也会按照顺序去存储到磁盘上,写入和检索性能都很高。
innodb里只能存在一个聚集索引,如果有多个,那意味着这个表会有多个副本,这不仅会造成空间浪费,还会导致数据的维护困难。
innodb主键索引存储了一个表的完整数据,主键和行记录放在同一个叶节点,找到了主键也就找到了行记录。所以如果是基于非聚集索引去查找一条数据,最终还是需要访问主键索引来进行检索。
跟MyISAM引擎的非聚集索引不同的是,MyISAM叶节点保存的是地址,而InnoDB是主键,InnoDB非聚集索引的索引文件和数据文件分开存储,索引文件的叶节点只保存主键,在查找时,要先找到叶节点中的主键,再根据主键去主索引文件查找详细行记录;
上述InnoDB引擎中,非主键索引查找数据时需要先找到主键,再根据主键查找具体行数据,这种现象叫回表查询。
如何解决:覆盖索引,即将查询sql中的字段添加到联合索引里面,只要保证查询语句里面的字段都在索引文件中,就无需进行回表查询;让索引范围覆盖住我们select 的范围,就不会发生回表查询。
比方说有个用户表,有id、name、age、addr四个字段,其中id为主键,主键自带主键索引,无需创建
值1:1、小张、18、成都;
值2:2、小黄、20、北京;
这种查询就必须先在索引文件中找到name为小张的索引节点,很明显这个节点里面只有id,因为这张表只有主键索引,再根据id去数据文件查找具体数据。
如果把name、age、addr建立到联合索引,在找到name为小张的索引节点时,发现里面已经有了我们所需要的age、addr,就无需再到数据文件查找;
当然实际开发中,不可能把所有字段建立到联合索引,应根据实际业务场景,把经常需要查询的字段建立到联合索引即可。
索引下推是 MySQL 5.6 版本中提供的一项索引优化功能,可以在非聚簇索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表次数。
不使用索引条件下推优化时的查询过程
获取下一行,首先读取索引信息,然后根据索引将整行数据读取出来。
然后通过where条件判断当前数据是否符合条件,符合返回数据。
使用索引条件下推优化时的查询过程
获取下一行的索引信息。
检查索引中存储的列信息是否符合索引条件,如果符合将整行数据读取出来,如果不符合跳过读取下一行。
用剩余的判断条件,判断此行数据是否符合要求,符合要求返回数据。
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道在 InnoDB 存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
覆盖索引即需要查询的字段正好是索引的字段,那么直接根据该索引,就可以查到数据了,而无需回表查询。
使用表中的多个字段创建索引,就是 联合索引,也叫 组合索引 或 复合索引。
覆盖索引是通过联合索引来实现的。
最左前缀匹配原则指的是,在使用联合索引时,MySQL 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询,如 >、<、between 和 以%开头的like查询 等条件,才会停止匹配。
如有索引 (a,b,c,d),查询条件 a=1 and b=2 and c>3 and d=4,则会在每个节点依次命中a、b、c,无法命中d。(c已经是范围查询了,d肯定是排不了序了)
因此,列的排列顺序决定了可命中索引的列数。
2.被频繁更新的字段应该慎重建立索引。
3.尽可能的考虑建立联合索引而不是单列索引。
4.注意避免冗余索引 。
5.考虑在字符串类型的字段上使用前缀索引代替普通索引。
// 主键索引
ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
//唯一索引
ALTER TABLE `table_name` ADD UNIQUE ( `column` )
//普通索引
ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
//全文索引
ALTER TABLE `table_name` ADD FULLTEXT ( `column`)
//联合索引
ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )
索引的数据结构
Hash 类型的索引
Btree : InnoDB 的默认数据结构~