代码改变世界

B-树与Sqlite数据库

2013-03-23 11:36  onm  阅读(686)  评论(0编辑  收藏  举报
B-树和数据库还是春节前研究的,现在回想起来,很多细节都要耐心的细想。还好有若干索引,所以说索引真的是个好东西~ B-树是一种常用的索引数据结构,B-树不是二叉树,比二叉树要复杂一些。中文维基百科上是这样写的: “在计算机科学中,B树是,存储排序数据并允许以O(log n)的运行时间进行查找,顺序读取,插入和删除的数据结构。B树,概括来说是一个节点可以拥有多于2个子节点的二叉查找树。与自平衡二叉查找树不同,B-树为系统最优化大块数据的读和写操作。B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。” B-树有一个变形是B+树,数据库索引用到更多的是B+树。关于B-树和B+树的具体定义和查找,插入,删除等操作,可以参阅相关的书籍或者文章。查询是B-树的目的,插入,删除操作都是要保持B-树的性质。 之前花了一些时间,写了一个B-树的实现,网上没有找到什么好的实现,所以自己写了一个,也加深了对B-树的理解。实现的很丑陋,代码在:https://gist.github.com/5226953 数据库为什么要用到B-树呢? 我们都知道数据库查询是数据库使用的主要场景,查询效率是数据库性能指标的一个重要因素。查询的话,最基本的方法是循序遍历,当数据量很大的时候,这样效率当然很慢。再者是二分搜索,虽然时间复杂度变成O(logn),但是这样要求数据有序。另一方面,查询的条件可能很复杂,使用传统的方法效率会更加低效。使用索引可以加快检索的过程,索引就是把关键码与对应数据的位置相关联的过程。检索的过程,由直接检索数据,变成检索索引,然后定位数据,这样设计好索引就会大大加快检索的过程。 索引也有好多种,为什么数据库要用B-树及其变体B+树呢? 这个和磁盘的访问有关,我们知道,数据库存储的数据文件是在磁盘上的,而且数据量很大,一般相关的索引也会很大,所以索引也是存在磁盘上的。我们都知道访问磁盘比访问内存速度相差非常多,所以磁盘IO的性能就不能不考虑了。B-树正是满足了减少磁盘IO次数的需求。这里提到的磁盘是指传统的机械磁盘。 数据库设计存储索引的时候,会按页存储,读取索引的时候,会一次读取几页,由于磁盘也是在读取的时候会预读,每次读取都是按磁盘扇区大小来读取到内存中的。这样数据库读书数据的时候就会更有效。另外,B-树比其他树的有点在于树高会矮很多,因为B-树在每节点都可以有很多元素,这样随着树深的增加,下一层子节点的数量成指数级别增长。所以树高会较其它树小,这样在检索的时候,为了找到元素,从树根找到叶子节点就会减少很多访问外存的次数。另外B+树只在叶子节点存储实际键值,虽然增加了深度,但是增加了插入、删除的复杂度,切B+树由于所有键值都在叶子节点,所以对范围查询支持更好。 关于B+树在外存访存的分析上《MySQL索引背后的数据结构及算法原理》这篇文章写的很好,强烈推荐看看一看。 B-树具体到文件中是怎么存储的呢? 我们知道了,数据库索引使用的B-树,但是到底数据库是怎么实现的呢?为此,我研究了Sqlite数据库。这个号称最简单的数据库,在我看来也是极其的复杂的,我抽取了几部分作为研究。主要看了Sqlite的文件存储格式。 Sqlite是采用单文件存储的数据库(为了事务,异常恢复等会有多余的日志文件)。文件的格式设计官方有个文档:http://www.sqlite.org/fileformat.html。 这个文档详细说明了sqlite数据库文件的存储格式,包括文件头规定,Page页的设计(包括表内部页、表叶子页、索引内部页、索引叶子页),Page页的头规定,Cell单元Payload的设计等等。 国人有个分析的文档,写的很详细:http://vdisk.weibo.com/s/r1VK3,可以参阅。 这些东西展开说,都要费些时间,花些脑筋,就不写了。我模拟Sqlite的文件格式写了一个简化的数据库引擎的部分,之前写的,还没有完成,实际写起来还是很是费劲的,文件的读写操作就很麻烦,比操作内存要麻烦很多。 最后,再重新推荐一下这两篇文章: MySQL索引背后的数据结构及算法原理 SQLite文件格式分析 还有一本书: 数据库系统实现