【连载】关系型数据库是如何工作的?(5) - B+Tree索引

虽然上一章节介绍的二叉搜索树在查询指定值时表现很好,但是当查询两个值之间的多个节点时,就会遇到很大的问题。因为需要遍历整个树的节点,并检查每个节点是否在指定的区间内。而且遍历整颗树是随机磁盘IO(译者注:随机IO会导致频繁的磁头换道,所以相比顺序IO来说非常耗时),所以我们需要找到一种更有效做范围查询的方法。为了解决这个难题,现代数据库修正了之前介绍的二叉搜索树,我们称修正后的数据结构为B+Tree:

  • 只有叶子节点(树最底层的节点,图中橘黄色的节点)存储信息,即:行在表中精确的位置,也就是rowid;
  • 其他的节点仅仅是在搜索时用于路由到正确的节点。

就像上图中所示,有了更多的节点,实际上这些额外的节点就是决策节点,它会帮助我们找到正确的节点(实际存储行精确位置),但是它的搜索复杂度却依然是O(log(N)),和搜索二叉树最大的不同就是叶子节点都持有下一个节点的指针(译者注:可以看做一个有序的单向链表)。

使用B+Tree,如果我们需要查询40到100之间的值:

  • 你仅仅需要找到40节点,或者如果40节点不存在则找到大于40的最近节点;
  • 根据上一步找到的节点持有的指针,树藤摸瓜找到下一个节点,直到找到100节点截止。

译者注:上图其实也间接的解释了数据库是怎么构建B+Tree索引的,应该是自底向上的来构建。

假设实际需要在一棵有N个节点的树中范围查找到M个节点,那么其时间复杂度为M+log(N)。因为找到开始节点需要log(N),接着就可以根据指针按顺序找到M个节点,实际耗费M。M+log(N)与之前二叉搜索书的N相比,你不需要搜索整颗树,仅仅搜索M+log(N)个节点,这意味着更少的磁盘IO消耗;如果M很小(比如:200行),而N很大(比如:1 000 000行),那这两者的差距就非常巨大了。

可是我们又发现了一个问题,如果数据库使用B+Tree索引,而你删除一张表中的某一行,那么:

  • 你必须保持整棵树中节点的顺序,否则在乱序的树种你无法找到正确的节点。
  • 你必须保证树的高度尽可能低,否则无论单值查询还是范围查询的复杂度就会从O(log(N))无限的接近O(N)。(译者注:当树的高度就是节点的数量时,那么树的外形就像一条竖线,这时要找到一个节点实际上需要遍历整颗树

简而言之,B+Tree需要自排序和自平衡。虽然我们可以高效快速的删除和插入,但这是有代价的:在B+Tree数据库中代价是O(log(N))。这就是为什么你经常看到创建过多的索引不是一个好主意,这样会降低在表中插入/删除/更新行的速度。究其原因,是每一次插入/删除/更新,都会导致数据库更新(译者注:指自排序和自平衡)表的索引树,并且这种更新每个索引耗费是O(log(N))(译者注:上文指的代价)。而且,增加索引会导致事务管理器的负载增大(后续篇章会介绍到)。

关于B+Tree的更多细节,可以查看维基百科中的B+Tree说明。如果你想找B+Tree在数据库中的实现例子,可以查看来自MySQL核心开发者贡献的这两篇关于InnoDB如何处理索引的文章:The physical structure of InnoDB index pagesB+Tree index structures in InnoDB

时间: 2024-10-10 06:42:08

【连载】关系型数据库是如何工作的?(5) - B+Tree索引的相关文章

【连载】关系型数据库是如何工作的?(4) - 数组、二叉搜索树

在我们理解了隐藏在时间复杂度和排序后面的思想之后,我必须再谈谈3种数据结构了.它们极其重要,因为它们是现代数据库的基石.我也会顺便介绍下索引的概念. 数组 二维数组是最简单的数据结构,一张数据库表就可以看做一个二维数组,例如: 二维数组就是一个既有行又有列的表: 一行就表示一个主题(记录) 一列就是描述主题(记录)的一个特性 每一列存储同一个类型的数据(integer, string, date -) 虽然表能很好的存储并展示数据,但是当你需要搜索数据时,它的表现就很糟糕了. 例如,如果你要找到

【连载】关系型数据库是如何工作的?(9) - 查询管理器

查询管理器 查询管理是一个数据库强大与否的一个判断指标.通过查询管理,可以把一个糟糕的查询语句转换为一段快速执行的代码,代码执行后返回结果给客户端管理器.整个过程分为多步: 查询首先被解析并检查其有效性: 重写查询并删除不必要的操作,另外做一些预优化: 为了提升性能进行必要的优化,最终转换为一个执行计划: 编译执行计划: 最后运行执行计划. 在读了这一章节之后,如果对查询优化想更深入的理解,我推荐阅读以下资料: 1979年发表的一篇最早的关于基于成本优化的论文:Access Path Selec

【连载】关系型数据库是如何工作的?(6) - Hash表

最后我们介绍的重要数据结构就是Hash表.当你需要快速查找的时候非常有用,而且理解Hash表会有助于我们以后理解常用数据库Join方式之一Hash join.这种数据结构常被数据库用作存储内部数据结构:表锁或缓存池(后续章节会介绍). Hash表能够通过元素Key快速找到元素的,为了构建一张Hash表,你需要定义: 一个元素的Key: 一个关于Key的Hash函数,Key的hash值就代表元素所在的位置(我们通常称为Hash桶): 一个关于Key的比较函数,一旦你找到了正确的桶,你就可以通过比较

【连载】关系型数据库是如何工作的?(7) - 数据库架构视图

现在我们可以看看数据库内部都有什么组件.一个数据库就是容易访问和修改的信息集合,实际上,一组简单的文件就可以做到.最简单的数据库SQLite就是由一组简单文件组成的,并且是一组精心设计的一组文件,它允许你: 通过事务保证数据的安全性和一致性: 即时海量数据也能保证快速处理数据. 通常,一个数据的组件视图如下: 在写下这部分内容之前,我阅读了很多书籍和论文,每一个都有其特有的方式来描述数据库.因此就不要纠结我怎么组织数据库,或者我怎么命名这些组件,因为我已经为此考虑良久来适合这篇文章.不同的组件并

【连载】关系型数据库是如何工作的?(11) - 查询管理器之重写

在重写这一步,我们拿到了查询SQL的内部表示,重写的目的是: 预优化SQL 避免不必要的操作 帮助优化器找到尽可能好的解决方案 重写器会在查询上匹配一系列规则,如果匹配一个规则就应用它重写查询,以下是部分可选的规则: 视图合并:如果你在查询中用了视图,那么视图就会被转换为一段代码: 优化子查询:由于一个子查询非常难以优化,因此重写器会修改子查询并删除子查询. 例如: SELECT PERSON.* FROM PERSON WHERE PERSON.person_key IN (SELECT MA

4.非关系型数据库(Nosql)之mongodb:普通索引,唯一索引

 一:普通索引 1创建一个新的数据库 > use toto; switched to db toto > show dbs; admin (empty) local 0.078GB > use toto; switched to db toto > db toto > 2创建100万条数据 > for(var i=1; i <= 1000000; i++){ ...db.c3.insert({name:"zhangsan",age:i});

关系型数据库工作原理-事务管理(二)(翻译自Coding-Geek文章)

本文翻译自Coding-Geek文章:< How does a relational database work>. 原文链接:http://coding-geek.com/how-databases-work/#Buffer-Replacement_strategies 紧接上一篇文章,本文翻译了如下章节: 一. Log manager(日志管理) 通过前面的章节,我们已经知道,为了提升性能,数据库会将数据缓存在内存中.但是,如果在事务提交过程中,数据库服务器崩溃了.缓存在内存的数据就会丢失

深入理解关系型数据库(一)

前言:是否写了很多年的SQL查询,仍然不知道这个大盒子里怎么运作的? 如果你感兴趣,不妨读读本文. 每当说到关系型数据库时,我总感觉少点什么.各式各样的数据库被到处使用,从轻量的SQLite到强大的Teradata.但是,几乎没有一篇文章来解释这些关系型数据库到底是怎样工作的.你使用谷歌搜索“关系型数据库的运行原理”,基本上搜不到什么结果.现在,如果你有接触到比较流行的技术(Big Data, NoSQL或者JavaScript),你却可以找到一些比较深入的介绍它们原理的文章. 难道关系型的数据

nosql的介绍以及和关系型数据库的区别

一直对非关系型数据库和关系型数据库的了解感觉不太深入,在网上收集了一些关于sql和nosql的区别和优缺点分享给大家. Nosql介绍 Nosql的全称是Not Only Sql,这个概念早起就有人提出,在09年的时候比较火.Nosql指的是非关系型数据库,而我们常用的都是关系型数据库.就像我们常用的mysql,sqlserver一样,这些数据库一般用来存储重要信息,应对普通的业务是没有问题的.但是,随着互联网的高速发展,传统的关系型数据库在应付超大规模,超大流量以及高并发的时候力不从心.而就在