精华内容
下载资源
问答
  • 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题。特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文...
  • 数据库索引原理

    2018-08-26 11:32:22
    https://www.cnblogs.com/aspwebchh/p/6652855.html
    展开全文
  • 【数据库】数据库索引原理

    千次阅读 2019-06-04 08:45:40
    3.索引原理B+ tree4.B+ tree 在两大引擎中的体现5.索引的原则 1.索引是什么? 索引是为了加速对表中数据行的检索而创建的一种分散存储的数据结构。 2.为什么? 索引能极大的减少存储引擎需要扫描的数据量 索引可以...

    正确的创建合适的索引 是提升数据库查询性能的基础

    1.索引是什么?

    索引是为了加速对表中数据行的检索而创建的一种分散存储的数据结构。
    在这里插入图片描述

    2.为什么?

    索引能极大的减少存储引擎需要扫描的数据量
    索引可以把随机IO变成顺序IO
    索引可以帮助我们在进行分组、排序等操作时,避免使 用临时表

    3.索引原理

    B+ tree

    为什么用B+ tree? 而不用二叉树,平衡树
    二叉树
    在这里插入图片描述
    平横树图
    在这里插入图片描述
    它太深了
    数据处的(高)深度决定着他的IO操作次数,IO操作耗时大
    它太小了 每一个磁盘块(节点/页)保存的数据量太小了,没有很好的利用操作磁盘IO的数据交换特性,也没有利用好磁盘IO的预读能力(空间局部性原理),从而带来频繁的IO操作。磁盘IO操作是以页为单位,每一页可以存储4kB的数据。

    B 树
    在这里插入图片描述特点:
    每个节点上可以有多个关键字,每个节点都存储着关键字对应的数据。
    子节点不存储父节点的关键字

    B+ 树
    在这里插入图片描述
    B+树与B 树区别:
    1,B+节点关键字搜索采用闭合区间
    2,B+非叶节点不保存数据相关信息,只保存关键字和子节点的引用
    3,B+关键字对应的数据保存在叶子节点中
    4,B+叶子节点是顺序排列的,并且相邻节点具有顺序引用的关系

    B+树是B-树的变种(PLUS版)多路绝对平衡查找树,他拥有B-树的优势
    B+树扫库、表能力更强
    B+树的磁盘读写能力更强
    B+树的排序能力更强
    B+树的查询效率更加稳定

    4.B+ tree 在两大引擎中的体现

    Myisam
    在这里插入图片描述
    在这里插入图片描述
    InnoDB
    主键索引属于聚集索引
    在这里插入图片描述
    在这里插入图片描述

    5.索引的原则

    5.1 离散性越高越好
    5.2 最左匹配原则
    对索引中关键字进行计算(对比),一定是从左往右依次进行,且不可跳过
    5.3 索引列的数据长度能少则少
    5.4 匹配列前缀可用到索引 like %9999%、like %9999用不到索引;
    5.5 Where 条件中 not in 和 <>操作无法使用索引
    5.6 多用指定列查询,只返回自己想到的数据列,少用select *,这样有可能用到覆盖索引;
    5.7 索引上不加计算

    联合索引:
    联合索引列选择原则
    1,经常用的列优先 【最左匹配原则】
    2,选择性(离散度)高的列优先【离散度高原则】
    3,宽度小的列优先【最少空间原则】
    联合索引中如果不是按照索引最左列开始查找,无法使用索引;
    联合索引中精确匹配最左前列并范围匹配另外一列可以用到索引;
    联合索引中如果查询中有某个列的范围查询,则其右边的所有列都无法使用索引;

    参考文章:
    https://www.zhihu.com/question/36996520

    展开全文
  • 数据库索引原理 b树

    千次阅读 2018-05-17 09:49:38
    ①在向数据库中插入新的数据时,同时也需要向数据库索引中插入相应的索引键值 ,则需要向 B+树 中插入新的键值。 即上面我们提到的B-树插入算法。 ②当从数据库中删除数据时,同时也需要从数据库索引中删除相应的...

    1 .B-树定义

    B-树是一种平衡的多路查找树,它在文件系统中很有用。

    定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的m 叉树:
    ⑴树中每个结点至多有m 棵子树;
    ⑵若根结点不是叶子结点,则至少有两棵子树;

    ⑶除根结点之外的所有非终端结点至少有[m/2] 棵子树;
    ⑷所有的非终端结点中包含以下信息数据:

          (n,A0,K1,A1,K2,…,Kn,An)
    其中:Ki(i=1,2,…,n)为关键码,且Ki<Ki+1,

               Ai 为指向子树根结点的指针(i=0,1,…,n),且指针Ai-1 所指子树中所有结点的关键码均小于Ki (i=1,2,…,n),An 所指子树中所有结点的关键码均大于Kn.

               n   为关键码的个数。
    ⑸所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。

       即所有叶节点具有相同的深度,等于树高度。



    B-树主要应用在文件系统

    为了将大型数据库文件存储在硬盘上 以减少访问硬盘次数为目的 在此提出了一种平衡多路查找树——B-树结构 由其性能分析可知它的检索效率是相当高的 为了提高 B-树性能’还有很多种B-树的变型,力图对B-树进行改进


    B+树是应文件系统所需而产生的一种B-树的 变形树 。一棵m 阶的B+树和m 阶的B- 树的差异在于:

    ⑴有n 棵子树的结点中含有n 个关键码;
    ⑵所有的叶子结点中包含了全部关键码的信息,及指向含有这些关键码记录的指针,且
    叶子结点本身依关键码的大小自小而大的顺序链接。
    ⑶所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键码。

    B树和B+树的区别


     

    如图所示,区别有以下两点:

    1. B+树中只有叶子节点会带有指向记录的指针(ROWID),而B树则所有节点都带有,在内部节点出现的索引项不会再出现在叶子节点中。

    2. B+树中所有叶子节点都是通过指针连接在一起,而B树不会。

     

    B+树的优点:

    1. 非叶子节点不会带上ROWID,这样,一个块中可以容纳更多的索引项,一是可以降低树的高度。二是一个内部节点可以定位更多的叶子节点。

    2. 叶子节点之间通过指针来连接,范围扫描将十分简单,而对于B树来说,则需要在叶子节点和内部节点不停的往返移动。

     

    B树的优点:

    对于在内部节点的数据,可直接得到,不必根据叶子节点来定位。


    B+树在数据库中的应用

    1. 索引在数据库中的作用 

            在数据库系统的使用过程当中,数据的查询是使用最频繁的一种数据操作。

            最基本的查询算法当然是顺序查找(linear search),遍历表然后逐行匹配行值是否等于待查找的关键字,其时间复杂度为O(n)。但时间复杂度为O(n)的算法规模小的表,负载轻的数据库,也能有好的性能。  但是数据增大的时候,时间复杂度为O(n)的算法显然是糟糕的,性能就很快下降了。

           好在计算机科学的发展提供了很多更优秀的查找算法,例如二分查找(binary search)、二叉树查找(binary tree search)等。如果稍微分析一下会发现,每种查找算法都只能应用于特定的数据结构之上,例如二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,但是数据本身的组织结构不可能完全满足各种数据结构(例如,理论上不可能同时将两列都按顺序进行组织),所以,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

           索引是对数据库表 中一个或多个列的值进行排序的结构。与在表 中搜索所有的行相比,索引用指针 指向存储在表中指定列的数据值,然后根据指定的次序排列这些指针,有助于更快地获取信息。通常情 况下 ,只有当经常查询索引列中的数据时 ,才需要在表上创建索引。索引将占用磁盘空间,并且影响数 据更新的速度。但是在多数情况下 ,索引所带来的数据检索速度优势大大超过它的不足之处。

    2. B+树在数据库索引中的应用


    目前大部分数据库系统及文件系统都采用B-Tree或其变种B+Tree作为索引结构

    1)在数据库索引的应用

    在数据库索引的应用中,B+树按照下列方式进行组织   :

    ①  叶结点的组织方式 。B+树的查找键 是数据文件的主键 ,且索引是稠密的。也就是说 ,叶结点 中为数据文件的第一个记录设有一个键、指针对 ,该数据文件可以按主键排序,也可以不按主键排序 ;数据文件按主键排序,且 B +树是稀疏索引 ,  在叶结点中为数据文件的每一个块设有一个键、指针对 ;数据文件不按键属性排序 ,且该属性是 B +树 的查找键 , 叶结点中为数据文件里出现的每个属性K设有一个键 、 指针对 , 其中指针执行排序键值为 K的 记录中的第一个。

    ② 非叶结点 的组织方式。B+树 中的非叶结点形成 了叶结点上的一个多级稀疏索引。  每个非叶结点中至少有ceil( m/2 ) 个指针 , 至多有 m 个指针 。  

    2)B+树索引的插入和删除

    ①在向数据库中插入新的数据时,同时也需要向数据库索引中插入相应的索引键值 ,则需要向 B+树 中插入新的键值。即上面我们提到的B-树插入算法。

    ②当从数据库中删除数据时,同时也需要从数据库索引中删除相应的索引键值 ,则需要从 B+树 中删 除该键值 。即B-树删除算法

    为什么使用B-Tree(B+Tree)

         二叉查找树进化品种的红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-/+Tree作为索引结构。

     一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。为什么使用B-/+Tree,还跟磁盘存取原理有关。

           局部性原理与磁盘预读

      由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:

      当一个数据被用到时,其附近的数据也通常会马上被使用。

      程序运行期间所需要的数据通常比较集中。

      由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。

      预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。

     

          我们上面分析B-/+Tree检索一次最多需要访问节点:

         h =

        

          数据库系统巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B- Tree还需要使用如下技巧:

          每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

      B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logmN)。一般实际应用中,m是非常大的数字,通常超过100,因此h非常小(通常不超过3)。

      综上所述,用B-Tree作为索引结构效率是非常高的。

      而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。

     

    MySQL的B-Tree索引(技术上说B+Tree)

           在 MySQL 中,主要有四种类型的索引,分别为: B-Tree 索引, Hash 索引, Fulltext 索引和 R-Tree 索引。我们主要分析B-Tree 索引。

            B-Tree 索引是 MySQL 数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有的存储引擎都支持 B-Tree 索引。Archive 引擎直到 MySQL 5.1 才支持索引,而且只支持索引单个 AUTO_INCREMENT 列。

           不仅仅在 MySQL 中是如此,实际上在其他的很多数据库管理系统中B-Tree 索引也同样是作为最主要的索引类型,这主要是因为 B-Tree 索引的存储结构在数据库的数据检索中有非常优异的表现。

         一般来说, MySQL 中的 B-Tree 索引的物理文件大多都是以 Balance Tree 的结构来存储的,也就是所有实际需要的数据都存放于 Tree 的 Leaf Node(叶子节点) ,而且到任何一个 Leaf Node 的最短路径的长度都是完全相同的,所以我们大家都称之为 B-Tree 索引。当然,可能各种数据库(或 MySQL 的各种存储引擎)在存放自己的 B-Tree 索引的时候会对存储结构稍作改造。如 Innodb 存储引擎的 B-Tree 索引实际使用的存储结构实际上是 B+Tree,也就是在 B-Tree 数据结构的基础上做了很小的改造,在每一个Leaf Node 上面出了存放索引键的相关信息之外,还存储了指向与该 Leaf Node 相邻的后一个 LeafNode 的指针信息(增加了顺序访问指针),这主要是为了加快检索多个相邻 Leaf Node 的效率考虑。

     

    下面主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式:

    1. MyISAM索引实现:

    1)主键索引:

    MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM主键索引的原理图:


                                                                               (图myisam1)

    这里设表一共有三列,假设我们以Col1为主键,图myisam1是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。

    2)辅助索引(Secondary key)

    在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:
      

     

    同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。

    MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。

     

    2. InnoDB索引实现

    然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同.

    1)主键索引:

             MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。

                   (图inndb主键索引)

     

     

    (图inndb主键索引)是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。

     

    2). InnoDB的辅助索引

           InnoDB的所有辅助索引都引用主键作为data域。例如,下图为定义在Col3上的一个辅助索引:

     

        

           

            InnoDB 表是基于聚簇索引建立的。因此InnoDB 的索引能提供一种非常快速的主键查找性能。不过,它的辅助索引(Secondary Index, 也就是非主键索引)也会包含主键列,所以,如果主键定义的比较大,其他索引也将很大。如果想在表上定义 、很多索引,则争取尽量把主键定义得小一些。InnoDB 不会压缩索引。

          文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

          不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

     InnoDB索引MyISAM索引的区别:

    一是主索引的区别,InnoDB的数据文件本身就是索引文件。而MyISAM的索引和数据是分开的。

    二是辅助索引的区别:InnoDB的辅助索引data域存储相应记录主键的值而不是地址。而MyISAM的辅助索引和主索引没有多大区别


    展开全文
  • mongo数据库索引原理

    万次阅读 2017-08-05 00:40:19
    索引的本质 索引(Index)是帮助数据库高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。 现在的数据库(mongo,mysql等)索引多采用B-Tree数据结构

    一、索引的本质

    索引(Index)是帮助数据库高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构。

    现在的数据库(mongo,mysql等)索引多采用B-Tree数据结构,不懂BTree的同学先自行去了解下,个人觉得这篇文章比较易懂一些,http://www.cnblogs.com/coder2012/p/5309197.html

    为什么使用B-Tree(B+Tree)

    红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-/+Tree作为索引结构,这一节将结合计算机组成原理相关知识讨论B-/+Tree作为索引的理论基础。

    一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在 磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。下面先介绍内存和磁盘存取原理,然后再结合这些原理分析B-/+Tree作为索引的效率。

    主存存取原理

    目前计算机使用的主存基本都是随机读写存储器(RAM),现代RAM的结构和存取原理比较复杂,这里本文抛却具体差别,抽象出一个十分简单的存取模型来说明RAM的工作原理。

    MySQL索引背后的数据结构及算法原理

    图5

    从抽象角度看,主存是一系列的存储单元组成的矩阵,每个存储单元存储固定大小的数据。每个存储单元有唯一的地址,现代主存的编址规则比较复杂,这里将其简化成一个二维地址:通过一个行地址和一个列地址可以唯一定位到一个存储单元。图5展示了一个4 x 4的主存模型。

    主存的存取过程如下:

    当系统需要读取主存时,则将地址信号放到地址总线上传给主存,主存读到地址信号后,解析信号并定位到指定存储单元,然后将此存储单元数据放到数据总线上,供其它部件读取。

    写主存的过程类似,系统将要写入单元地址和数据分别放在地址总线和数据总线上,主存读取两个总线的内容,做相应的写操作。

    这里可以看出,主存存取的时间仅与存取次数呈线性关系,因为不存在机械操作,两次存取的数据的“距离”不会对时间有任何影响,例如,先取A0再取A1和先取A0再取D3的时间消耗是一样的。

    磁盘存取原理

    上文说过,索引一般以文件形式存储在磁盘上,索引检索需要磁盘I/O操作。与主存不同,磁盘I/O存在机械运动耗费,因此磁盘I/O的时间消耗是巨大的。

    图6是磁盘的整体结构示意图。

    MySQL索引背后的数据结构及算法原理

    图6

    一个磁盘由大小相同且同轴的圆形盘片组成,磁盘可以转动(各个磁盘必须同步转动)。在磁盘的一侧有磁头支架,磁头支架固定了一组磁头,每个磁头负责存取一个磁盘的内容。磁头不能转动,但是可以沿磁盘半径方向运动(实际是斜切向运动),每个磁头同一时刻也必须是同轴的,即从正上方向下看,所有磁头任何时候都是重叠的(不过目前已经有多磁头独立技术,可不受此限制)。

    图7是磁盘结构的示意图。

    MySQL索引背后的数据结构及算法原理

    图7

    盘片被划分成一系列同心环,圆心是盘片中心,每个同心环叫做一个磁道,所有半径相同的磁道组成一个柱面。磁道被沿半径线划分成一个个小的段,每个段叫做一个扇区,每个扇区是磁盘的最小存储单元。为了简单起见,我们下面假设磁盘只有一个盘片和一个磁头。

    当需要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即确定要读的数据在哪个磁道,哪个扇区。为了读取这个扇区的数据,需要将磁头放到这个扇区上方,为了实现这一点,磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间,然后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫做旋转时间。

    局部性原理与磁盘预读

    由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:

    当一个数据被用到时,其附近的数据也通常会马上被使用。

    程序运行期间所需要的数据通常比较集中。

    由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。

    预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。

    B-/+Tree索引的性能分析

    到这里终于可以分析B-/+Tree索引的性能了。

    上文说过一般使用磁盘I/O次数评价索引结构的优劣。先从B-Tree分析,根据B-Tree的定义,可知检索一次最多需要访问h(h为数高)个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:

    每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

    B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)。一般实际应用中,出度d是非常大的数字,通常超过100,因此h非常小(通常不超过3)。

    综上所述,用B-Tree作为索引结构效率是非常高的。

    而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。

    B+Tree更适合外存索引,原因和内节点出度d有关。从上面分析可以看到,d越大索引的性能越好,而出度的上限取决于节点内key和data的大小:

    dmax = floor(pagesize / (keysize + datasize + pointsize))   (pagesize – dmax >= pointsize)

    dmax = floor(pagesize / (keysize + datasize + pointsize)) – 1   (pagesize – dmax < pointsize)

    floor表示向下取整。由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。

    二、mongo中的索引

    为什么需要索引?

    当你抱怨MongoDB集合查询效率低的时候,可能你就需要考虑使用索引了,为了方便后续介绍,先科普下MongoDB里的索引机制(同样适用于其他的数据库比如mysql)。

    mongo-9552:PRIMARY&gt; db.person.find()
    { "_id" : ObjectId("571b5da31b0d530a03b3ce82"), "name" : "jack", "age" : 19 }
    { "_id" : ObjectId("571b5dae1b0d530a03b3ce83"), "name" : "rose", "age" : 20 }
    { "_id" : ObjectId("571b5db81b0d530a03b3ce84"), "name" : "jack", "age" : 18 }
    { "_id" : ObjectId("571b5dc21b0d530a03b3ce85"), "name" : "tony", "age" : 21 }
    { "_id" : ObjectId("571b5dc21b0d530a03b3ce86"), "name" : "adam", "age" : 18 }
    

    当你往某各个集合插入多个文档后,每个文档在经过底层的存储引擎持久化后,会有一个位置信息,通过这个位置信息,就能从存储引擎里读出该文档。比如mmapv1引擎里,位置信息是『文件id + 文件内offset 』, 在wiredtiger存储引擎(一个KV存储引擎)里,位置信息是wiredtiger在存储文档时生成的一个key,通过这个key能访问到对应的文档;为方便介绍,统一用pos(position的缩写)来代表位置信息。

    比如上面的例子里,person集合里包含插入了4个文档,假设其存储后位置信息如下(为方便描述,文档省去_id字段)

    位置信息文档
    pos1{“name” : “jack”, “age” : 19 }
    pos2{“name” : “rose”, “age” : 20 }
    pos3{“name” : “jack”, “age” : 18 }
    pos4{“name” : “tony”, “age” : 21}
    pos5{“name” : “adam”, “age” : 18}

    假设现在有个查询 db.person.find( {age: 18} ), 查询所有年龄为18岁的人,这时需要遍历所有的文档(『全表扫描』),根据位置信息读出文档,对比age字段是否为18。当然如果只有4个文档,全表扫描的开销并不大,但如果集合文档数量到百万、甚至千万上亿的时候,对集合进行全表扫描开销是非常大的,一个查询耗费数十秒甚至几分钟都有可能。

    如果想加速 db.person.find( {age: 18} ),就可以考虑对person表的age字段建立索引

    db.person.createIndex( {age: 1} )  // 按age字段创建升序索引
    

    建立索引后,MongoDB会额外存储一份按age字段升序排序的索引数据,索引结构类似如下,索引通常采用类似btree的结构持久化存储,以保证从索引里快速(O(logN)的时间复杂度)找出某个age值对应的位置信息,然后根据位置信息就能读取出对应的文档。

    AGE位置信息
    18pos3
    18pos5
    19pos1
    20pos2
    21pos4

    简单的说,索引就是将文档按照某个(或某些)字段顺序组织起来,以便能根据该字段高效的查询。有了索引,至少能优化如下场景的效率:

    • 查询,比如查询年龄为18的所有人
    • 更新/删除,将年龄为18的所有人的信息更新或删除,因为更新或删除时,需要根据条件先查询出所有符合条件的文档,所以本质上还是在优化查询
    • 排序,将所有人的信息按年龄排序,如果没有索引,需要全表扫描文档,然后再对扫描的结果进行排序

    众所周知,MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字段),_id是文档唯一的标识,为了保证能根据文档id快递查询文档,MongoDB默认会为集合创建_id字段的索引。

    mongo-9552:PRIMARY&gt; db.person.getIndexes() // 查询集合的索引信息
    [
        {
            "ns" : "test.person",  // 集合名
            "v" : 1,               // 索引版本
            "key" : {              // 索引的字段及排序方向
                "_id" : 1           // 根据_id字段升序索引
            },
            "name" : "_id_"        // 索引的名称
        }
    ]
    

    MongoDB索引类型

    MongoDB支持多种类型的索引,包括单字段索引、复合索引、多key索引、文本索引等,每种类型的索引有不同的使用场合。

    单字段索引 (Single Field Index)

        db.person.createIndex( {age: 1} ) 
    

    上述语句针对age创建了单字段索引,其能加速对age字段的各种查询请求,是最常见的索引形式,MongoDB默认创建的id索引也是这种类型。

    {age: 1} 代表升序索引,也可以通过{age: -1}来指定降序索引,对于单字段索引,升序/降序效果是一样的。

    复合索引 (Compound Index)

    复合索引是Single Field Index的升级版本,它针对多个字段联合创建索引,先按第一个字段排序,第一个字段相同的文档按第二个字段排序,依次类推,如下针对age, name这2个字段创建一个复合索引。

        db.person.createIndex( {age: 1, name: 1} ) 
    

    上述索引对应的数据组织类似下表,与{age: 1}索引不同的时,当age字段相同时,在根据name字段进行排序,所以pos5对应的文档排在pos3之前。

    AGE,NAME位置信息
    18,adampos5
    18,jackpos3
    19,jackpos1
    20,rosepos2
    21,tonypos4

    复合索引能满足的查询场景比单字段索引更丰富,不光能满足多个字段组合起来的查询,比如db.person.find( {age: 18, name: "jack"} ),也能满足所以能匹配符合索引前缀的查询,这里{age: 1}即为{age: 1, name: 1}的前缀,所以类似db.person.find( {age: 18} )的查询也能通过该索引来加速;但db.person.find( {name: "jack"} )则无法使用该复合索引。如果经常需要根据『name字段』以及『name和age字段组合』来查询,则应该创建如下的复合索引

    db.person.createIndex( {name: 1, age: 1} ) 
    

    除了查询的需求能够影响索引的顺序,字段的值分布也是一个重要的考量因素,即使person集合所有的查询都是『name和age字段组合』(指定特定的name和age),字段的顺序也是有影响的。

    age字段的取值很有限,即拥有相同age字段的文档会有很多;而name字段的取值则丰富很多,拥有相同name字段的文档很少;显然先按name字段查找,再在相同name的文档里查找age字段更为高效。

    多key索引 (Multikey Index)

    当索引的字段为数组时,创建出的索引称为多key索引,多key索引会为数组的每个元素建立一条索引,比如person表加入一个habbit字段(数组)用于描述兴趣爱好,需要查询有相同兴趣爱好的人就可以利用habbit字段的多key索引。

    {"name" : "jack", "age" : 19, habbit: ["football, runnning"]}
    db.person.createIndex( {habbit: 1} )  // 自动创建多key索引
    db.person.find( {habbit: "football"} )
    

    其他类型索引

    哈希索引(Hashed Index)是指按照某个字段的hash值来建立索引,目前主要用于MongoDB Sharded Cluster的Hash分片,hash索引只能满足字段完全匹配的查询,不能满足范围查询等。

    地理位置索引(Geospatial Index)能很好的解决O2O的应用场景,比如『查找附近的美食』、『查找某个区域内的车站』等。

    文本索引(Text Index)能解决快速文本查找的需求,比如有一个博客文章集合,需要根据博客的内容来快速查找,则可以针对博客内容建立文本索引。

    索引额外属性

    MongoDB除了支持多种不同类型的索引,还能对索引定制一些特殊的属性。

    • 唯一索引 (unique index):保证索引对应的字段不会出现相同的值,比如_id索引就是唯一索引
    • TTL索引:可以针对某个时间字段,指定文档的过期时间(经过指定时间后过期 或 在某个时间点过期)
    • 部分索引 (partial index): 只针对符合某个特定条件的文档建立索引,3.2版本才支持该特性
    • 稀疏索引(sparse index): 只针对存在索引字段的文档建立索引,可看做是部分索引的一种特殊情况

    查询计划

    索引已经建立了,但查询还是很慢怎么破?这时就得深入的分析下索引的使用情况了,可通过查看下详细的查询计划来决定如何优化。通过执行计划可以看出如下问题

    1. 根据某个/些字段查询,但没有建立索引
    2. 根据某个/些字段查询,但建立了多个索引,执行查询时没有使用预期的索引。

    建立索引前,db.person.find( {age: 18} )必须执行COLLSCAN,即全表扫描。

    mongo-9552:PRIMARY&gt; db.person.find({age: 18}).explain()
    {
        "queryPlanner" : {
            "plannerVersion" : 1,
            "namespace" : "test.person",
            "indexFilterSet" : false,
            "parsedQuery" : {
                "age" : {
                    "$eq" : 18
                }
            },
            "winningPlan" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    "age" : {
                        "$eq" : 18
                    }
                },
                "direction" : "forward"
            },
            "rejectedPlans" : [ ]
        },
        "serverInfo" : {
            "host" : "localhost",
            "port" : 9552,
            "version" : "3.2.3",
            "gitVersion" : "b326ba837cf6f49d65c2f85e1b70f6f31ece7937"
        },
        "ok" : 1
    }
    

    建立索引后,通过查询计划可以看出,先进行[IXSCAN]((https://docs.mongodb.org/manual/reference/explain-results/#queryplanner)(从索引中查找),然后FETCH,读取出满足条件的文档。

    mongo-9552:PRIMARY&gt; db.person.find({age: 18}).explain()
    {
        "queryPlanner" : {
            "plannerVersion" : 1,
            "namespace" : "test.person",
            "indexFilterSet" : false,
            "parsedQuery" : {
                "age" : {
                    "$eq" : 18
                }
            },
            "winningPlan" : {
                "stage" : "FETCH",
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "age" : 1
                    },
                    "indexName" : "age_1",
                    "isMultiKey" : false,
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 1,
                    "direction" : "forward",
                    "indexBounds" : {
                        "age" : [
                            "[18.0, 18.0]"
                        ]
                    }
                }
            },
            "rejectedPlans" : [ ]
        },
        "serverInfo" : {
            "host" : "localhost",
            "port" : 9552,
            "version" : "3.2.3",
            "gitVersion" : "b326ba837cf6f49d65c2f85e1b70f6f31ece7937"
        },
        "ok" : 1
    }

    三、注意事项

    既然索引可以加快查询速度,那么是不是只要是查询语句需要,就建上索引?答案是否定的。因为索引虽然加快了查询速度,但索引也是有代价的:索引文件本身要消耗存储空间,同时索引会加重插入、删除和修改记录时的负担,另外,数据库在运行时也要消耗资源维护索引,因此索引并不是越多越好。一般两种情况下不建议建索引。

    第一种情况是表记录比较少,例如一两千条甚至只有几百条记录的表,没必要建索引,让查询做全表扫描就好了。至于多少条记录才算多,这个个人有个人的看法,我个人的经验是以2000作为分界线,记录数不超过 2000可以考虑不建索引,超过2000条可以酌情考虑索引。

    另一种不建议建索引的情况是索引的选择性较低。所谓索引的选择性(Selectivity),是指不重复的索引值(也叫基数,Cardinality)与表记录数(#T)的比值:

    Index Selectivity = Cardinality / #T

    常见慢查询:

    1.不等于和不包含查询

    2.通配符在前面的模糊查询, like '%xxx'

    3.无索引的count 查询 和 排序(复合索引顺序不匹配)

    4.多个范围查询(范围列可以用到索引(必须是最左前缀),但是范围列后面的列无法用到索引

    5.skip跳过过多的行数(优化方案:我们第一页可以用db.article.find().limit(articles_of_each_page),并记录最后一片文章的_id(或者其他排序值),之后查询db.article.find({_id:{$lt:_id_stored}}).limit(articles_of_each_page)来查找下一页或者类似的,上一页的文章,可以避免大量计数.

    四、正确建立索引

    在没有建立索引的情况下,对Mongodb数据表进行查询操作的时候,需要把数据都加载到内存。当数据的数量达到几十万乃至上百万的时候,这样的加载过程会对系统造成较大的冲击,并影响到其他请求的处理过程。

    索引是对数据库表中一列或多列的值进行排序的一种结构,建立索引以后,对索引字段进行查询时,仅会加载索引数据,并能提高查询速度。

    1、建立合适的索引

    为每一个查询建立合适的索引。

    组合索引是创建的索引由多个字段组成,例如:

    db.test.ensureIndex({"username":1, "age":-1}) #1是按升序排列,-1是按降序排列 

    交叉索引是每个字段单独建立索引,但是在查询的时候组合查找,例如:

    db.test.ensureIndex({"username":1}) db.test.ensureIndex({"age":-1}) db.test.find({"username":"kaka", "age": 30}) 

    交叉索引的查询效率较低,在使用时,当查询使用到多个字段的时候,尽量使用组合索引,而不是交叉索引。

    2、组合索引的字段排列顺序

    当我们的组合索引内容包含匹配条件以及范围条件的时候,比如包含用户名(匹配条件)以及年龄(范围条件),那么匹配条件应该放在范围条件之前。

    比如需要查询:

    db.test.find({"username":"kaka", "age": {$gt: 10}}) 

    那么组合索引应该这样创建:

    db.test.ensureIndex({"username":1, "age":-1}) 

    3、查询时尽可能仅查询出索引字段

    有时候仅需要查询少部分的字段内容,而且这部分内容刚好都建立了索引,那么尽可能只查询出这些索引内容,需要用到的字段显式声明(_id字段需要显式忽略!)。因为这些数据需要把原始数据文档从磁盘读入内存,造成一定的损耗。

    比如说我们的表有三个字段:

    username, age, mobile 

    索引是这样建立的:

    db.test.ensureIndex({"username":1,"age":-1}) 

    我们仅需要查到某个用户的年龄(age),那可以这样写:

    db.test.find({"username":"kaka"}, {"_id":0, "age":1}) 

    注意到上面的语句,我们除了”age”:1外,还加了”_id”:0,因为默认情况下,_id都是会被一并查询出来的,当不需要_id的时候记得直接忽略,避免不必要的磁盘操作。

    4、对现有的数据大表建立索引的时候,采用后台运行方式

    在对数据集合建立索引的过程中,数据库会停止该集合的所有读写操作,因此如果建立索引的数据量大,建立过程慢的情况下,建议采用后台运行的方式,避免影响正常业务流程。

    db.test.ensureIndex({"username":1,"age":-1},{"background":true}) #默认情况下background是false。


    参考文章 

    索引的本质部分参考:http://blog.jobbole.com/24006/

    mongo中的索引部分参考:http://www.mongoing.com/archives/2797

    B-Tree数据结构:http://www.cnblogs.com/coder2012/p/5309197.html

    展开全文
  • Mysql数据库索引原理及算法原理

    千次阅读 多人点赞 2019-03-07 18:01:23
    面试的时候总会被提及一些关于数据库操作的问题,那么数据库索引作为一项热门问题,总会被问到。最近在网上看到了一篇关于mysql数据库索引的好文章,认真看完之后肯定受益匪浅,(虽说有的地方我不太理解)转来供...
  • 索引对于提高数据的查询速度具有十分重要的意义。
  • 面试题之数据库索引原理

    千次阅读 2019-03-12 23:22:44
    某年某月某日,在求职面试的过程中被多家公司问及数据库索引问题,问题如下: 1.你有了解索引吗? 2.你知道索引的具体实现原理吗? 3.你知道索引的数据结构是什么吗? 俗话说,初生牛犊不怕虎,不知者无畏,我第一...
  • 数据库索引实现原理

    万次阅读 多人点赞 2019-04-15 16:28:54
    MySQL索引实现 在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的,本文主要讨论...下图是MyISAM索引原理图: 图8 这里设表一共有三列,假设我们以Col1为主键,则图8是一个MyISA...
  • 深入浅出数据库索引原理

    万次阅读 2018-08-20 15:40:49
    使用索引很简单,只要能写创建表的语句,就肯定能写创建索引的语句,要知道这个世界上是不存在不会创建表的服务器端程序员的。然而, 会使用索引是一回事, 而深入理解索引原理又能恰到好处...
  • Oracle加数据库索引原理

    千次阅读 2019-03-06 16:48:19
    然而, 会使用索引是一回事, 而深入理解索引原理又能恰到好处使用索引又是另一回事,这完全是两个天差地别的境界(我自己也还没有达到这层境界)。很大一部份程序员对索引的了解仅限于到“加索引能使查询变快”这个...
  • 数据库索引原理 数据库索引原理 数据库索引原理 数据库索引原理
  • mysql数据库索引实现原理

    千次阅读 2019-06-01 21:27:11
    在介绍索引实现之前,我们先来了解下几种树的数据结构。 二叉搜索树 二叉搜索树有以下性质 1.每个节点有一个关键字 2.左右孩子至多有一个。 3.关键字大于左孩子,小于右孩子。 正因为二叉搜索树的特性,所以...
  • 通过两个图形说明了在oracle数据库中b-tree索引和位图索引的工作原理
  • 数据库索引原理实例

    千次阅读 2021-01-17 10:52:00
    --未使用索引,不存在to_char(d1,'yyyymmdd’)的函数索引 解决办法:改为d1 between to_date('20200102 00:00:00','yyyymmdd hh24:mi:ss') and to_date('20200102 23:59:59','yyyymmdd hh24:mi:ss') 也可以执行create...
  • 了解数据库索引及其原理

    万次阅读 多人点赞 2018-06-25 16:04:24
    索引这个词相信对于一个开发猿来说,就好比看到我们的代码一样低头不见抬头见,...然而, 会使用索引是一回事, 而深入理解索引原理又能恰到好处使用索引又是另一回事,这完全是两个天差地别的境界(我自己也还没有...
  • Oracle数据库索引底层实现原理笔记

    千次阅读 2020-08-19 16:49:20
    想要理解索引原理必须清楚一种数据结构「平衡树」(非二叉),也就是b tree或者 b+ tree,重要的事情说三遍:“平衡树,平衡树,平衡树”。当然, 有的数据库也使用哈希桶作用索引的数据结构 , 然而, 主流的RDBMS都...
  • 数据库索引原理,索引的实现,索引的使用注意事项
  • 深入了解数据库索引原理

    千次阅读 2018-09-27 16:52:24
      前段时间,公司一个新上线的网站出现页面响应速度缓慢的问题, 一位负责这个项目...我第一反应觉的是数据库上的问题,假装思索了一下,摆着一副深沉炫酷的模样说:“是不是数据库查询上出问题了, 给表加上索引...
  • 此文是我之前的笔记整理而来,以索引为入口进行探讨相关数据库知识(又做了修改以让人更好消化)。SQL Server接触不久的朋友可以只看以下蓝色字体字,简单有用节省时间;如果是数据库基础不错的朋友,可以全看,欢迎...
  • 1.首先简单说下mysql的两大...你去你的mysql安装目录下看两个数据库的文件就会发现,MyiSam的文件比InnoDB的多出了一个文件:MYI (存索引的文件) 为什么这样做呢 ?当然是适应不同业务场景,通常InnoDB适用于大多
  • 数据库索引原理解析

    千次阅读 2018-11-28 09:43:06
    数据库索引的理解什么是全表扫描?什么是索引?索引是怎么提升性能的?数据库索引里究竟存的是什么?索引存储了指向表中某一行的指针数据库怎么知道什么时候使用索引? 什么是全表扫描? 了解索引之前,我们先搞懂...
  • 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种...
  • 数据库索引原理是B+树进行存储,适应数据库底层存储数据的原理。 参考链接:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
  • 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。  在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以...
  • 数据库索引原理理解

    千次阅读 2010-06-09 14:22:00
    以前对数据库的理解总是停留在使用的阶段,没有去研究过深层次的东西,这两天正好有空(其实也是工作需要),看了一下数据库索引的一些基础的东西,希望通过这篇博文,整理一下自己的思路。 1.什么是索引? 我想...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 182,815
精华内容 73,126
关键字:

数据库索引原理