搜尋可以說是咱們生活中非常常見的乙個功能了,基本上只要是個網際網路公司都離不開搜尋模組,但是實現它的方案卻有點麻煩。
眾所周知,咱們的資料都是從資料庫來的,因此一講到搜尋,我們就會想能不能用資料庫解決,然後只要是個正常人,大腦經過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...