数据库索引的数据结构和原理介绍

一、引言

如果我们新建一个没加主键的表,那么当我们向表中插入数据时,这些数据会无序的存放在磁盘存储器上,一行一行的排列的很整齐, 这样的数据在表中是无序的,只与插入顺序有关。当我们想要查询表中数据时,这时只能拿着查询条件一条一条逐一的与数据库中的数据进行比较,如果匹配到的数据正好是最后一条,这样一次查询就把表中的所有数据都匹配了一遍。如果数据库中只有几百条数据,这样的查询或许不会让你抓狂,但如果数据库中有几百万甚至几千万条的数据时,这样的查询效率就让你不止抓狂了。这时候或许你就要给表中的某个字段建立索引,来提高查询的效率。那么何为索引呢?建立索引为什么就能提高查询效率呢?下面就来详细学习一下。

二、索引

1、何为索引?

在关系型数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构它是表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。(说白了,就是索引列的值和指向具体某一条记录的指针)

索引提供指向存储在表的指定列中的数据值的指针,然后根据你指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息。

当表中有大量记录时,若要对表进行查询,第一种搜索信息方式是全表搜索,是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这样做会消耗大量数据库系统时间,并造成大量磁盘I/O操作;第二种就是在表中建立索引,然后在索引中找到符合查询条件的索引值,最后通过保存在索引中的 ROWID(相当于页码)快速找到表中对应的记录。

索引是为了加速对表中数据行的检索而创建的一种分散的存储结构。索引是针对表而建立的,它是由数据页面以外的索引页面组成的,每个索引页面中的行都会含有逻辑指针,以便加速检索物理数据。(☆☆☆☆☆)

2、索引类型

根据数据库的功能,可以在数据库设计器中创建四种索引:普通索引、唯一索引、主键索引、聚集索引。

(1)、普通索引

最基本的索引类型,没有唯一性之类的限制。普通索引可以通过以下几种方式创建:

创建索引,例如:create index <索引的名字> on tablename (列的列表);

修改表方式创建索引,例如:alter table tablename add index [索引的名字] (列的列表);

创建表的时候指定索引,例如:create table tablename ( [...], index [索引的名字] (列的列表) );
(2)、唯一索引(UNIQUE:建立唯一索引)

唯一索引是不允许其中任何两行具有相同索引值的索引。当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。例如,如果在 employee 表中员工的姓名 (name) 上创建了唯一索引,则任何两个员工都不能同姓。

对某个列建立UNIQUE索引后,插入新纪录时,数据库管理系统会自动检查新纪录在该列上是否取了重复值,在CREATE TABLE 命令中的UNIQE约束将隐式创建UNIQUE索引。

创建唯一索引的几种方式:

创建索引,例如:create unique index <索引的名字> on tablename (列的列表);

修改表方式创建索引,例如:alter table tablename add unique [索引的名字] (列的列表); 

创建表的时候指定索引,例如:create table tablename ( [...], unique [索引的名字] (列的列表) );
(3)、主键索引

简称主索引,数据库表中一列或列组合(字段)的值唯一标识表中的每一行。该列称为表的主键。在数据库中,为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。

尽管唯一索引有助于定位信息,但为获得最佳性能结果,建议使用主键索引。

(4)、聚集索引(CLUSTERED:建立聚集索引。)

在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引, 即如果存在聚集索引,就不能再指定CLUSTERED 关键字。一个表中只能有一个聚集索引,但表中的每一列都可以有自己的非聚集索引。

索引不是聚集索引(非聚集索引),则表中行的物理顺序与键值的逻辑(索引)顺序不匹配。与非聚集索引相比,聚集索引通常提供更快的数据访问速度。聚集索引更适用于对很少对基表进行增删改操作的情况。

如果在表中创建了主键约束,SQL Server将自动为其产生唯一性约束。在创建主键约束时,指定了CLUSTERED关键字或干脆没有制定该关键字,SQL Sever将会自动为表生成唯一聚集索引。

3、索引的优缺点
(1)、优点
1)、大大加快数据的检索速度;
2)、创建唯一性索引,保证数据库表中每一行数据的唯一性;
3)、加速表和表之间的连接;
4)、在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。
(2)、缺点
1)、索引需要占物理空间。
2)、当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。

三、索引的存储结构

MySQL引擎与锁机制一文中说过,InnoDB和MyISAM两种引擎所使用的索引的数据结构都是B+树。

1、索引原理

在数据库中查找数据面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的查询问题呢?我们设想一下能不能把数据分成段,然后再分段查询呢?

举个最简单的例子,如果有1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段……这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是一千万的记录呢?分成几段比较好?稍有算法基础的人可能会想到搜索树,因为它的平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的,数据库实现比较复杂,数据保存在磁盘上,而为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。

2、磁盘IO与预读

前面提到了访问磁盘,那么这里先简单介绍一下磁盘IO和预读。磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS的机器每秒可以执行5亿条指令,因为指令依靠的是电的性质,换句话说执行一次IO的时间可以执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。

考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。

3、索引的数据结构

前面说了索引的基本原理、数据库的复杂性,又介绍了操作系统的相关知识,现在总结一下:我们需要把每次查找数据时的磁盘IO次数控制在一个很小的数量级,最好是常数数量级。就这样,B+树应运而生。关于数据结构——树,可以参考另一篇博文数据结构——树与二叉树

4、详解使用B+树查找数据过程

在这里插入图片描述
上图是一棵B+树,浅蓝色的块称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示)。如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。

(1)、B+树的查找过程

在上图中,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的B+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。

(2)、B+树性质
性质一:

通过上面的分析,我们知道IO次数取决于B+数的高度 h ,假设当前数据表的数据为 N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N。当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。

结论:这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么B+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。

B+树的高度计算:第一层为m个数据项,第二层为(m+1)*m个数据项,第三层为(m+1)*(m+1)*m个数据项,第四层依次类推,最后用等比公式的求和公式,就可以得到上面的公式。
性质二:

当B+树的数据项是复合的数据结构,比如(name,age,sex)的时候,B+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,B+树会优先比较name来确定下一步的搜索方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,B+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询(索引失效)。比如当(张三,F)这样的数据来检索时,B+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。

注意:索引的最左前缀原则

(3)、慢查询优化

了解完索引原理之后,我们来看看慢查询,大家是不是有什么想法呢?先总结一下索引的几大基本原则。

建索引的几大原则

(1)、最左前缀匹配原则。MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

(2)、= 和 in 可以乱序。比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,MySQL的查询优化器会帮你优化成索引可以识别的形式。

(3)、尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例。比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。

(4)、索引列不能参与计算,保持列“干净”。比如from_unixtime(create_time) = ’2019-01-13’就不能使用到索引,原因很简单,B+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2019-01-13’)。

(5)、尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

四、小结

1、索引树结构中各节点的值来自于表中的索引字段, 假如给user表的name字段加上索引 , 那么索引就是由name字段中的值构成。

2、聚集索引决定了表中数据的物理存储顺序,那么一个表则有且只有一个聚集索引。聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。

3、如果给表加上了主键,那么表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构,也就是平衡树结构,换句话说,就是整个表就变成了一个索引。

4、主键的作用就是把表的数据格式转换成索引(平衡树)的格式放置。

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值