现在很多中间件系统的存储模块都会多少用到索引。由于网上大多对索引的介绍都是比较浅的逻辑视图描述,很少有真正从底层的索引存储以及其工作过程的详细描述的文章。所以本文重点在后者。重新梳理一遍,作为自己的知识总结,同时也分享给广大网友参阅。
如果一个没有索引或索引结构的文件,你要从中拿到你想要的数据内容,那么你不得不遍历整个文件。即从文件的起始位置地址开始依次顺序读取文件内容,直到目标内容出现为止。如下图所示。我们需要寻找“天”这个字附近的相关内容。相关代码,我将以伪代码展示。
伪代码如下:
File file = new File("C:\test.text");//打开文件
byte[] temp=new byte[10];//数据缓冲
while(file.read(temp)){//循环遍历整个文件
String content=new String(temp,"UTF-8");//将字节数据转换为字符串
if(content.contain("天")){//判断是否出现目标信息
//已经找到目标内容
}
}
显然这样的查找方式效率非常低,每次检索目标数据,都需要从头到尾遍历文件。现在我们常用的数据库系统,如果也是像上面那样查询目标数据,那么就没法玩下去了,肯定慢的要死。那么数据库系统又是如何做到快速检索数据的呢?答案显然是通过索引,这个大家都明白。可真正能理解索引底层是如何工作的,加快数据检索效率的过程恐怕不多。网上大部分资料基本都是简单的通过几个索引结构的图给我们展示原理。但是我这篇文章并不打算复制粘贴这些东西。而是要从磁盘上真实的数据分布结构上深入说明,让大家以后能有个真实直观理解索引的机制。
我们研发人员最常接触到的是关系数据库里的索引,比如B+tree的聚集索引和非聚集索引以及哈希索引等。不过我这里并不打算详细介绍数据库上的索引,因为本身的复杂度可能需要研究非常透彻才能说清楚。我这里只关注底层索引的一般工作原理,使用一个简单的示例简明扼要的叙述清楚就行。
如下图,假设有一张表格数据。模拟数据库系统进行阐述。表有两个列,其中主键是int类型,课程名称是vachar类型。
主键ID(id) |
课程名称(name) |
1 |
数学 |
2 |
计算机科学 |
3 |
图形学 |
4 |
英语 |
那么对于这样一张表格的数据,我们如何存储到磁盘中呢?如下图所示,我们可以这样设计底层的数据存储结构。红色部分表示实际的表对应的文件的数据磁盘存储结构,里面的内容代表实际存储的信息。比如有存储长度的,存储表格字段值的。背景深灰黑色代表磁盘的磁道。其他的都是为了理解知识而画的解释说明。比如每个信息单元占用的实际存储空间(红色上一层)。这里我们定义各个信息单元占用空间大小如下。
列长度:整数类型,2字节。
id列:整数类型,4字节。
name列:变长字符串类型,不固定字节。
表格中的数据最终能够存储到磁盘上,就是逻辑上紧密排列存储的。只是会多出一些额外的字节用来记录字段(ID和Name)的实际存储长度的行头部数据。当然这些数据实际存储在磁盘哪个位置是不确定的,完全是由操作系统决定的。不理解的同学可以看我的另外一篇文章《文件随机或顺序读写原理深入浅出》。
刚开始,我们没有使用索引结构存储,而是按照一行一行的方式排列存储的。所以目前的情况,如果我们要找出ID为3的行数据,我们需要顺序遍历整个文件才行。在此之前我们先解释下本文的一些概念词汇的含义。
地址/偏移地址/逻辑地址/位置。都是指文件数