用Golang写一个搜索引擎(0x08)--- 索引的段

我以为这个标题应该改改了,我写下来实际上是告诉你们怎么写一个搜索引擎,并无涉及太多的Golang的东西,我以为这样也挺好,熟悉了原理,用什么实现其实并不重要了,并且说说原理比说代码更实在。前端

以前已经说了底层的数据结构了,包括倒排和正排索引。今天咱们上一层,来讲说索引的字段和段。数据库

字段这个上一篇已经介绍过了,字段的概念其实是搜索引擎索引中咱们能看到的最底层的东西,也是对外暴露的最底层的概念,在字段之下是倒排和正排索引,这两项其实对用户是封装起来了,咱们能够认为每一个字段对应一个正排和一个倒排,而实际上也确实是这样的。数组

在字段之上就是咱们这一篇主要说的了,段这个概念并非搜索引擎特有的,也不是必须的,是我这个项目新增出来的,固然,也不是我原创,不少搜索引擎的引擎系统都有这个概念。数据结构

所谓段,就是最基本的检索系统,一个包含全部字段,包含一部分连续的文档集合,可以进行完整的检索,能够把它当成一个检索系统最基本单位。分布式

这么说可能仍是有点抽象,咱们打个比方,在数据库中,一行数据是最基本的单位,对应搜索引擎中的是一个文档,而表是全部文档的集合,对应搜索引擎中是一份索引,而段就是一部分表,它包含一部分文档的内容,能够对这一部分文档进行检索,多个段合并起来就是一份完整的索引。搜索引擎

为何要有段这个概念呢?

  • 若是一个搜索引擎的数据再建好索引之后并不变化,那么彻底没有必要使用段,直接在创建全量索引的时候把数据都建好就好了spa

  • 若是有增量数据,而且增量数据是不断进入系统的话,那么段的概念就有必要了,新增的数据首先在内存中进行保存,而后周期性的生成一个段,持久化到磁盘中提供检索操做。日志

  • 段还有一个好处就是当系统是一个分布式的系统的时候,进行索引同步的时候,由于各个段持久化之后就不会变化了,只须要把段拷贝到各个机器,就能够提供检索服务了,不须要在各个机器上重建索引。code

  • 一个段损坏了,并不影响其余段的检索,只须要从其余机器上将这个段拷贝过来就能正常检索了,若是只有一个索引的话,一旦索引坏了,就没法提供检索服务了,须要等把正确索引拷贝过来才行。排序

一个段都存一些什么信息呢?

一个段包含几个文件

  • indexname_{segementNumber}.meta 这里是段的元信息,包括段中字段的名称,类型,也包括段的文档的起始和终止编号。

  • indexname_{segementNumber}.bt 这里是段的倒排索引的字典文件

  • indexname_{segementNumber}.idx 这里是段的全部字段的倒排文件

  • indexname_{segementNumber}.pfl 这里是段的全部数字正排文件的数据,同时也包含字符串类型数据的位置信息

  • indexname_{segementNumber}.dtl 这里是段的字符串类型数据的详情数据

上面的indexname是这个索引的名称,至关于数据库中的表名,segmentNumber是段编号,这个编号是系统生成的。

多个段合在一块儿就是一个完整的索引,检索的时候其实是每一个段单独检索,而后把数据合并起来就是最后的结果集了。

段的构建

下面咱们一个一个来讲说这些个文件,看看一堆正排和一堆倒排如何构成一个段的。

一个真正意义上的段的构建由如下几个步骤来构建,咱们以一个实际的例子来讲明一下段的构建,好比咱们如今索引结构是这样,这个索引包括三个字段,分别是姓名(字符串),年龄(数字),自我介绍(带分词的字符串),那么构建段和索引的时候步骤是这样的

1.前期准备

首先新建一个段须要先初始化一个段,在初始化段的时候咱们实际上已经知道这个段包含哪些字段,每一个字段的类型。

  • 初始化一个段信息,包含段所包含的字段信息和类型,在这里就是包含姓名(字符串【正排和倒排】),年龄(数字【正排】),自我介绍(带分词的字符串【正排和倒排】)。

  • 给段一个编号,好比1000。

  • 准备开始接收数据。

2.创建内存中的段

内存中的段是构建段的第一步,以上述的字段信息为例,咱们会在内存中创建如下几个数据结构,在这里我都是使用语言自动的原始数据结构

  • 姓名须要创建倒排索引,因此创建一个map<string,list>,key是姓名,value是docid,姓名也要创建正排索引,因此创建一个StringArray[],保存每条数据的姓名的详情。

  • 年龄须要创建正排索引,因此创建一个IntegerArray[],保存每条数据的年龄的详情。

  • 自我介绍须要创建倒排索引,因此创建一个map<string,list>,key是自我介绍的分词的term,value是docid,自我介绍也要创建正排索引,因此创建一个StringArray[],保存每条数据的自我介绍的详情。

当新增一条数据的时候{"name":"张三","age":18,"introduce":"我喜欢跑步"},首先咱们给他一个docid【假如是0】,而后咱们把数据分别存放到上面的5个数据结构中,若是再来一条数据{"name":"李四","age":28,"introduce":"我喜欢唱歌"},咱们给他一个docid【假如是1】,那么数据就变成了下图的样子

图片描述

3.将数据结构持久化到磁盘中

这样,随着数据的不停导入,内存中的数据结构不断变化,内存段的数据也愈来愈大,当达到必定阈值的时候(这部分策略之后会说,我把这部分策略放到了引擎层,由引擎来决定何时进行段的持久化),咱们将把数据持久化到磁盘中。

进行持久化的过程当中

  • 若是是map的数据结构,咱们将遍历整个map,首先将value追加写到.idx文件中,而后把key创建B+树,value是刚刚写入的idx文件的偏移位置。

  • 若是是IntegerArray,咱们遍历整个数组,而后把数据写入到pfl文件中,每一个数据占用8个字节。

  • 若是是StringArray,咱们遍历整个数据,首先把value追加写入到dtl文件中,而后把文件偏移量写入到pfl文件中

完成上面的三个步骤,咱们的持久化工做就完成了,完成之后数据结构就变成下面的样子了,你们能够本身脑子里实现一遍。

图片描述

4.段构建完成后

段构建完成后,这个段就算彻底持久化磁盘中了,不会再进行更改了,至关于提交到索引系统了,能够进行检索了。这时候,咱们再新建一个段,接着接收新的文档数据,而后继续把后续的段持久化到磁盘中。

当检索的时候,依次检索每一个段,而后将结果集合并起来返回给前端。

段的合并

段创建好了之后,可能须要对段进行合并操做,段的合并方式也不少,最简单的就是新建一个段,而后遍历以前的全部数据,重新创建一个段便可,这比较适合于数据量少的状况,由于新建一个段是在内存中的,若是以前的数据太多的话,内存会撑不住。

还有一种方式是分别将倒排,正排依次合并,这种方式不耗费内存,可是比较耗费磁盘的IO,两种方式你们能够根据本身的业务场景进行选择,第一种的方法和以前段的构建是同样的,这里咱们说说第二种方式。

合并倒排文件

咱们使用的B+树对倒排索引的字典文件进行存储,B+树自然带排序,那么合并段的时候实际上就是合并多个B+树,咱们只要使用归并排序的方式就能合并多个B+树了。归并排序不清楚的能够本身去查查,每一个B+树的Key就是待归并的元素,一边扫描B+树一边构建一个新的B+树,而后把倒排文件合并起来造成一个新的idx文件,倒排文件就合并完了。

合并正排文件

合并正排文件更加简单,只须要按照字段依次遍历每一个段的正排文件,而后一边遍历一边就造成了一个新的正排文件,遍历完正排文件也就合并完了。

合并的方法在FalconIndex/segment/segment.goMergeSegments中有详细代码,你们能够参考一下这种最简单的合并方式。

段的策略

段的策略比较自由,通常也不建议固化到索引中。通常有如下几种策略可供选择,具体须要根据本身的业务逻辑来选择一个合适的段的持久化策略。

  • 若是你的系统是一个一旦创建了索引就不怎么变化的系统,那么在作全量索引的时候创建一个段就好了,全量索引构建完了,而后把段持久化到磁盘就好了,若是全量索引量很大,怕内存扛不住,那么能够每10万条创建一个段,当全量索引完成了之后再将全部的段合并成一个段就好了,段的合并后面会说,合并段基本不占用什么内存,能够随时合并,若是有增量数据,每隔一段时间序列化一下段,而后再每隔一段时间将全部非全量数据的段合并一下,那么系统中就基本上只有一个全量的段和一个增量的段,检索起来仍是很是快的。

  • 若是你的系统是一个实时变化比较大的系统,好比日志系统,那么全量索引实际就没什么意义了,因为日志系统的检索其实实时性要求没有那么高,那么段的策略能够是每新增10万条数据持久化一个段,没到10个段将全部段合并成一个段。或者按照时间戳来合并段,方便剔除老的数据。

  • 若是你的系统是一个实时性要求很高的系统,那么能够按照时间(好比10秒)持久化一次段,每当系统空闲的时候将小的段合并成一个大的段。

总之,段的策略比较自由,彻底由引擎层来实现,根据本身的业务场景来选择重写一个段合并的策略都是能够的。

段是索引的一部分,也是一个微型的索引,下面一篇咱们将会介绍索引层了,索引层介绍玩之后搜索引擎的数据层就彻底结束了,上面就是各类引擎的策略了,有了索引层之后,其实对上你要变成一个搜索引擎仍是要变成一个数据库,或者变成一个KVDB的数据库都是能够的,反正基础的东西不会有太多变化。

好了,若是你想看以前的文章,能够关注个人公众号哈:)
图片描述

相关文章
相关标签/搜索