FalconEngine - 一個 Go 語言實現的簡單搜索引擎
我的這個搜索引擎原始數據是MySql數據庫的,大家可以根據需要進行二次開發,用來支持其他數據庫或者本地文件,Detail文件是存儲在 Redis數據庫中,同樣這部分也可以根據自己的需要二次開發,使用本地文件或者其他數據庫,倒排索引和正排索引本地存儲的時候使用的json格式,比較耗磁盤,第一版暫時這樣了吧,后續再做優化。
性能測試
- 不好意思,還沒測呢,功能已經都測完了,性能還未優化,后續會測試,這是個持續的項目,會一直優化下去。
使用方法
依賴以下幾個庫
- github.com/outmana/log4jzl log文件
- github.com/ewangplay/config 配置文件解析
- github.com/go-sql-driver/mysql mysql驅動
- github.com/garyburd/redigo/redis Redis驅動
- github.com/huichen/sego 分詞器,作者主頁非常感謝他的分析器,他主頁上也有個搜索引擎,沒看具體實現,大家感興趣可以去看看。
- 直接運行install.sh
- 從 github.com/huichen/sego 獲取分詞的字典文件
- 運行索引器,會將索引文件生成到index目錄下
bin/FalconEngine -mode=build
- 運行搜索器
bin/FalconEngine -mode=search
基本概念
以下幾個概念需要理解,如果完全不了解的話,還需要自己稍微百度一下:倒排索引,正排文件,Detail文件,全量索引,增量索引,哈希函數,DocId
基礎數據結構
搜索引擎首先并不神秘,基礎的數據結構就那么幾個,定了以后后面就是在上面添磚加瓦了。
假如有下面五個文檔需要進行搜索
文檔編號 | 內容 |
---|---|
1 | 你好,搜索引擎 |
2 | 搜索引擎有一條數據 |
3 | 你好,有一條測試數據 |
倒排索引
倒排索引是搜索引擎基礎中的基礎,主要的檢索都是從倒排索引開始的,所以,首先,設計一個倒排索引的數據結構是所有搜索引擎的基礎。
搜索引擎的基礎是DocId,也就是文檔ID,DocId是唯一的并且是連續的,而倒排索引就是一組DocId鏈表,每個鏈表對應一個關鍵詞。
上面的文檔建立號倒排索引的基礎結構如下圖
關鍵詞 | 文檔編號 |
---|---|
你好 | 1,3 |
搜索引擎 | 1,2 |
數據 | 2,3 |
有一條 | 2,3 |
測試 | 3 |
所以,當我們檢索數據這個詞的時候能迅速知道在文檔 2 和 3 有這條數據,就能進行檢索了。
是不是很簡單,關鍵問題是檢索數據的時候,如何能迅速定位到第三行數據,這里就用到哈希表了,所以,一個完整的倒排包括兩部分,一部分是上面的這個表,第二個是一個哈希表,通過這個哈希表能知道數據這個詞的下標為3,從而找到2,3這兩個文檔。
哈希表的具體實現就不詳細展開了,哈希表有很多種實現方式,并且哈希函數也有很多種實現方式,總之,對于一個關鍵詞的定位
- 首先,通過計算這個關鍵詞的hash,得到它的下標
- 然后,查找倒排索引的下標,得到文檔ID的鏈表
在代碼的InvertIndex.go中是倒排索引的數據結構,StringIndexDic.go是關鍵詞的哈希表,這兩個文件產生的數據都會序列化成json文件存儲下來。
正排索引
正排索引相對倒排就簡單多了,實際上就是一個字典文件,key是DocId,value是這個DocId對應的內容,主要用來做結果集的過濾,所謂倒排檢索,正排過濾,什么場景需要這樣的東西呢?下面的場景你肯定經歷過。
你在一個某東的網站搜索運動鞋,肯定出來一堆鞋子,但是你只想看nike的鞋子,這時候你可以再運動鞋后面加上Nike,搜索nike運動鞋,但是結果不一定準,因為并不是每個nike的鞋子的標題上都會寫上nike,這時候就需要用到正排了,他會把nike鞋子給你過濾出來。
正排索引就是一個數組,數組的下標就是DocId,文件中的NumberProfile.go和TextProfile.go是具體的實現文件
Detail文件
Detail文件使用的是Redis實現的,沒有具體的數據結構,實際上就是以主鍵ID為key來實現的。
增量更新
增量更新使用的是掃描mysql中的一個last_modify_time字段,獲取數據,然后和redis中的數據進行對比,如果更新了就添加到索引中,添加索引按照如下的步驟進行
-
如果是正排字段更新,并且不是新增的數據,只是原來的數據修改
- 直接更新DocId對應下標的數據
-
如果是正排字段更新,但是是新增的數據
- 新增一個DocId并添加到正排文件的后面
-
如果是倒排字段更新
- 將原始的DocId從BitMap中刪除
- 新增一個DocId并添加到倒排文件的后面
因為DocId是連續的,倒排字段更新的話,要修改倒排鏈表,而目前的倒排鏈表是數組的,所以直接建立一個BitMap,將對應的DocId刪除,后續改成鏈表形式的話,可以動態的刪除。
增量更新使用的一個go的協程來做的,掃描的是數據庫字段,后續可以改成從kafka獲取數據或者其他方式獲取增量更新
數據檢索
數據檢索分成以下幾個步驟
- 根據關鍵詞從倒排索引中獲取DocId鏈,有多個關鍵詞的時候求交集
- 通過BitMap過濾掉已經刪除的DocId
- 最后得到的DocId按照正排文件的條件進行過濾操作,獲取最終的DocId鏈
- 通過DocId反查出文檔的真實ID,并通過Redis獲取文檔的詳細信息用于顯示
文件中的IndexSet.go主要實現了上述步驟