MySQL笔记(二)

该博客主要讲解一下MySQL中的索引。

一、B树和B+树

在讲解索引之前,先简要说明一下索引的底层数据结构。MySQL中常用的存储引擎,如InnoDB和MyISAM的索引都是B+树索引,也就是底层数据结构就是B+树。

1、B树

B+树是由B树发展而来的,因此我们先讲一下B树。B树的定义(按照阶数来定义的)如下:

MySQL笔记(二)_第1张图片

一组关键字建立的B树:

MySQL笔记(二)_第2张图片

2、B+树

 

一棵m阶的B+树和m阶的B树的异同点在于:

    1、有n棵子树的结点中含有n-1个关键字(与B树相同,MySQL中的B+树索引采用这种); (此处有争议,另一种说法是,B+树具有n棵子树的结点中含有n个关键字)

    2、所有的叶子结点中包含了全部关键字的信息,即所有的非叶子节点仅仅起到索引作用,非叶结点中仅含有其子树根结点中最大关键字及指向该子树的指针。(而B树的叶子节点并没有包括全部需要查找的信息)

    3、在B+树上,有一个指针指向关键字最小的叶子节点,所有叶子节点链接成一个线性链表。(B树不存在指针将叶子节点连成一个链表)

一组关键字建立的B+树:

MySQL笔记(二)_第3张图片

二、B树索引(B+树索引)

MySQL中的存储引擎通常实现的都是B+树索引。

下图为B+树索引示例:

MySQL笔记(二)_第4张图片

B+树索引是用于全键值、键值范围和键前缀查找。其中键前缀查找只适用于最左前缀查找。

假设有如下数据表:

CREAT TABLE(

    last_name  varchar(50)   not null,

    first_name varchar(50)   not null,

    dob           date               not null,

    gender      enum('m', 'f') not null,

    key(last_name, first_name, dob) 

);

B+树索对下面类型的查询有效:

全值匹配:

  指和索引中的所有列进行匹配,例如在前面的索引中查找姓名为Cuba Allen、出生于1960-01-01年的人。

匹配最左前缀

  在前面的索引中查找姓为Allen的所有人,即只是用了第一列。

匹配列前缀

  也可以只匹配某一列的值的开头部分,例如查找所有以A开头的姓的人,这也只是用了第一列。

匹配范围值

  例如查找姓在Allen和Barrymore之间的人,这也是只用到了第一列。

精确匹配某一列并范围匹配另一列

  前面的索引也可以查询所有姓为Allen,且名字以K开头的人。即第一列全匹配,第二列范围匹配。

只访问索引的查询

  支持覆盖索引,即查询只需要访问索引无须访问数据行。这是一种优化技巧。

B+树索引的限制:

  • 如果不是按照索引的最左列开始查找,则无法使用索引。例如,无法查找名字是Bill的人,也无法查找特定生日的人,因为这两列不是最左数据列。类似地,也无法查找姓氏以某个字母结尾的人,因为不是该列的最左前缀。
  • 不能跳过索引中的列。例如使用第一列和第三列进行查找,则索引实际上只使用了第一列。
  • 如果查询中有某个列的范围查询,则其右边的列都无法使用索引优化查询。

由此,可以看出,索隐列的顺序是很重要的,所以在建立索引时,既要考虑选取哪些列,也要考虑这些列的顺序。

MySQL中还有一些其他类型的索引,比如哈希索引、全文索引、空间索引等,这里就不再叙述了。

索引的优点:

  1、索引大大减少了服务器需要扫描的数据量。

  2、索引可以帮助服务器避免排序和临时表。

  3、索引可以将随机I/O变为顺序I/O。

索引的缺点:

  1、索引需要占用额外的存储空间,数据量越大占用的额外空间也就越大。

  2、进行更新操作时需要花费额外的时间维护索引,造成更新操作效率降低。

三、高性能的索引策略

1、独立的列

独立的列是指索引列不能是表达式的一部分,也不能是函数的参数。

例如:SELECT id FROM actor WHERE id+1=5;

这个例子就不能使用id的索引,可以改成id=4,这就可以使用索引了。

例如另外一种常见错误:SELECT ... WHERE TO_DAYS(CURRENT_DATE)-TO_DAYS(date_col)<=10; 这是不能使用date_col列的索引的。

2、前缀索引和索引选择性

有时候需要索引很长的的字符列,这会让索引变得大且慢。这时就可以采用前缀索引,即只索引列的前面一部分字符,这样可以大大节约索引空间,从而提高索引效率。但是这样会降低索引的选择性。索引的选择性是指,不重复的索引值和数据表的记录总数(#T)的比值,范围是在1/#T到1之间。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

在选择索引的时候,既要选择足够长的前缀以保证较高的选择性,同时又不能太长,以便节约空间。

3、多列索引及索引列顺序

一个常见的错误就是,为每个列创建独立的索引,或者按照错误的顺序创建多列索引。在多个列上建立独立的单列索引大部分情况下并不能提高MySQL的查询性能。当出现服务器对多个索引做相交操作时(通常有多个AND条件),通常意味着需要一个包含所有列的多列索引,而不是多个独立的单列索引。

索引顺序的选择通常是一个令人困惑的问题,正确的顺序依赖于使用该索引的查询,并且同时需要考虑如何更好地满足排序和分组的需求。在一个多列B+树索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,等等。选择索引列顺序有一个经验法则:将选择性最高的列放到索引最前列(在不需要考虑排序和分组的时候,效果很好)

4、聚簇索引

聚簇索引并不是一种单独的索引类型,而是一种索引存储结构。具体的实现细节依赖于存储引擎,InnoDB的聚簇索引实际上在同一个结构中B+树索引和数据行,而MyISAM(不支持聚簇索引)是将索引和数据分为两个单独的文件存储的。

当表有聚集索引时,它的数据行实际上存放在索引的叶子页中。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。MySQL中不是所有的存储引擎都支持聚簇索引,InnoDB支持,而MyISAM不支持,下面主要讲解InnoDB的聚簇索引。

InnoDB是通过主键聚集数据的,不支持选择其他索引作为聚集索引。

聚簇索引的优点:

  • 可以把相关数据保存在一起。你如实现电子邮箱时,可以根据用户ID聚集数据。
  • 数据访问更快。聚簇索引将索引和数据保存在同一个B+树中,因此从聚簇索引中获取数据比在非聚簇索引中查找更快。
  • 使用索引覆盖扫描的查询可以直接使用叶节点中的主键值。
  • 当出现行移动或者数据分页时,二级索引不需要再进行额外的维护了。

聚簇索引的缺点:

  • 聚簇索引提高了I/O密集型应用的性能,但是如果数据全部都可以放到内存中,那聚簇索引就没有什么优势了。
  • 插入速度严重依赖插入顺序,按主键顺序插入是速度最快的。
  • 更新索引列的代价很高,因为会强制InnoDB将每个被更新的行移动到新的位置。而行的移动有可能会导致“页分裂”问题。当某一行必须插入到一个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是页分裂。
  • 聚簇索引可能会导致全表扫描变慢,尤其是行比较稀疏的时候。
  • 二级索引(非聚集索引)可能比想象的要大,因为在二级索引的叶子节点中包含了引用行的主键列。
  • 二级索引访问需要两次查找。存储引擎需要找到二级索引的叶子节点获得对应的主键值,然后根据这个主键值去聚簇索引中查找对应的行。这里进行了两次B+树查找。

InnoDB和MyISAM的数据分布对比

对于如下表格:

CREATE TABLE layout_test(

    col1 int NOT NULL,

    col2 int NOT NULL,

    PRIMARY KEY(col1),

    KEY(col2)

);

写下来看一下InnoDB和MyISAM是如何存储的。

MyISAM的数据分布

MyISAM按照数据插入的顺序存储在磁盘上,如下图:

MySQL笔记(二)_第5张图片

这种数据存储方式,很容易创建索引:

MySQL笔记(二)_第6张图片

col2列上的索引与主键索引没有什么大的区别:

MySQL笔记(二)_第7张图片

MyISAM中的主键索引和其它索引在结构上没有什么不同。主键索引就是一个名为PRIMARY的唯一非空索引。MyISAM通过索引查询到行号之后,可以直接通过行号(或者说是物理地址)去读取相应的数据行。

InnoDB的数据分布

因为InnoDB支持聚簇索引,所以,使用的存储方式与MyISAM有很大的不同。其存储方式如下图:

MySQL笔记(二)_第8张图片

从这里可以看出,InnoDB把主键索引和整张表结合在了一起。在InnoDB中,聚簇索引“就是”表,所以不会像MyISAM那样需要独立的行存储数据。

聚簇索引的每一个叶子节点都包含了主键值、事务ID、用于事务和MVCC的回滚指针以及所有的剩余列(在该例子中就是col2)。如果主键是一个列前缀索引,InnoDB也会包含完整的主键列和剩下的其他列。

InnoDB的二级索引(不是聚簇索引,一张表只能有一个聚簇索引)和聚簇索引很不相同,如下图:

MySQL笔记(二)_第9张图片

InnoDB的二级索引的叶子节点存储的不是“行指针”,而是主键值,并以此作为指向行的“指针”。一个查找如果使用的是二级索引,则需要先在二级索引中查找到主键值,再根据主键值去聚簇索引中查找相应的数据(需要两次B+树查找)。这样的策略减少了当出现行移动或者数据页分裂时二级索引的维护工作。,但是使用主键值会使得二级索引占用更多的存储空间。

下面给出InnoDB和MyISAM存储表的抽象对比图:

5、覆盖索引

如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。覆盖索引能够极大地提高性能。

覆盖索引有许多好处:

  • 索引条目通常远小于数据行大小,所以如果只需要读取索引,那MySQL就会极大地减少数据访问量。
  • 因为索引是按照列值顺序存储的,所以对于I/O密集型的范围查询会比随机从磁盘中读取每一行的I/O要少得多。
  • 一些存储引擎如MyISAM只在内存中缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用,这会导致严重的性能问题。而覆盖索引则只需要访问内存即可。
  • 由于InnoDB的聚簇索引,覆盖索引对InnoDB特别有用。如果二级索引能够覆盖查询,则可以避免对主键索引的二次查询。

覆盖索引必须要存储索引列的值,所以,MySQL只能使用B+树做覆盖索引。

参考:

高性能MySQL

你可能感兴趣的:(MySQL笔记(二))