用 Golang 写一个搜索引擎(0x09)— 数据增,删,改

根据某位和我同姓的朋友的建议,后面的文章都会加上副标题,方便查阅。算法

今天的文章会比较短,很快就能看完。数据库

按照步骤,说完段层之后,应该就开始涉及到索引层了,但我想说的是一个分布式的搜索引擎,因此除了索引层之外,还有个分片层,这两个概念是紧密联系在一块儿的,我怕说很差,因此在说索引层和分片层以前,咱们先直观的对索引有个了解,而且先熟悉一下索引层的一些特殊的数据结构以及一些经常使用算法,让你们对搜索引擎有个总体了解之后再来讲说索引层和分片层。数组

本篇只会涉及到数据的增,删,改,由于是一个大的话题,涉及的东西有点多,可能须要单独的一篇甚至两篇来讲,好了,不废话,咱们来讲说今天的主题吧。数据结构

以前咱们已经基本将搜索引擎的底层数据结构介绍完毕了,那么一个搜索引擎最基本的功能也和数据库同样,为了使搜索引擎跑起来,必需要有增,删,改,查四个基本功能,下来咱们一个一个来讲。分布式

在这以前,咱们还须要一些准备工做,那么仍是用一个例子引出全部内容,假如咱们有一份这样的微博数据,每一条数据就是一个微博用户发布的一条微博,每条数据有三个字段,分别是用户昵称,微博内容,发布时间,具体数据长成这样子:搜索引擎

{"nickname":"吴YH坚","content":"今天是个好日子","datetime":"2016-05-05 22:12:00"}3d

那么首先,咱们须要创建一个这样的索引结构cdn

字段名称 字段类型
nickname string(全词匹配) 倒排+正排
content string(分词模式匹配) 倒排+正排
datetime datetime(时间类型) 正排
_id 主键(自动生成型主键) 倒排+正排

好了,把上面这个表的内容提交到搜索引擎中,就创建好这个索引结构了,搜索引擎会按照咱们以前所写的,创建好各个字段,给每一个字段创建好倒排和正排索引

增这个其实就是全量索引构建增量索引构建了,咱们以前一直再说的就是这个,这里咱们就不详细说了。内存

不论是全量仍是增量,增长数据的时候就是将一条一条上文中的原始数据导入到搜索引擎中,每增长一条数据会先在内存中增长数据,而后达到必定条件之后将一批数据写入到段文件中,而且达到必定条件之后会将各个段合并起来。

惟一要说的是主键这个字段,因为在原始数据中是没有_id这个东西的,因此在增长数据的时候搜索引擎内部会给每条数据生成一个_id。

具体的_id的生成方法有不少,可使用随机数生成,也可使用GUID的方法进行生成,只要保证_id的惟一性就好了,因为最后咱们须要的是一个分布式的搜索引擎,因此_id的生成方法仍是有一些讲究的,通常简单的能够用随机数-时间戳-本机IP(MAC)地址来生成,这样产生相同的_id几率就已经几乎没有了,固然对于分布式系统还有其余办法生成惟一_id,咱们之后会介绍。

主键的倒排我没有保存在段中,而是直接使用了一个叫primary.pk的B+树文件进行保存,这个B+树的value就是主键对应的docid,由于主键是惟一的,因此只须要一个value值就好了,不须要倒排链。

若是数据中自带惟一id,那么更简单,直接使用就好了。

通常搜索引擎也提供无主键的数据检索,这种索引就只有增长和检索操做了,因为没有主键,通常不提供删除和修改操做。

删除操做是搜索引擎比较特殊的操做,由于咱们知道,保存到搜索引擎中的数据有倒排文件,想一想倒排文件的数据结构,要从中删除一个文档的话,须要先找出这个文档的内容,而后找到全部包含这个文档的倒排链,而后把每一个倒排链中的文档删除掉,再把倒排链重新排列,写入倒排文件中,加上读取词典数据,少说也有6,7次IO。

很麻烦是否是,咱们固然不会这么作,这里,咱们用到了一个新的数据结构,bitmap,分分钟解决问题。

bitmap是什么?bitmap是一种很是高效和经常使用的数据结构,它经过一个bit数组来存储特定数据的一种数据结构;因为bit是数据的最小单位,因此这种数据结构每每是很是节省存储空间,而一个bit要不是0,要不是1,因此bitmap只适合用来存储ture/false的这种数据(扩展之后能够存储更多信息),好比下面就是一个32位的bitmap,它能够用来表示0到31这些数中,哪些是有效的(用0表示),哪些是无效的(用1表示),在这里,0,4,14这三个数是无效的。这个数据结构很好理解吧。

0000 0000 0000 0000 0100 0000 0001 0001

而搜索引擎中,docid是连续的,而且docid是有效的仍是被删除了正好对应ture和false,因此很是适合使用bitmap来描述某个文档是否被删除。

bitmap的基本运算是位运算,因此速度也特别快,而且bitmap用一个bit来表示一个文档,因此存储空间也很是少,一个16M的bitmap能够存储134217728个文档是否被删除的信息。

因此,在搜索引擎中删除一个数据,咱们按照如下几步进行

  • 经过主键找到这个文档的docid
  • 直接将bitmap的第docid位设置为1
  • 结束

因此说是删除,实际并无删除,数据还在,在检索的时候作点小动做就能让人以为被删除了,什么小动做说检索的时候再说吧。

有了增和删,改的话就简单了,先删再增就是改了。

改的话还有个小技巧,为了尽可能的少增长数据量,当咱们修改的时候若是只修改了正排字段,而且修改先后正排字段的长度没有发生变化的话,咱们能够直接在原始的记录上覆盖,而不用删除再增长记录,固然,这么作的话,修改的时候须要比对全部字段来判断使用哪一种更新模式,不如先删后增来得直接,并且在分布式的环境下,这样不太利于同步数据。

说完了增,删,改,特别特别简单吧,但咱们发现搜索引擎若是一直不停的修改删除数据的话,而实际上并无真正的删除数据,数据会愈来愈多并不会减小,因此搜索引擎每隔一段时间都要重作一下全量索引,把无用的数据真正的清除掉,否则冗余的数据愈来愈多,会影响查询的效率。

后面一篇咱们再来详细说说,涉及到的算法部分稍微多一点,一篇可能说不完。


若是想看其余文章,欢迎关注个人公众号,主要聊聊搜索引擎,推荐广告算法以及其余瞎扯的东西 :)

相关文章
相关标签/搜索