關於搜索引擎的實現機制分享

我相信很多的小夥伴都做過“搜索”的功能,可能“搜索引擎”不一定接觸過,但是站內搜索應該不會陌生。而不同的用戶體量、不同的階段,對於搜索的需求也是不一樣的,所以,搜索也是一個不斷演化的過程。

初級階段

在搜索需求的原始階段,一般我們就使用LIKE。可能我們有一個新聞的Table。

<code>CREATE TABLE `News` (
`Id` int(10) NOT NULL COMMENT '新聞ID',
`Title` VARCHAR(200) NOT NULL COMMENT '標題',
`Content` TEXT NOT NULL COMMENT '內容',
\tPRIMARY KEY (`Id`)
) /<code>

假如我們想要通過關鍵字對新聞的內容進行搜索時,我們會寫:

<code>SELECT * FROM News WHERE Content LIKE '%關鍵字%';/<code>

不過,這種“搜索”方式的優缺點也是非常明顯的。

優點就是:一句SQL就搞定,代碼簡單,維護方便,成本也非常低。

缺點也顯而易見:效率低,每次查詢都是全表檢索;計算量大,連續多查幾次CPU就100%了;而且也不支持分詞查找。

對於小型的系統,這種搜索方式自然沒有問題,但是規模稍大的系統,都不建議使用此種方式的。

中級階段

如果我需要提高我的查詢效率,並且我還要支持分詞,但有不想對我現有的系統改動過大呢?那可以考慮為你需要的字段進行全文索引。也就是使用:

<code>ALTER TABLE News ADD FULLTEXT ft_news(`Title`, `Content`);/<code>

如此一來,針對增加了全文索引的字段,你就不能夠再寫LIKE了,而需要改用

<code>SELECT * FROM News WHERE MATCH(`Title`, `Content`) AGAINST('關鍵字');/<code>

全文索引能夠快速的實現分詞搜索的需求,並且對現有的系統影響過大。同時,也能夠有效的提升性能,絕對比寫LIKE要高效得多(不會再進行全表掃描了)。當然,全文檢索也不是完美的,他的缺點也比較明顯:

  1. 在MySQL5.6.24之前的版本,全文檢索只支持MyISAM,之後的版本加入了InnoDB;
  2. 此全文檢索方式是使用的數據庫特性,搜索和其他的CURD相互耦合,當搜索的併發量大時,會影響到CRUD的性能;同樣,CRUD的併發量大時,搜索也會變慢;
  3. 性能容易出現瓶頸,數據量到達百萬級別時,速度就會難以接受了;
  4. 很難進行擴展

所以,我們的數據體量不大,業務量少時,這種全文檢索方式是可以的,但是當體量提高以後,就必須重新進行考慮了。

高級階段

經過了前兩個階段的修煉,單單依靠數據庫我們已經很難搞定了。到這裡,我們就需要考慮一下外置索引了。核心的思路自然是將原始數據與索引數據進行分離,原始數據用於保證普通的CRUD需求,索引數據用於滿足用戶的搜索需求。

而索引數據和原始數據之間通過雙寫,通知,定期重建等機制保證數據的一致性。

常見的外置索引方案有Solr、Lucene、ES,其中,ES(ElasticSearch)是目前最流行的一種方案。

ES是在Lucene的內核的基礎上搭建起來的索引服務,它解決了很多Lucene上面的不足,並且,ES將接口封裝成了一個個的RESTful的服務,更加的友好,應用上也更加簡單。ES能夠支持大數據量的數據存儲,併發性能也非常強,而且還支持集群。

可以說,ES已經能夠解決現在絕大多數科技公司或者互聯網公司的搜索需求了,是一個非常強大的解決方案。當然,ES也不是說沒有坑,這些坑就需要使用者根據自己的業務邏輯去一個一個的踩一踩了。

再往上,那就我現在暫時沒能觸碰的領域了,也就不多說了,但是呢,我們也可以聊一聊。

全網搜索引擎

這是一個粗略的搜索引擎架構圖:


關於搜索引擎的實現機制分享

在搜索引擎中,有三個核心的子系統,分別是爬蟲系統、索引系統和打分排序系統。

爬蟲系統我們這裡就不多說了,我們重點來說說索引系統和排序系統。

索引分成了所以的寫入和索引的查詢兩個部分。當爬蟲把網頁的數據抓取到以後,會經過簡單的處理,然後放到網頁倉庫中(這裡幾乎會存儲整個互聯網的所有html靜態頁面);然後索引系統就會對這些新來的網頁進行數據解析(分詞、正倒排索引等),然後生成並保存索引數據,也就是以下這些步驟。


關於搜索引擎的實現機制分享

當用戶要搜索時,索引檢索模塊會根據用戶輸入的內容進行分詞,然後根據分詞的內容查詢“倒排索引”,獲得匹配的頁面結果,然後將此結果交給打分排序的模塊,打分排序的模塊會根據規則對內容進行重新排序,然後再把結果交給檢索模塊並最終呈現給用戶。


關於搜索引擎的實現機制分享

當然,這只是一個粗略的架構原理,內部還有很多深層次的內容。不過,我們能夠從這個流程中看出,搜索引擎中的索引數據需要消耗大量的時間、大量的服務器資源進行數據的爬取、分析和索引的構建,所以現在很多想要做全網搜索引擎的公司,暫時都很難超過百度。

我們一般的程序員,99%應該都不會接觸到全網的搜索引擎,絕大部分的公司也僅僅是需要一個站內的搜索引擎,內站內的搜索引擎和全網的相比差異並不是很大,主要是在範圍上做出了限制,並且,由於數據都是自己的,所以也就不需要爬蟲系統了,可以通過網站靜態化並主動傳遞給網頁倉庫的方式來替代爬蟲。

關於搜索引擎的實現機制分享

倒排索引

上面我們提到了,用戶搜索的時候,會需要根據分詞內容去查詢“倒排索引”,什麼是倒排索引呢?

要說倒排索引,自然就要先說說正排索引。回到我們的初級階段,我們用最初的新聞Table來舉例。

<code>CREATE TABLE `News` (
`Id` int(10) NOT NULL COMMENT '新聞ID',
`Title` VARCHAR(200) NOT NULL COMMENT '標題',
`Content` TEXT NOT NULL COMMENT '內容',
\tPRIMARY KEY (`Id`)
)/<code>

假如,我們想要查找某個新聞,然後就使用新聞ID進行查找獲得新聞的標題和內容。為此,我們建立了新聞ID的索引,這種就是正排索引。

如果把這種方式擴展到我們的網頁查找中,那Url就相當於新聞ID,網頁的內容就相當於新聞內容,新聞的內容在分詞以後,就會形成很多很多的內容Item,正排索引也就是建立一個Url和List<item>之間的索引關係。/<item>

而倒排索引正好相反,就是在分詞以後,我們根據分詞進行url的歸類,最終形成一個Item和List之間的索引關係,而這種索引正式搜索引擎所需要的。

我們在分詞後,通過倒排索引查找,可以找到很多的集合,但是,我們最終呈現給用戶的只會有一個結果。例如:搜索“會技術的葛大爺”,分詞以後,“技術”查找到了一個集合,葛大爺查找到了一個集合,其中的排序順序各有不同,我們就需要將兩個集合合併成為一個集合並按順序排序呈現給用戶。

排序

最簡單的方式自然是循環 * 循環,但是效率可想而知。

我們可以用更簡單的方法,就是在倒排索引查找的時候,就將結果集進行排序,然後合併集合時,我們可以將集合數組想象成鏈表,比較鏈表中的值的大小,取出排序優先的值,然後移動遊標再次比較,這樣的話,每個元素比較的次數將會大量減少,兩兩之間最多比較一次。

關於搜索引擎的實現機制分享

當然,這種方式在數據量大時並不適用,沒有辦法做到並行處理。如果想要並行處理,我們可以先將結果集合按照範圍進行拆分,再進行比較。例如,我們按照1-10,10-20,20-30進行分組,然後在並行的進行排序,最終將結果合併,這樣效率就大大提高了。

關於搜索引擎的實現機制分享

關於搜索引擎的內容,我們也就差不多都這裡,搜索引擎中其實還有很多的內容,這都只有讓我們慢慢的去發覺。


分享到:


相關文章: