目录
1.什么是索引?为什么要用索引?
1.1索引的含义
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据库中表的数据.索引的实现通常使用B树和变种的B+树(mysql常用的索引就是B+树)。除了数据之外,数据库系统还维护为满足特定查找算法的数据结构,这些数据结构以某种方式引用数据.这种数据结构就是索引!
简言之,索引就类似于书本,字典的目录!
1.2为什么用?
打个比方,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。对于没有索引的表,单表查询可能几十万数据就是瓶颈,而通常大型网站单日就可能会产生几十万甚至几百万的数据,没有索引查询会变的非常缓慢。
一言以蔽之,合理使用索引,可以加快数据库的查询效率和提升程序性能!
2.索引的作用与缺点
2.1作用
①通过创建索引,可以在查询的过程中,提高系统的性能
②通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性
③在使用分组和排序子句进行数据检索时,可以减少查询中分组和排序的时间
2.2缺点
①创建索引和维护索引要耗费时间,而且时间随着数据量的增加而增大
②索引需要占用物理空间,如果要建立聚簇索引,所需要的空间会更大
③在对表中的数据进行增加删除和修改时需要耗费较多的时间,因为索引也要动态地维护
3.索引的使用场景
3.1应创建索引的场景
①经常需要搜索的列上
②作为主键的列上
③经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度
④经常需要根据范围进行搜索的列上
⑤经常需要排序的列上
⑥经常使用在where子句上面的列上
3.2不应创建索引的场景
①查询中很少用到的列
②对于那些具有很少数据值的列.比如数据表中的性别列,bit数据类型的列
③对于那些定义为text,image的列.因为这些列的数据量相当大
④当对修改性能的要求远远大于搜索性能时.因为当增加索引时,会提高搜索性能,但是会降低修改性能
4.索引的分类与说明
4.1主键索引
设定为主键后数据库会自动建立索引,innodb为聚簇索引
#随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id)
);
#使用AUTO_INCREMENT关键字的列必须有索引(只要有索引就行)。
CREATE TABLE customer2 (id INT(10) UNSIGNED,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id)
);
#单独建主键索引:
ALTER TABLE customer add PRIMARY KEY customer(customer_no);
#删除建主键索引:
ALTER TABLE customer drop PRIMARY KEY ;
#修改建主键索引:
#必须先删除掉(drop)原索引,再新建(add)索引
4.2单列索引
一个索引只包含单个列,一个表可以有多个单列索引
#随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id),
KEY (customer_name)
);
#随表一起建立的索引 索引名同 列名(customer_name)
#单独建单值索引:
CREATE INDEX idx_customer_name ON customer(customer_name);
#删除索引:
DROP INDEX idx_customer_name ;
4.3唯一索引
索引列的值必须唯一,但允许有空值
#随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id),
KEY (customer_name),
UNIQUE (customer_no)
);
#建立 唯一索引时必须保证所有的值是唯一的(除了null),若有重复数据,会报错。
#单独建唯一索引:
CREATE UNIQUE INDEX idx_customer_no ON customer(customer_no);
#删除索引:
DROP INDEX idx_customer_no on customer ;
4.4复合索引
一个索引包含多个列,在数据库操作期间,复合索引比单值索引所需要的开销更小(对于相同的多个列建索引)
如果一个表中的数据在查询时有多个字段总是同时出现则这些字段就可以作为复合索引,形成索引覆盖可以提高查询的效率!
#随表一起建索引:
CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200),
PRIMARY KEY(id),
KEY (customer_name),
UNIQUE (customer_name),
KEY (customer_no,customer_name)
);
#单独建索引:
CREATE INDEX idx_no_name ON customer(customer_no,customer_name);
#删除索引:
DROP INDEX idx_no_name on customer ;
4.5聚集索引与非聚集索引
4.5.1聚集索引
聚集索引:指索引项的排序方式和表中数据记录排序方式一致的索引。它会根据聚集索引键的顺序来存储表中的数据,即对表 的数据按索引键的顺序进行排序,然后重新存储到磁盘上。因为数据在物理存放时只能有一种排列方式,所以一个表只能有一个聚集索引。比如字典中,用‘拼音’查汉字,就是聚集索引。因为正文中字都是按照拼音排序的。而用‘偏旁部首’查汉字,就是非聚集索引,因为正文中的字并不是按照偏旁部首排序的,我们通过检字表得到正文中的字在索引中的映射,然后通过映射找到所需要的字。
聚集索引的使用场合为:
a.查询命令的回传结果是以该字段为排序依据的;
b.查询的结果返回一个区间的值;
c.查询的结果返回某值相同的大量结果集。
聚集索引会降低 insert,和update操作的性能,所以,是否使用聚集索引要全面衡量。
4.5.2非聚集索引
非聚集索引:与聚集索引相反, 索引顺序与物理存储顺序不一致。
非聚集索引的使用场合为:
a.查询所获数据量较少时;
b.某字段中的数据的唯一性比较高时;
非聚集索引必须是稠密索引
4.5.3使用及语法
create [unique] [clustered] [nonclustered] index index_name on {tabel/view} (column[dese/asc][....n])
注: [unique] [clustered] [nonclustered]表示要创建索引的类型,以此为唯一索引,聚集索引,和非聚集索引,当省略unique选项时,建立非唯一索引.当省略clustered,nonclustered选项时.建立聚集索引,省略nonclustered选项时,建立唯一聚集索引。
4.5.4使用场景对比
动作描述 | 使用聚集索引 | 使用非聚集索引 |
列经常被分组排序 | 使用 | 使用 |
返回某范围内的数据 | 使用 | 不使用 |
一个或极少不同值 | 不使用 | 不使用 |
小数目的不同值 | 使用 | 不使用 |
大数目的不同值 | 不使用 | 使用 |
频繁更新的列 | 不使用 | 使用 |
外键列 | 使用 | 使用 |
主键列 | 使用 | 使用 |
频繁修改索引列 | 不使用 | 使用 |
4.6聚簇索引与非聚簇索引
4.6.1聚簇索引
聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据。
聚簇索引的特点:
①聚簇索引具有唯一性,由于聚簇索引是将数据跟索引结构放到一块,因此一个表仅有一个聚簇索引。
②表中行的物理顺序和索引中行的物理顺序是相同的,在创建任何非聚簇索引之前创建聚簇索引,这是因为聚簇索引改变了表中行的物理顺序,数据行 按照一定的顺序排列,并且自动维护这个顺序;
③聚簇索引默认是主键,如果表中没有定义主键,InnoDB 会选择一个唯一且非空的索引代替。如果没有这样的索引,InnoDB 会隐式定义一个主键(类似oracle中的RowId)来作为聚簇索引。如果已经设置了主键为聚簇索引又希望再单独设置聚簇索引,必须先删除主键,然后添加我们想要的聚簇索引,最后恢复设置主键即可。
4.6.2非聚簇索引
不是聚簇索引的二级索引,也叫辅助索引,都称为非聚簇索引。将数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置。
4.6.3Mysql的MYISAM和INNODB引擎
因为这两种引擎对非聚簇索引和聚簇索引的使用,就是他们之间很大的一个区别。所以结合这两个引擎,再对这两种索引展开些描述就更明了了。
在innodb中,在聚簇索引之上创建的索引称之为辅助索引,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引。辅助索引叶子节点存储的不再是行的物理位置,而是主键值,辅助索引访问数据总是需要二次查找。
- InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。
- 若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。(重点在于通过其他键需要建立辅助索引)
MyISAM使用的是非聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。
4.6.4对比总结
每次使用辅助索引检索都要经过两次B+树查找,看上去聚簇索引的效率明显要低于非聚簇索引,这不是多此一举吗?聚簇索引的优势在哪?
1.由于行数据和聚簇索引的叶子节点存储在一起,同一页中会有多条行数据,访问同一数据页不同行记录时,已经把页加载到了Buffer中(缓存器),再次访问时,会在内存中完成访问,不必访问磁盘。这样主键和行数据是一起被载入内存的,找到叶子节点就可以立刻将行数据返回了,如果按照主键Id来组织数据,获得数据更快。
2.辅助索引的叶子节点,存储主键值,而不是数据的存放地址。好处是当行数据放生变化时,索引树的节点也需要分裂变化;或者是我们需要查找的数据,在上一次IO读写的缓存中没有,需要发生一次新的IO操作时,可以避免对辅助索引的维护工作,只需要维护聚簇索引树就好了。另一个好处是,因为辅助索引存放的是主键值,减少了辅助索引占用的存储空间大小。
注:我们知道一次io读写,可以获取到16K大小的资源,我们称之为读取到的数据区域为Page。而我们的B树,B+树的索引结构,叶子节点上存放好多个关键字(索引值)和对应的数据,都会在一次IO操作中被读取到缓存中,所以在访问同一个页中的不同记录时,会在内存里操作,而不用再次进行IO操作了。除非发生了页的分裂,即要查询的行数据不在上次IO操作的换村里,才会触发新的IO操作。
3.因为MyISAM的主索引并非聚簇索引,那么他的数据的物理地址必然是凌乱的,拿到这些物理地址,按照合适的算法进行I/O读取,于是开始不停的寻道不停的旋转。聚簇索引则只需一次I/O。(强烈的对比)
4.不过,如果涉及到大数据量的排序、全表扫描、count之类的操作的话,还是MyISAM占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。
5.当使用主键为聚簇索引时,主键最好不要使用uuid,因为uuid的值太过离散,不适合排序且可能出线新增加记录的uuid,会插入在索引树中间的位置,导致索引树调整复杂度变大,消耗更多的时间和资源。建议使用int类型的自增,方便排序并且默认会在索引树的末尾增加主键值,对索引树的结构影响最小。而且,主键值占用的存储空间越大,辅助索引中保存的主键值也会跟着变大,占用存储空间,也会影响到IO操作读取到的数据量。
4.7稠密索引与稀疏索引
在了解稠密索引和稀疏索引之前我们先来了解一下什么是聚焦索引。在一个文件中,可以有多个索引,分别基于不同的搜索码。如果包含数据记录的文件按照某个指定的顺序排列,那么该搜索码对应的索引就是聚焦索引。
4.7.1稠密索引
在稠密索引中,文件中的每个搜索码值都对应一个索引值。也就是说,稠密索引为数据记录文件的每一条记录都设一个键-指针对。如下图所示,索引项包括索引值以及指向该搜索码的第一条数据记录的指针,即我们所说的键-指针对。
4.7.2稀疏索引
在稀疏索引中,只为搜索码的某些值建立索引项。也就是说,稀疏索引为数据记录文件的每个存储块设一个键-指针对,存储块意味着块内存储单元连续。如下图所示。
5.索引的底层原理
此节我们抛开其他的数据库索引实现,主讲Mysql的索引底层实现。说到Mysql的索引,了解过的人应该知道,其底层是通过B+数来实现的数据结构存储。
数据存储结构,决定了数据查找和操作时的效率,包括时间复杂度和空间复杂度。而在取舍的时候,也无非就是时间换空间,空间换时间的权衡罢了。所以,这就很好的解释了,为什么Mysql在索引的底层设计上,选用了B+数,而没有选用B-树,或是红黑树,AVL树等等其他数据结构。总之,就是使用B+树作为索引的结构存储,能在I/O性能上得到一个较大的优势。
那么具体优势在哪里呢?咱们继续往下看。
本章我们以B-树与B+树的对比,来阐述具体差异和B+树的优势。
5.1 B-Tree
B-树是一种多路自平衡的搜索树 它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。B-Tree 相对于 AVLTree 缩减了节点个数,使每次磁盘 I/O 取到内存的数据都发挥了作用,从而提高了查询效率。
注:B-Tree就是我们常说的B树
那么m阶 B-Tree 是满足下列条件的数据结构:
- 所有键值分布在整颗树中
- 搜索有可能在非叶子结点结束,在关键字全集内做一次查找,性能逼近二分查找
- 每个节点最多拥有m个子树
- 根节点至少有2个子树
- 分支节点至少拥有m/2颗子树(除根节点和叶子节点外都是分支节点)
- 所有叶子节点都在同一层、每个节点最多可以有m-1个key,并且以升序排列
每个节点占用一个磁盘块,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为 17 和 35,P1 指针指向的子树的数据范围为小于 17,P2 指针指向的子树的数据范围为 17~35,P3 指针指向的子树的数据范围为大于 35。
模拟查找关键字 29 的过程:
- 根据根节点找到磁盘块 1,读入内存。【磁盘 I/O 操作第 1 次】
- 比较关键字 29 在区间(17,35),找到磁盘块 1 的指针 P2。
- 根据 P2 指针找到磁盘块 3,读入内存。【磁盘 I/O 操作第 2 次】
- 比较关键字 29 在区间(26,30),找到磁盘块 3 的指针 P2。
- 根据 P2 指针找到磁盘块 8,读入内存。【磁盘 I/O 操作第 3 次】
- 在磁盘块 8 中的关键字列表中找到关键字 29。
- 分析上面过程,发现需要 3 次磁盘 I/O 操作,和 3 次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而 3 次磁盘 I/O 操作是影响整个 B-Tree 查找效率的决定因素。
但同时B-Tree也存在问题:
- 每个节点中有key,也有data,而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的 key 的数量很小。
- 当存储的数据量很大时同样会导致 B-Tree 的深度较大,增大查询时的磁盘 I/O 次数,进而影响查询效率
5.2 B+Tree
B+Tree 是在 B-Tree 基础上的一种优化,InnoDB 存储引擎就是用 B+Tree 实现其索引结构。它带来的变化点:
- B+树每个节点可以包含更多的节点,这样做有两个原因,一个是降低树的高度。另外一个是将数据范围变为多个区间,区间越多,数据检索越快
- 非叶子节点存储key,叶子节点存储key和数据
- 叶子节点两两指针相互链接(符合磁盘的预读特性),顺序查询性能更高
注:MySQL 的 InnoDB 存储引擎在设计时是将根节点常驻内存的,因此力求达到树的深度不超过 3,也就是说 I/O 不需要超过 3 次。
通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对 B+Tree 进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。
5.3 B-树和B+树的区别
- B+树内节点不存储数据,所有数据存储在叶节点导致查询时间复杂度固定为 log n
- B-树查询时间复杂度不固定,与 key 在树中的位置有关,最好为O(1)
- B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等
- B+树更适合外部存储(存储磁盘数据)。由于内节点无 data 域,每个节点能索引的范围更大更精确。
6.总结
本章内容其实是我个人对索引这块的知识点的巩固与梳理。之前有很多总结,比较零散,趁当下有点时间,所以整理了出来。其中还有很多不足,或是有瑕疵的地方,若有不对之处,尽情拍砖指正。最后,也希望能够帮助到,正在学习的你,加油!
参考资料:https://blog.csdn.net/qq_36071795/article/details/83956068
https://blog.csdn.net/qq_17707713/article/details/90059408
https://blog.csdn.net/guzhangyu12345/article/details/96423704
https://my.oschina.net/xiaoyoung/blog/3046779
来源:CSDN
作者:一个砖猿
链接:https://blog.csdn.net/yy339452689/article/details/103816128