如何實現分布式搜尋(一)實現簡單的分詞器

2021-10-02 13:12:19 字數 2725 閱讀 5709

搜尋可以說是咱們生活中非常常見的乙個功能了,基本上只要是個網際網路公司都離不開搜尋模組,但是實現它的方案卻有點麻煩。

眾所周知,咱們的資料都是從資料庫來的,因此一講到搜尋,我們就會想能不能用資料庫解決,然後只要是個正常人,大腦經過0.01秒的思考,就會拋棄資料庫~

原因有三:

1、資料庫的搜尋(也就是模糊查詢)不智慧型。

我想要搜尋 『北京的大學』 出來的結果絕對不會包含『北京大學』,更別說搜尋出北大了。

2、資料庫的搜尋太慢

咱們搜尋乙個資料,基本上就是全表掃瞄,對於資料庫來說,花個一天兩天也可能是正常的,基本上吃個飯睡個覺,也許你的搜尋頁面就有結果了呢?~

當然可能會有人說建立索引。但是啊,萬事萬物都是互通的,所謂的資料庫索引不過也要基於你的底層**來實現,對於字串的索引,是乙個詞乙個詞建立的,因此,當模糊查詢搜尋字串的時候,他能快速的從第乙個字元搜尋,也就是滿足『like  字串%』,而對於'like %字串%'的sql,它可不會走索引。

3、 資料庫要是被搜尋打崩了,獻祭哪個程式設計師祭天好呢?

估計很多人會直接想到使用solr或者elasticsearch,lucene等等第三方工具來解決這些問題,沒錯,這些都是主流技術手段,但不是我們剖析的重點。我想授人以魚不如授人以漁,明白了原理,學習別人的第三方工具也會非常簡單。

廢話少說,針對上述的三個問題,我們可以分析出如下的結果:

1、搜尋模組業務量挺大,需要單獨抽取出乙個模組,這叫做服務解耦。

2、為了加快搜尋速度,最好直接將要被搜尋的資料內容存在記憶體而非硬碟,並且提供持久化方案,宕機後也能讀取本地資料立馬啟動。

3、對於搜尋,我們需要對搜尋詞進行分詞和過濾,然後根據分詞的結果,去搜尋對應的資料。

上面的解決方案已經初步提出了搜尋模組的乙個雛形,現在我們需要考慮的就是如何實現這三個需求。

需求1比較抽象,但是實現起來並不難。

需求2內容太大,但是目前看來實現起來就是類似資料庫的資料匯入一樣,並不難(如果不包含建立索引等等的話~)

需求3則是這一節我要講的內容:實現分詞和過濾。

其實很多人對於分詞和過濾都是有個實現方案的,那也是我第一次接觸搜尋的時候想到的方案。

**實現如下:

順便吐槽一句切回eclipse敲**真的不順手,還是idea好用。。

public class participlebuilder 		

public listexecutor(string search)else}}

containwords.add(search);

return containwords; }

public static void main(string args)

}

那麼,對於乙個詞的查詢,有什麼辦法呢?

其實是有的,小學的時候買過的厚厚的中文詞典給了我們解決方案,眾所周知,詞典查詞的時候,就比如  博主很帥  ,我們先找到博字所在的頁碼,接著找到主zhu所在的頁碼,然後乙個字乙個字的找,就會發現,這個詞,壓根找不到。

好吧,扯遠了,但是博主真的很帥。

這裡又要給大家介紹乙個資料結構了,叫做trie樹。

參考:輕鬆看懂trie樹

看懂trie樹之後接下來就是**實現:

(該**需要對資料結構有一定掌握,不能手寫hashmap的人請跳過。)

從trie樹的樹狀圖我們可以用hashmap來實現,也可以自定義資料結構來實現,此處為了方便,用了hashmap。使用hashmap的缺點就是會造成大量的空間浪費,好處就是時間複雜度最小,最快。

其次就是需要注意,如何標誌這個詞已經結尾。

如果使用自定義資料型別,這個是很簡單的,定義乙個bool變數即可,但是我們使用hashmap,儲存型別是就不能這樣做了,我使用的方案是,對於詞中國,給『國』這個key對應的value值,也就是乙個map,新增乙個符號『/$』做key,(絕大部分情況下,這些符號不會參加分詞,也毫無意義)。

// 構建trie樹

private maptriekeywords = new hashmap();

private string keywordsarray = ;

初始化你的樹

// 開始構建乙個trie樹

for (string word : keywordsarray) else

// 對於最後乙個字,需要做額外判斷,標識他是乙個詞

if (l + 1 == word.length())

} }system.out.println(triekeywords);

開始分詞

// 分詞

public listexecutor(string search)

// 判斷是否有後續

string key = search.substring(i, i + 1);

if (triemap.get(key) != null) else

// 指標移動到下一位

triemap = triemap.get(key);

//如果到末尾了再判斷一次,防止最後乙個詞漏掉

if (i+1==search.length()&&triemap.get("/$") != null)

}} return result;

}

測試與結果

分布式鎖簡單實現

基於zookeeper的實現方式 基於redis的實現方式 基於資料庫實現分布式鎖 基於快取,實現分布式鎖,如redis 基於zookeeper實現分布式鎖 樂觀鎖機制其實就是在資料庫表中引入乙個版本號 version 欄位來實現的。當我們要從資料庫中讀取資料的時候,同時把這個version欄位也讀...

分布式鎖的簡單實現

分布式鎖在分布式應用當中是要經常用到的,主要是解決分布式資源訪問衝突的問題。一開始考慮採用reentrantlock來實現,但是實際上去實現的時候,是有問題的,reentrantlock的lock和unlock要求必須是在同一執行緒進行,而分布式應用中,lock和unlock是兩次不相關的請求,因此...

Redis分布式鎖簡單實現

偽 下訂單 1 查庫存 getstock 2 判斷庫存 stock 0下單 3 下單 addorder 4 減庫存 public class redisutils setnx param key param value param seconds 過期時間,單位秒 return public sta...