用Golang写一个搜索引擎(0x02)
这一篇,我们来说说搜索引擎最核心的技术, 倒排索引什么是倒排索引呢?索引我们都知道,就是为了能更快的找到文档的数据结构,比如给文档编个号,那么通过这个号就可以很快的找到某一篇文档,而倒排索引不是根据文档编号,而是通过文档中的某些个词而找到文档的索引结构。 倒排索引技术简单,高效,简直是为搜索引擎这种东西量身定做的,就是靠这个技术,实现一个搜索引擎才成为可能,我们也才能在海量的文章中通过一个关键词找到我们想要的内容。 我们看个例子,有下面的几个文档:
直观的看,我们通过编号 倒排索引【只列出了部分关键词】
这样就非常好理解了吧,实际上倒排索引就是把文档的内容切词以后重新生成了一个表格,通过这个表格,我们可以很快的找到每个关键词对应的文档,好了,没有了,到这里,就是倒排索引的核心原理,也是搜索引擎最基础的基石,不管是谷歌还是某度,最核心的东西就是这两个表格了,呵呵,没这两表格,啥都干不了。 看上去很简单吧,好吧,我们现在来模拟搜索引擎进行一次搜索,比如,我们键入关键词 上面就是搜索引擎的最基础的技术了,如果来设计一个数据结构和算法来实现表2就成了搜索引擎技术的关键。 在实现数据结构和算法之前,我们需要知道搜索引擎搜索的是海量的数据,一般的中型电商的数据都是几十上百G的数据了,所以这个数据结构应该是存储在本地磁盘的而不是在内存中的,基于以上的考虑,为了快速搜索,要么自己实现cache来缓存热数据,要么考虑使用操作系统的底层技术 MMAP系统调用
mmap最大的一个好处是操作系统会自己将磁盘上的文件映射到内存,当内存足够的时候,操作文件就像操作内存一样快,而当内存不足的时候,操作系统又会自己将一些页从内存中去掉,实现了一个类似缓存的东西。特别适合于对于巨大文件的读操作,而我们的倒排索引文件就是这种巨大的文件,而且基本上写入一次以后就不太修改了,每次查询都读操作,所以使用mmap是一个比较好的选择。 mmap是一个系统调用,不同的操作系统实现有所不同,Linux下对应的C的调用方法是下面这个,具体的参数含义大家可以man一下: 头文件 <sys/mman.h>
函数原型
void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
一个巨大的文件mmap之后,文件读写操作的性能由系统内存决定,系统可用内存越大,那么读写文件的性能越好,因为操作系统的内存足够,系统会将更多的文件载入到内存,提高系统吞吐量。 在Go语言中,对应的MMAP调用是:(需要引入
参数分别是:文件描述符,偏移量,需要映射的长度,期望的内存保护标志【是只读还是只写还是读写】,映射方式【是否同步到文件,还是只是副本修改等】。 因为mmap是基础实现,很多地方都需要使用,所以单独实现了一个mmap的类,在utils.mmap中,提供一些基础的方法: func NewMmap(file_name string,mode int) (*Mmap,error) 新建一个mmap
func (this *Mmap) ReadInt64(start int64) int64 //从指定位置读取一个int64的值
func (this *Mmap) WriteInt64(start,value int64) error //在指定位置写入一个int64的值
func (this *Mmap) ReadDocIdsArry(start,len uint64) []DocIdNode //从指定位置读取一个docid的链
......
巨大文件的读写技术方案解决了,实际上主要就是解决了表2的第二列的问题,在一个拥有巨大文档数的数据中,表2的第二列占用了绝大多数磁盘空间,我们会将表2分成两个数据结构来存储,第二列就是一个连续的存储文件,叫
而第一列我们将保存关键字和偏移量。这样,表2就被我们拆分成两个数据结构了,现在的关键是第一列使用什么数据结构可以保证在查询的时候迅速找到对应的关键字,从而找到偏移量得到第二列的具体数据。 好了,现在有几位选手要上场,他们都可以实现第一列的结构,他们分别是: 文章的更新频率会在一周3到5篇左右吧,欢迎大家扫描一下下面的微信公众号订阅,首先会在这里发出来:) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |