Mysql回顾(二)索引总结

目录

一:基础知识

1:页。

2:索引。

二:索引的分类

   聚集索引和非聚集索引 :参考

三:索引匹配方式

四:使用索引中的小细节

五:hash索引总结


一:基础知识

磁盘预读(预读的长度一般为页的整数倍)

1:页。

内存和磁盘交互的时候不是想读多少字节就读多少字节的,它有个最小的逻辑单元,成为页或块。页是存储器的逻辑块,在操作系统中往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中页的大小为4k),主存和磁盘以页为单位交换数据。在InnerDB中默认读取的是16KB。

2:索引。

1) Mysql索引是什么?

Mysql索引是帮助Mysql高效获取数据的数据结构。

2)索引是需要落地磁盘的,它存储在文件系统中。这是可想而知的,如果索引存在内存中,数据库宕机就找不回来了。

索引落地磁盘这就牵涉到读写IO的问题,在Mysql中主要看两个方面来看读写是否快,一个是IO的量,一个是IO的次数,在Mysql中不用考虑顺序读写和随机读写。

3)索引的文件存储与存储引擎相关。不同存储引擎的存储文件在磁盘 上组织形式不一样。我们常见的存储引擎InnerDB,它把表数据和索引都存放在同一个文件上。

4)索引文件的数据结构。

hash,二叉树,B树,B+树。

(1)关于hash索引的介绍可以参考这篇文章:哈希索引

      补充两点:利用hash索引需要将所有数据文件加载到内存,比较耗费内存空间 。如果所有的查询都是等值查询确实很快,但是在实际生产中范围查找的数据也很多,hash就不太适合了。

(2)二叉树。数据结构网站   树的结构有很多,BST(Binary Search Trees),AVL(Balanced Binary Search Trees).

Bst树:  小于根节点的在左边,大于根节点在右边。

正常的情况下的树如下:这种树根据二分查找速度也不算慢。

Mysql回顾(二)索引总结_第1张图片

但是这种树存在一种极端的情况,就是树只有一条分支,就退化成一条链表了。如下所示:这种对于查找很不利。

Mysql回顾(二)索引总结_第2张图片

AVL树:平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。它是通过不停的左旋或右旋树结构来进行平衡的。

我们同样是上面的几个数,在AVL树中是如下结构:这样就不会存在不平衡的情况。可以用于二分查找。

Mysql回顾(二)索引总结_第3张图片

因为在插入新的数据的时候会导致树的不平衡,所以会有树旋转的操作,很消耗性能,如果插入数据之后,剩下的都是查询,倒也可以使用这种数据结构。但是实际肯定不会这样。

红黑树: 红黑树详述  

AVL 树和红黑树基本都是存储在内存中才会使用的数据结构。在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况。为什么会出现这样的情况,我们知道要获取磁盘上数据,必须先通过磁盘移动臂移动到数据所在的柱面,然后找到指定盘面,接着旋转盘面找到数据所在的磁道,最后对数据进行读写。磁盘IO代价主要花费在查找所需的柱面上,树的深度过大会造成磁盘IO频繁读写。根据磁盘查找存取的次数往往由树的高度所决定,所以,只要我们通过某种较好的树结构减少树的结构尽量减少树的高度,B树可以有多个子女,从几十到上千,可以降低树的高度。

而且AVL树和红黑树它们的节点都会存储数据,即一个节点即有索引值又有数据值,和B+树比这也会增加IO读取次数,因为B+树只有叶子节点存储具体的数据,其它节点存储索引值,这样在读取一页数据假设16kb的情况下,B+树所能 读取到的范围更大。

数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

关于这一点也可以参考:B+树作为索引的分析   深入剖析索引

二:索引的分类

   聚集索引和非聚集索引 :参考

     对于InnerDB来说聚簇索引(聚集索引)是主键,非空唯一健,如果都没有的话会生成一个rowid来作为key存储数据。
请注意聚簇索引是把索引和数据存储在一起(索引和数据存在同一个文件中),存在B+树的叶子节点,比如根据主键索引找到对应索引节点就找到了对应的数据。

Mysql会给唯一键创建索引,主键是特殊的唯一键,因为它非空且唯一,如果有主键和唯一键,会给主键创建聚簇索引,唯一键创建非聚簇索引。

   上面两类的分法外还可以进一步细分索引分类:

普通索引:最基本的索引,没有任何限制
唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值。
主键索引:它 是一种特殊的唯一索引,不允许有空值。 
全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时好空间。
组合索引:为了更多的提高mysql效率可建立组合索引,遵循”最左匹配“原则

索引中的一些技术名词:

1:回表

对于那些非聚簇索引(二级索引)是会再建一个B+树,叶子节点仍是索引节点(可能是那个主键,非空唯一健,或rowid)。如果我们现在根据非聚簇索引字段查询数据,会在非聚簇索引B+树中找到聚簇索引的key(可能是那个主键,非空唯一健,或rowid),然后再去聚簇索引B+树中查询具体的行数据,这一个查询的过程称为回表。所以当数据量较少时,使用索引反而会慢,因为要查询两颗B+树才能定位到数据。

 2:索引覆盖

         假如有张表t_name的字段如下,id为主键,name为普通索引。也就是说mysql会给这张表创建一个聚簇索引和一个非聚簇索引。

         

id name age birthday
1 lis 1 20200101
2 asan 1 20200101


 看如下sql。

/*
    首先从非聚簇索引树中查询name索引所在的叶子节点,这个叶子存的数据就是对应的主键id,
 所以只要查询一颗索引树就可以得到结果
*/
select id from  t_name where name =lisi;
/*
  首先从非聚簇索引树中查询name索引所在的叶子节点,这个叶子存的数据就是对应的主键id,但是要取的值是*所以还要再查询一边聚簇索引树才能得到完整的列记录。
*/
select *from t_name where name=lisi;

 上面第一个sql,通过非聚簇索引就能得到想要的结果而没有走回表,这种就叫做索引覆盖。执行计划的Extra: using index

3:最左匹配

如果索引是由多列字段组成的,称为组合索引/联合索引。在使用联合索引的时候会由最左匹配的概念。就是:在包含多个列的查询过程中,会依靠先查询第一个列,再查询第二个列。

比如现在有(name,age)这两个字段组成了联合索引。那么使用如下sqL语句查询时候,只有第三个条件不会走联合索引,其它条件都会走索引。第四个条件走索引因为Mysql底层会有优化器帮我们做一些步骤。


where name=? and age=?;
where name=?;
where age=?;
where age=? and name=?;

 补充:

对于组合索引,比如有(name,age,pos)三个组合索引。

    1):在 where name=lis and pos=99;这个语句的时候,只会用到name这一个索引值。

     2):有些人说在使用or的时候会让索引失效。但是我尝试的结果是会用索引。可能和版本有关系,在具体使用的时候查询下执行计划。

Mysql回顾(二)索引总结_第4张图片

4:索引下推(ICP)

     不用索引下推的话:

                  先根据name列的值把所有数据拉取到server层,然后在server层对age过滤

      使用索引下推的话:

                  先根据name,age,两列的值把满足要求的数据拉取到server层,取出对应的数据。

索引下推一般可用于所求查询字段(select列)不全是联合索引的字段,查询条件为多条件查询且查询条件子句(where/order by)字段全是联合索引。

对应的执行计划有个:Extra:using index condition说明。

5:MRR:  Multi-Range Read Optimization 可以做到对索引查询的优化。

6::InnerDB自适应hash索引:自适应

三:索引匹配方式

1:全值匹配:全值匹配指的是和索引中的所有列进行等值匹配。

 2:最左匹配:只匹配前几列。对于组合索引来说的,见上面的最左匹配。

3:匹配列前缀:对一个索引列进行like查询的时候,比如name这列有索引,(1)where name like 'LI%'; (1) where name like '%LI%';

     第一个查询会走索引,第二个查询不会走索引。

4:匹配范围值:  使用索引列查询范围记录也会走索引。

5:精准匹配某一列并范围匹配另外一列:假如有张T_USER表有USER_NAME,AGE,POS列,每个列上都建有索引。有如下sql:

   第一个sql会走两个列的索引,第二个sql也只走前两列的索引,因为用到了索引下推。索引中间使用范围的时候会阻断索引。

  第三个sql就会走三个列的索引。

select *from  T_USER where USER_NAME=lisi and AGE>40 ;

select *from  T_USER where USER_NAME=lisi and AGE>40 and POS=900;

select *from  T_USER where USER_NAME=lisi and AGE=40 and POS>900;

四:使用索引中的小细节

1:当使用索引列进行查询的时候不要使用表达式,比如(where  id+1=5)会使索引失效。把计算放到业务层而不是数据库层。

2:尽量使用主键查询,而不是其它索引,因为使用主键查询不会使用回表查询。

3:使用前缀索引。有时候索引列是很长的字符串,直接建索引会让索引变得大而慢,通常情况下可以使用某个列开始的部分字符串,这样大大的节约索引的空间,从而提高效率。但这会降低索引的选择性,,索引的选择性是指不重复的索引值和数据表记录总数的比值,范围从1~100,索引的选择性越高,则查询效率越高,因为选择性更高的索引可以让mysql在查找的时候,过滤掉更多的行。

    一般情况下某个列前缀的选择性也是足够的,足以满足查询的性能,但是对应BLOB,TEXT,VARCHAR类型的列,必须使用前缀索引,因为mysql不允许索引这些列的完整长度,使用该方法的诀窍在于要选择足够长的前缀来保证较高的选择性。

但是需要注意:mysql无法使用其前缀索引做ORDER BY和GROUP BY,也无法使用前缀索引做覆盖扫描。

     关于前缀索引进一步的介绍可以参见:前缀索引

4:使用索引扫描进行排序。

MySQL有两种方式可以生成有序的结果:通过排序操作;或者按照索引顺序扫描;如果EXPLAIN 出来的结果的type列的值为“index”,则说明MySQL使用了索引扫描来做排序(不要和Extra列的“Using index”搞混)。

  扫描索引本身是很快的,因为只需要从一条索引记录移动到紧接着的下一条记录。但如果索引不能覆盖查询所需要的全部列,那就不得不没扫描一条索引记录就回表查询一次对应的行。这基本上都是随机的io,因此按索引顺序读取数据的速度通常要比顺序的全表扫描慢,尤其是在io密集型的工作负载时。

  MySQL可以使用同一个索引既满足排序,又用于查找行。因此,如果可能,设计索引时应该尽可能地同时满足这两种任务,这样是最好的。

  只有当索引的列顺序和order by 子句的顺序完全一致,并且所有列的排序方向(倒序或正序)都一致时,MySQL才能使用索引来对结果做排序。如果查询需要关联多张表,则只有当order by 子句引用的字段全部为第一个表时,才能使用索引做排序。order by子句和查找型查询的限制是一样的:需要满足索引的最左缀的要求,否则,MySQL都需要执行排序操作,而且无法利用索引排序。

  有一种情况下order by 子句可以不满足最左前缀的要求,就是前导列为常量的时候。如果WHERE 子句或者JOIN子句中对这些列指定了常量,就可以弥补索引的不足。

5:union all ,union,in ,or 都能够使用索引,但是推荐使用in。

6:范围可以用到索引列。范围条件为:<,<=,>,>=,between。范围列可以用到索引,但是范围列后面的列无法用到索引,索引最多用于一个范围列。  

7:强制类型转换会导致索引失效。

8:数据更新频繁,数据区分度不高的字段上不易加索引。一般区分度在80%以上的时候可以建立索引,区分度可以使用count(distinct(列名))/count(*)来计算。

9:创建索引的列,不允许为null,可能造成不符合预期的结果,因为某个字段null 不能用等号比较。

10:当需要进行表连接的时候,最好不要超过三张表,因为需要join的字段数据类型必须一致。

11:能用limit的时候使用limit

12: 单表创建索引控制在5个以内。

13:组合索引不要超过五个字段

五:hash索引总结

1:基于hash表的表现,只有精准匹配索引所有列的查询才有效。

2:在mysql,只有memory的存储引擎显示支持哈希索引。

3:哈希索引自身只存储对应的hash值,所以索引的结构十分紧凑,这让哈希索引查找的速度很快。

4:哈希索引也有很多限制:

   1)哈希索引只包含哈希值和行指针,而不存字段值。

    2)哈希索引数据并不是按照索引值顺序存储的,所以无法进行排序。

    3)哈希索引不支持部分列匹配查找,哈希索引是使用索引列的全部内容来计算哈希值。

    4)哈希索引支持等值比较查询,也不支持任何范围查询。

    5)  访问哈希索引的数据非常快,除非有很多哈希冲突,当出现哈希冲突的时候,存储引擎必须遍历链表中的所有行指针,逐行进行比较,直到找到所有符合条件的行。

   6)哈希冲突比较多的话,维护的代价也很高。

 

 

 

你可能感兴趣的:(数据库,mysql,索引)