當前位置:成語大全網 - 英語詞典 - 優化了破網站的搜索功能

優化了破網站的搜索功能

大家好,我是魚皮,今天搞壹場技術實戰,需求分析 => 技術選型 => 設計實現,從 0 到 1,帶大家優化網站搜索的靈活性。

本文大綱:

魚皮 - 網站搜索優化

我開發的 編程導航網站 已經上線 6 個月了,但是從上線之初,網站壹直存在壹個很嚴重的問題,就是搜索功能並不好用。

此前,為了追求快速上線,搜索功能就簡單地使用了數據庫模糊查詢(包含)來實現,開發是方便了,但這種方式很不靈活。

舉個例子,網站上有個資源叫 “Java 設計模式”,而用戶搜索 “Java設計模式” 就啥都搜不出來,原因是資源名中包含了空格,而用戶搜索時輸入的關鍵詞並不包含空格。

空格只是壹種特例,類似的情況還有很多,比如網站上有個資源叫 “Java 並發編程實戰”,但用戶搜索 “Java 實戰” 時,明明前者包含 “Java” 和 “實戰” 這兩個詞,但卻是什麽都搜不出來的。

要知道,搜索功能對於壹個信息聚合類站點是至關重要的,直接影響用戶的體驗。在妳的網站上搜不到資源,誰還會用?

所以我也收到了壹些小夥伴的禮貌建議,比如這位禿頭 Tom:

之前沒有優化搜索,主要是兩個原因:窮 + 怕麻煩。但隨著網站用戶量的增大,是時候填坑了!

想要提高網站搜索靈活性,可以使用 全文搜索 技術,在前端和後端都可以實現。

有時,我們要檢索的數據是有限的,且所有數據都是 存儲在客戶端 的。

比如個人博客網站,我們通常會把每篇文章作為壹個文件存放在某目錄下,而不是存在後臺數據庫中,這種情況下,不需要再從服務器上去請求動態數據,那麽可以直接在前端搜索數據。

有壹些現成的搜索庫,比如 Lunr.js (GitHub 7k+ star),先添加要檢索的內容:

然後搜索就可以了:

純前端全文搜索的好處是無需後端、簡單方便,可以節省服務器的壓力;無需連網,也沒有額外的網絡開銷,檢索更快速。

區別於前端,後端全文搜索在服務器上完成,從遠程數據庫中搜索符合要求的數據,再直接返回給前端。

目前主流的後端全文搜索技術是 Elasticsearch,壹個分布式、RESTful 風格的搜索和數據分析引擎。

它的功能強大且靈活,但是需要自己搭建、定義數據、管理詞典、上傳和維護數據等,可操作性很強,需要壹些水平,新手和大佬設計出的 ES 搜索系統那是天差地別。

所以,對於不熟悉 Elasticsearch 的同學,也可以直接使用現成的全文檢索服務。比如 Algolia,直接通過它提供的 API 上傳需要檢索的數據,再用它提供的 API 檢索就行了。它提供了壹定的免費空間,對於小型網站和學習使用完全足夠了。

Algolia 檢索服務

那麽我的編程導航網站選擇哪種實現方式呢?

首先,該網站的資源數是不固定的、無規律動態更新的,因此不適合前端全文檢索。

其次,考慮到日後網站的數據量會比較大,而且可能要根據用戶的搜索動態地去優化檢索系統(比如自定義編程詞典),因此考慮使用 Elasticsearch 技術 自行搭建搜索引擎,而不用現成的全文檢索服務,這樣今後自己想怎麽定制系統都可以。此外,不用向其他平臺發送網站數據,能保證數據的安全。

確定使用 Elasticsearch 後,要先搭建環境。

可以自己購買服務器,再按照官方文檔壹步步手動安裝。對於有壹定規模的個人網站來說,雖然搭建過程不難,但後期的維護成本卻是巨大的,比如性能分析、監控、告警、安全等等,都需要自己來配置。尤其是後期網站數據量更大了,還要考慮搭建集群、水平擴容等等。

因此,我選擇直接使用雲服務商提供的 Elasticsearch 服務,這裏選擇騰訊雲,自動為妳搭建了現成的 ES 集群服務,還提供了可視化架構管理、集群監控、日誌、高級插件、智能巡檢等功能。

雲 ES 集群架構圖

雖然 ES 服務的價格貴,但節省下大量時間成本,對我來說是值得的。

我們的目標是優化網站資源的搜索功能,但接下來要做的不是直接編寫具體的業務邏輯,而是先開發壹個 公***的 ES 服務

其實對 ES 的操作比較簡單,可以先簡單地把它理解為壹個數據庫,那麽公***的 ES 服務應具有基本的增刪改查功能,供其他函數調用。

由於編程導航的後端使用的是騰訊雲開發技術,用 Node.js 來編寫服務,所以選用官方推薦的 @elastic/elasticsearch 庫來操作 ES。

代碼很簡單,先是建立和 ES 的連接,此處為了保證數據安全,使用內網地址:

然後是編寫增刪改查。這裏做壹步 抽象 ,通過 switch 等分支語句,根據請求參數來區分操作、要操作的數據等,這樣就不用把每個操作都獨立寫成壹個接口了。

具體的增刪改查代碼就不贅述了,對著 ES Node 的官方文檔看壹遍就行了,後面會把代碼開源到編程導航倉庫中(/liyupi/code-nav)。

編寫好代碼後,可以用雲開發自帶的 tcb 命令行工具在本地執行該函數。

記得先把 ES 的連接地址改成公網,然後輸入壹行命令就行了。比如我們要向 ES 插入壹條數據,傳入要執行的函數名、請求參數、代碼路徑:

執行成功後,就能在 ES 中看到新插入的數據了(通過 Kibana 面板或 curl 查看):

本地測試好公***服務代碼後,把 ES 連接地址改成內網 IP,然後發布到雲端。

接下來試著編寫壹個其他的函數來訪問公*** ES 服務,比如插入資源到 ES,通過 callFunction 請求:

但是,數據並沒有被成功插入,而是返回了接口超時,Why?

通過日誌得知是 ES 連接不上,會不會是因為發布上線的 ES 公***服務所在的機器和 ES 不在同壹個內網呢?

所以需要在雲開發控制臺更改 ES 公***服務的私有網絡配置,選擇和購買 ES 時同樣的子網就行了:

配置 ES 雲函數私有網絡

修改之後,再次遠程請求 ES 公***服務,數據就插入成功了~

開發好 ES 公***服務後,就可以編寫具體的業務邏輯了。

首先要在 ES 中建立壹個索引(類似數據庫的表),來約定數據的類型、分詞等信息,而不是允許隨意插入數據。

比如為了更靈活搜索,資源名應該指定為 "text" 類型,以開啟分詞,並指定 ik 中文分詞器:

最好還要為索引指定壹個別名,便於後續修改字段時重建索引:

編寫好建立索引的 json 配置後,通過 curl 或 Kibana 去調用 ES 新建索引接口就行了。

之前,編程導航網站的資源數據都是存在數據庫中的,用戶從數據庫中查詢。而現在要改為從 ES 中查詢,ES 空空如也可不行,得想辦法把數據庫中的資源數據同步到 ES 中。

這裏有幾種同步策略。

以前,用戶推薦的資源只會插入到數據庫,雙寫是指在資源插入數據庫的時候,同時插入到 ES 就好了。

聽上去挺簡單的,但這種方式存在壹些問題:

那有沒有對現有代碼 侵入更小 的方法呢?

如果對數據實時性的要求不高,可以選擇定時同步,每隔壹段時間將最新插入或修改的數據從數據庫復制到 ES 上。

實現方式有很多種,比如用 Logstash 數據傳輸管道,或者自己編寫定時任務程序,這樣就完全不用改現有的代碼。

如果對數據實時性要求很高,剛剛插入數據庫的數據就要能立刻就能被搜索到,那麽就要實時同步。除了雙寫外,還可以監聽數據庫的 binlog,在數據庫發生任何變更時,我們都能感知到。

阿裏有個開源項目叫 Canal ,能夠實時監聽 MySQL 數據庫,並推送通知給下遊,感興趣的朋友可以看看。

Canal 項目

由於編程資源的搜索對實時性要求不高,所以定時同步就 ok。

雲開發默認提供了定時函數功能,我就直接寫壹個雲函數,每 1 分鐘執行壹次,每次讀取數據庫中近 5 分鐘內發生了變更的數據,以防止上次執行失敗的情況。此外,還要配置超時時間,防止函數執行時間過長導致的執行失敗。

在雲開發 - 雲函數控制臺就能可視化配置了,需要為定時任務指定壹個 crontab 表達式:

配置雲函數定時和超時

開啟定時同步後,不要忘了再編寫並執行壹個 首次 同步函數,用於將 歷史 的全量數據同步到 ES。

現在 ES 上已經有數據了,只剩最後壹步,就是怎麽把數據搜出來呢?

首先我們要學習 ES 的搜索 DSL(語法),包括如何取列、搜索、過濾、分頁、排序等,對新手來講,還是有點麻煩的,尤其是查詢條件中布爾表達式的組合,稍微不註意就查不出數據。所以建議大家先在 Kibana 提供的調試工具中編寫查詢語法:

Kibana 調試

查出預期的數據後,再編寫後端的搜索函數,接受的請求參數最好和原接口保持壹致,減少改動。

可以根據前端傳來的請求動態拼接查詢語法,比如要按照資源名搜索:

由此,整個網站的搜索優化完畢。

再去試壹下效果,現在哪怕我輸入壹些多 “魚” 的詞,也能搜到了!

新 ES 搜索接口的發布並不意味著老的數據庫查詢接口淘汰,可以同時保留。按名稱搜索資源時用新接口,更靈活;而根據審核狀態、搜索某用戶發布過的資源時,可以用老接口,從數據庫查。從而分攤負載,職責分離,讓對的技術做對的事情!

我是魚皮,最後再送大家壹些 幫助我拿到大廠 offer 的學習資料

指路:https://t.1yb.co/qOJG

歡迎閱讀 我從 0 自學進入騰訊的編程學習、實習、求職、考證、寫書經歷,不再迷茫!

指路:https://t.1yb.co/w66s