用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的數據庫都是能夠的,反正基礎的東西不會有太多變化。

好了,若是你想看以前的文章,能夠關注個人公衆號哈:)
圖片描述

相關文章
相關標籤/搜索