最近各类技术盛会太多,朋友圈各类刷屏,有厂商发的各类广告,有讲师发的各类自拍,各类参会的朋友们各类自拍,好不热闹,不知道你的朋友圈是否是也是这样啊,last year还没这么多技术会议,this year 感受爆发了,呵呵,真是一个互联网技术的好时代,并且还有各类Sibi可看,咱们这种码农仍是专 注 技 术 专 注 写 代 码吧。git
你有什么想了解的也能够给我留言哈,欢迎交流,个人工做以前主要作的是搜索的,也作推荐和广告,这部分的东西可能写得多点,对了,嵌入式领域也行(跨得有点大,这个嵌入式不是iOS和Android,是真的嵌入式),没什么高端背景,也不是BAT这种大厂的,就是一小公司写代码的,因此有不少东西仍是不懂,你要是和我交流了发现我答不上来很正常啊,人艰不拆啊。。github
本篇也比较长,可是干货很少,建议上厕所的时候看,或者在地铁一边听歌一边看。算法
前面几篇,基本上把倒排索引的数据结构给讲完了,而且简单的说了一下排序,而后说了一下倒排索引的构建。这一篇主要写一下正排索引以及倒排和正排怎么配合起来造成一个完整的字段索引。数据库
正排索引,也叫前向索引,和倒排索引(也叫反向索引)是相对的,正排索引相对倒排来讲简单多了,第二篇文章的时候有下面两个表格(表1和表2)编程
这个是表1数组
文档编号 | 文档内容 |
---|---|
1 | 这是一个Go语言实现的搜索引擎 |
2 | PHP是世界上最好的语言 |
3 | Linux是C语言和汇编语言实现的 |
4 | XX是一个世界上最好的搜索引擎公司 |
这个是表2 | 关键词 | 文档编号 | | ---- | ----- | | Go | 1 | | 语言 | 1,2,3 | | 实现 | 1,3 | | 搜索引擎 | 1,4 | | PHP | 2 | | 世界 | 2,4 | | 最好 | 2,4 | | 汇编 | 3 | | 公司 | 4 |数据结构
咱们以前一直在说做为倒排索引的表2,对于表1,咱们认为是数据的详情(detail)信息,最后用来作数据内容展现的,若是是放在一个只支持全文搜索的搜索引擎中的话,那确实表1只是用来作最后的数据展现,可是若是咱们的搜索引擎还想要一些复杂的功能,那么表1就是一个正排索引,若是咱们的搜索引擎同时支持倒排索引和正排索引,咱们能够简单的认为这是一个数据库系统(固然,和真正的数据库还差得远啊)。分布式
很明显,若是倒排索引知足不了搜索要求的时候,就须要引入正排索引,好比一个电商的搜索引擎,那么正排索引就是必须的了,假如咱们有如下几个商品须要上架:优化
商品编号 | 商品 | 发布时间 | 价格 | 品牌 |
---|---|---|---|---|
10001 | cui子手机T9 | 2026-06-06 | 5000 | cui子 |
10002 | 小米手机10 | 2020-02-02 | 1999 | 小米 |
10003 | 华为手机P20 | 2022-12-12 | 3999 | 华为 |
搜索的时候咱们可能须要搜索价格在一个区间的手机,那么仅仅用全文倒排索引就比较难完成任务了,并且咱们在使用电商的搜索引擎的时候,常常会在搜索结果的上方看到一些汇总的信息【好比品牌,型号,价格汇总】,这一部分的东西也是经过正排索引来实现的,像下面这个图ui
因此说,若是咱们的搜索需求不只仅是进行关键词的匹配,还须要进行一些过滤操做(好比价格区间的过滤),汇总操做(好比结果集中每种品牌数量的统计),那么就必须引入正排索引了。
实现正排索引有两种方式:
一种仍是基于倒排索引,以前的倒排索引不是经过B+树构建的么,B+树自然的带排序功能,因此是能够进行范围查找的,好比上面那个表格,咱们要搜索的关键词为手机,价格区间在1500–4000之间。
这是第一种实现方式,汇总操做你们能够本身想一想怎么作,也能作,就是麻烦点。这种实现方式有下面几个特色
除了上面那个,还有一种实现方式,就是经过一个数组来实现,数组的下表就是文档编号(docid,不是商品编号,商品编号是主键),因为在搜索引擎中,docid是自增的,并且不会进行删除,因此也是惟一的,正好能够和一个一维数组的下标对上,因此能够用一个数组来存储正排索引,就像下面这个表格,分别表示价格和品牌创建的正排索引,其实就是把表1的数据拆开来进行存储了而已。(为了节省空间,我把两个写在一块儿了)
DOCID | 价格 | DOCID | 品牌 |
---|---|---|---|
0 | 5000 | 0 | cui子 |
1 | 1999 | 1 | 小米 |
2 | 3999 | 2 | 华为 |
这么存的话,检索的时候怎么作呢?若是仍是上面那个检索条件要搜索的关键词为手机,价格区间在1500–4000之间
若是是汇总操做的话,和上述相似,在第二步遍历结果集的时候顺便就能够进行统计了,遍历完了也就统计完了。
条条大路通罗马,经过两种不一样的数据结构,最后获得了同样的结果,第二方式有如下几个特色
上面就是正排索引的两种实现方式,使用哪种要看具体的业务需求,好比像百度这种全文搜索引擎,主要的需求其实就是查找关键字,不多用到过滤,汇总操做,那么不用单独来实现正排索引,用第一种方式就好了,而若是是电商类型的搜索引擎的话,有大量的过滤啊,汇总操做,那么经过第二种方式来实现正排索引仍是比较必要的。
个人代码里面就是用的第二种方式,而且实现的时候是用mmap的方式在磁盘上实现的,若是内存够大,能够全载入到内存提升检索速度。
正排索引和倒排索引终于都说完了,这要是搜索引擎最关键的数据结构了,其余全部的东西都是在这个基础上发展起来的,咱们已经有了正排和倒排索引的结构,那么若是来构建一个索引系统的,我是这么来作的。
首先,咱们须要定一个规矩,所谓规矩就是咱们的这个搜索引擎哪些操做我支持,哪些操做我不支持,好比,我为了简单,我就支持全文检索,其余都不支持,那么只须要好好的实现一个倒排索引结构,那数据结构部分就设计的差很少了。而我在作这个搜索引擎的时候,想实现的是下面这些个功能。
既然是这么来实现,那对于每一个字段,他可能的类型就是
字段类型 | 行为 | 备注举例 |
---|---|---|
完整匹配的字符串 | 创建倒排,正排(正排只展现,不进行过滤操做) | 主键,型号 |
关键词字符串 | 创建倒排,正排(正排只展现,不进行过滤操做) | 标题,描述 |
数字 | 只创建正排 | 价格,库存 |
日期 | 只创建正排 | 上架 |
仅展现 | 只创建正排(正排只展现,不进行过滤操做) | 商品详情描述 |
这样,咱们实现的时候,首先实现一个倒排索引(src/FalconIndex/segment/invert.go),而后实现一个正排索引(src/FalconIndex/segment/profile.go),而后实现一个字段类(src/FalconIndex/segment/field.go)用来管理倒排和正排,那么搜索引擎最最基本的数据结构就OK了,对外来讲倒排和正排是隐藏的,只有Field类对外暴露,对检索操做来讲主要提供几个接口方法:
文章中我尽可能少列或者不列代码,主要是对搜索引擎的原理有了解,原理了解了能够本身来实现代码,实在不会能够本身去看个人代码,毕竟编程这东西只要知道了原理和算法,怎么实现并非麻烦事。
以前我一直作C++开发的,写的搜索代码也是C++的,如今用Golang,也没啥特别的难度,固然由于我对Golang的特性并非很熟悉,因此基本没有用Golang的高级功能,写出来的代码固然不够Golang范,但这也不影响个人实现。
OK,字段部分介绍完了,搜索引擎的核心数据结构也介绍完了,后面接下来会继续往上走,先到段层,而后到索引层,而后会说一下检索逻辑实现,合并逻辑之类的,索引之上会继续说一下搜索引擎的引擎部分,后面还会遇到一些数据结构,好比bitmap,哦,还会单独写一到两篇来介绍分词,至于排序和索引结构优化也会单独拿出来讲。
另外,个人代码基本完成了,包括分布式的部分,会在最近提交到github上去,因此后面也会有几篇来讲搜索引擎的分布式实现,仍是本着原生的原则,没用第三方库,因此分布式部分没有PAXOS这种高端的理论,也没有ZooKeeper这种高端玩意,到时候你们看吧。
目前个人代码初步测 试,8G,24核的机器中,1000万条数据*(微博数据,每条不超过140个字,我不是微博的人哈,不存在数据泄密,数据是某号称亚二爬的博士爬来的,我只是下下来用而已)*,单个term的平均检索时间在5ms,用AB进行单个URL测 试,QPS大概在7000,若是是随机关键词测 试,QPS大约在2000,基本达到我以前本身定的目标了,并且还有优化空间。下次测测ElasticSearch,目前感受比它报出来的数据要快,可是环境不同,下次部一个比较一下,并且功能上还彻底达不到ElasticSearch的水平,不过它那一套要实现出来也是没什么问题的,须要的是坚持,我会把这个项目维护下去,不过最近实在是太忙了,苦逼啊。。。
最后,继续发个二维码,你懂的,关注一下呗:)