最近中文字幕2019高清,亚洲人成高清在线播放,男生淦哭男生图片动漫有字,国产亚洲精品九九久在线观看,无码av专区丝袜专区

網(wǎng)站搜索大優(yōu)化!

優(yōu)采云 發(fā)布時(shí)間: 2022-06-18 06:20

  網(wǎng)站搜索大優(yōu)化!

  使用 ES + 云開(kāi)發(fā)實(shí)戰優(yōu)化網(wǎng)站搜索

  大家好,我是魚(yú)皮,今天搞一場(chǎng)技術(shù)實(shí)戰,帶大家優(yōu)化網(wǎng)站搜索的靈活性。

  ES + 云開(kāi)發(fā)搜索優(yōu)化實(shí)戰

  本文大綱:

  

  魚(yú)皮 - 網(wǎng)站搜索優(yōu)化背景

  我開(kāi)發(fā)的編程導航網(wǎng)站已經(jīng)上線(xiàn) 6 個(gè)月了,但是從上線(xiàn)之初,網(wǎng)站一直存在一個(gè)很?chē)乐氐膯?wèn)題,就是搜索功能并不好用。

  此前,為了追求快速上線(xiàn),搜索功能就簡(jiǎn)單地使用了數據庫模糊查詢(xún)(包含)來(lái)實(shí)現,開(kāi)發(fā)是方便了,但這種方式很不靈活。

  舉個(gè)例子,網(wǎng)站上有個(gè)資源叫 “Java 設計模式”,而用戶(hù)搜索 “Java設計模式” 就啥都搜不出來(lái),原因是資源名中包含了空格,而用戶(hù)搜索時(shí)輸入的關(guān)鍵詞并不包含空格。

  空格只是一種特例,類(lèi)似的情況還有很多,比如網(wǎng)站上有個(gè)資源叫 “Java 并發(fā)編程實(shí)戰”,但用戶(hù)搜索 “Java 實(shí)戰” 時(shí),明明前者包含 “Java” 和 “實(shí)戰” 這兩個(gè)詞,但卻是什么都搜不出來(lái)的。

  要知道,搜索功能對于一個(gè)信息聚合類(lèi)站點(diǎn)是至關(guān)重要的,直接影響用戶(hù)的體驗。在你的網(wǎng)站上搜不到資源,誰(shuí)還會(huì )用?

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

  

  之前沒(méi)有優(yōu)化搜索,主要是兩個(gè)原因:窮 + 怕麻煩。但隨著(zhù)網(wǎng)站用戶(hù)量的增大,是時(shí)候填坑了!

  技術(shù)選型

  想要提高網(wǎng)站搜索靈活性,可以使用 全文搜索 技術(shù),在前端和后端都可以實(shí)現。

  前端全文搜索

  有時(shí),我們要檢索的數據是有限的,且所有數據都是 存儲在客戶(hù)端 的。

  比如個(gè)人博客網(wǎng)站,我們通常會(huì )把每篇文章作為一個(gè)文件存放在某目錄下,而不是存在后臺數據庫中,這種情況下,不需要再從服務(wù)器上去請求動(dòng)態(tài)數據,那么可以直接在前端搜索數據。

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

  var?idx?=?lunr(function?()?{<br />??this.field('title')<br />??this.field('body')<br />??//?內容<br />??this.add({<br />????"title":?"yupi",<br />????"body":?"wx搜程序員魚(yú)皮,閱讀我的原創(chuàng )文章",<br />????"id":?"1"<br />??})<br />})<br />

  然后搜索就可以了:

  idx.search("魚(yú)皮")<br />

  純前端全文搜索的好處是無(wú)需后端、簡(jiǎn)單方便,可以節省服務(wù)器的壓力;無(wú)需連網(wǎng),也沒(méi)有額外的網(wǎng)絡(luò )開(kāi)銷(xiāo),檢索更快速。

  后端全文搜索

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

  目前主流的后端全文搜索技術(shù)是 Elasticsearch,一個(gè)分布式、RESTful 風(fēng)格的搜索和數據分析引擎。

  

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

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

  

  Algolia 檢索服務(wù)選擇

  那么我的編程導航網(wǎng)站選擇哪種實(shí)現方式呢?

  首先,該網(wǎng)站的資源數是不固定的、無(wú)規律動(dòng)態(tài)更新的,因此不適合前端全文檢索。

  其次,考慮到日后網(wǎng)站的數據量會(huì )比較大,而且可能要根據用戶(hù)的搜索動(dòng)態(tài)地去優(yōu)化檢索系統(比如自定義編程詞典),因此考慮使用 Elasticsearch 技術(shù) 自行搭建搜索引擎,而不用現成的全文檢索服務(wù),這樣今后自己想怎么定制系統都可以。此外,不用向其他平臺發(fā)送網(wǎng)站數據,能保證數據的安全。

  

  ES 安裝

  確定使用 Elasticsearch 后,要先搭建環(huán)境。

  可以自己購買(mǎi)服務(wù)器,再按照官方文檔一步步手動(dòng)安裝。對于有一定規模的個(gè)人網(wǎng)站來(lái)說(shuō),雖然搭建過(guò)程不難,但后期的維護成本卻是巨大的,比如性能分析、監控、告警、安全等等,都需要自己來(lái)配置。尤其是后期網(wǎng)站數據量更大了,還要考慮搭建集群、水平擴容等等。

  因此,我選擇直接使用云服務(wù)商提供的 Elasticsearch 服務(wù),這里選擇騰訊云,自動(dòng)為你搭建了現成的 ES 集群服務(wù),還提供了可視化架構管理、集群監控、日志、高級插件、智能巡檢等功能。

  

  云 ES 集群架構圖

  雖然 ES 服務(wù)的價(jià)格貴,但節省下大量時(shí)間成本,對我來(lái)說(shuō)是值得的。

  還有個(gè)很方便的定制化搜索服務(wù) Elastic App Search,大家感興趣可以試試。

  ES 公共服務(wù)

  我們的目標是優(yōu)化網(wǎng)站資源的搜索功能,但接下來(lái)要做的不是直接編寫(xiě)具體的業(yè)務(wù)邏輯,而是先開(kāi)發(fā)一個(gè) 公共的 ES 服務(wù) 。

  其實(shí)對 ES 的操作比較簡(jiǎn)單,可以先簡(jiǎn)單地把它理解為一個(gè)數據庫,那么公共的 ES 服務(wù)應具有基本的增刪改查功能,供其他函數調用。

  實(shí)現

  由于編程導航的后端使用的是騰訊云開(kāi)發(fā)技術(shù),用 Node.js 來(lái)編寫(xiě)服務(wù),所以選用官方推薦的 @elastic/elasticsearch 庫來(lái)操作 ES。

  沒(méi)用過(guò)云開(kāi)發(fā)也沒(méi)事,可以先把它理解為一個(gè)后端,歡迎閱讀我之前的文章: 。

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

  const?client?=?new?Client({<br />?//?內網(wǎng)地址<br />??node:?'http://10.0.61.1:9200',<br />??//?用戶(hù)名和密碼<br />??auth:?{<br />????username:?esConfig.username,<br />????password:?esConfig.password,<br />??},<br />});<br />

  然后是編寫(xiě)增刪改查。這里做一步 抽象,通過(guò) switch 等分支語(yǔ)句,根據請求參數來(lái)區分操作、要操作的數據等,這樣就不用把每個(gè)操作都獨立寫(xiě)成一個(gè)接口了。

  //?接受請求參數<br />const?{?op,?index,?id,?params?}?=?event;<br />//?根據操作執行增刪改查<br />switch?(op)?{<br />??case?'add':<br />????return?doAdd(index,?id,?params);<br />??case?'delete':<br />????return?doDelete(index,?id);<br />??case?'search':<br />????return?doSearch(index,?params);<br />??case?'update':<br />????return?doUpdate(index,?id,?params);<br />}<br />

  在云開(kāi)發(fā)中,假如某個(gè)函數太久沒(méi)被調用,就會(huì )釋放資源。下次請求時(shí),會(huì )進(jìn)行冷啟動(dòng),重新創(chuàng )建資源,導致接口返回較慢。因此,把多個(gè)操作封裝到同一個(gè)函數中,也可以減少冷啟動(dòng)的幾率。

  具體的增刪改查代碼就不贅述了,對著(zhù) ES Node 的官方文檔看一遍就行了,后面會(huì )把代碼開(kāi)源到編程導航倉庫中()。

  本地調試

  編寫(xiě)好代碼后,可以用云開(kāi)發(fā)自帶的 tcb 命令行工具在本地執行該函數。

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

  tcb?fn?run?<br />??--name?<br />??--params?"{\"op\":?\"add\"}"<br />??--path?<br />

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

  遠程測試

  本地測試好公共服務(wù)代碼后,把 ES 連接地址改成內網(wǎng) IP,然后發(fā)布到云端。

  接下來(lái)試著(zhù)編寫(xiě)一個(gè)其他的函數來(lái)訪(fǎng)問(wèn)公共 ES 服務(wù),比如插入資源到 ES,通過(guò) callFunction 請求:

  //?添加資源到?ES<br />function?addData()?{<br />??//?請求公共服務(wù)<br />??app.callFunction({<br />????name:?'esService',<br />????data:?{<br />??????op:?'add',<br />??????index:?'resource',<br />??????id,<br />??????params:?data,<br />????}<br />??});<br />}<br />

  但是,數據并沒(méi)有被成功插入,而是返回了接口超時(shí),Why?

  內網(wǎng)配置

  通過(guò)日志得知是 ES 連接不上,會(huì )不會(huì )是因為發(fā)布上線(xiàn)的 ES 公共服務(wù)所在的機器和 ES 不在同一個(gè)內網(wǎng)呢?

  所以需要在云開(kāi)發(fā)控制臺更改 ES 公共服務(wù)的私有網(wǎng)絡(luò )配置,選擇和購買(mǎi) ES 時(shí)同樣的子網(wǎng)就行了:

  

  配置 ES 云函數私有網(wǎng)絡(luò )

  修改之后,再次遠程請求 ES 公共服務(wù),數據就插入成功了~

  數據索引

  開(kāi)發(fā)好 ES 公共服務(wù)后,就可以編寫(xiě)具體的業(yè)務(wù)邏輯了。

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

  比如為了更靈活搜索,資源名應該指定為 "text" 類(lèi)型,以開(kāi)啟分詞,并指定 ik 中文分詞器:

  "name":?{<br />??"type":?"text",<br />??"analyzer":?"ik_max_word",<br />??"search_analyzer":?"ik_smart",<br />??"fields":?{<br />????"keyword":?{<br />??????"type":?"keyword",<br />??????"ignore_above":?256<br />????}<br />??}<br />}<br />

  而點(diǎn)贊數應設置為 "long" 類(lèi)型,只允許傳入數字:

  "likeNum":?{<br />??"type":?"long"<br />}<br />

  最好還要為索引指定一個(gè)別名,便于后續修改字段時(shí)重建索引:

  "aliases"?:?{?<br />??"resource":?{}<br />}<br />

  編寫(xiě)好建立索引的 json 配置后,通過(guò) curl 或 Kibana 去調用 ES 新建索引接口就行了。

  數據同步

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

  這里有幾種同步策略。

  雙寫(xiě)

  以前,用戶(hù)推薦的資源只會(huì )插入到數據庫,雙寫(xiě)是指在資源插入數據庫的時(shí)候,同時(shí)插入到 ES 就好了。

  聽(tīng)上去挺簡(jiǎn)單的,但這種方式存在一些問(wèn)題:

  會(huì )改動(dòng)以前的代碼,每個(gè)寫(xiě)數據庫的地方都要補充寫(xiě)入 ES。

  會(huì )存在一邊兒寫(xiě)入失敗、另一邊兒成功的情況,導致數據庫和 ES 的數據不一致。

  

  那有沒(méi)有對現有代碼 侵入更小 的方法呢?

  定時(shí)同步

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

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

  實(shí)時(shí)同步

  如果對數據實(shí)時(shí)性要求很高,剛剛插入數據庫的數據就要能立刻就能被搜索到,那么就要實(shí)時(shí)同步。除了雙寫(xiě)外,還可以*敏*感*詞*數據庫的 binlog,在數據庫發(fā)生任何變更時(shí),我們都能感知到。

  阿里有個(gè)開(kāi)源項目叫 Canal ,能夠實(shí)時(shí)*敏*感*詞* MySQL 數據庫,并推送通知給下游,感興趣的朋友可以看看。

  

  Canal 項目實(shí)現

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

  云開(kāi)發(fā)默認提供了定時(shí)函數功能,我就直接寫(xiě)一個(gè)云函數,每 1 分鐘執行一次,每次讀取數據庫中近 5 分鐘內發(fā)生了變更的數據,以防止上次執行失敗的情況。此外,還要配置超時(shí)時(shí)間,防止函數執行時(shí)間過(guò)長(cháng)導致的執行失敗。

  在云開(kāi)發(fā) - 云函數控制臺就能可視化配置了,需要為定時(shí)任務(wù)指定一個(gè) crontab 表達式:

  

  配置云函數定時(shí)和超時(shí)

  開(kāi)啟定時(shí)同步后,不要忘了再編寫(xiě)并執行一個(gè) 首次 同步函數,用于將歷史的全量數據同步到 ES。

  數據檢索

  現在 ES 上已經(jīng)有數據了,只剩最后一步,就是怎么把數據搜出來(lái)呢?

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

  

  Kibana 調試

  查出預期的數據后,再編寫(xiě)后端的搜索函數,接受的請求參數最好和原接口保持一致,減少改動(dòng)。

  可以根據前端傳來(lái)的請求動(dòng)態(tài)拼接查詢(xún)語(yǔ)法,比如要按照資源名搜索:

  //?傳了資源名<br />if?(name)?{<br />??//?拼接查詢(xún)語(yǔ)句<br />??query.bool.should?=?[<br />????{<br />??????match:?{<br />????????name<br />??????}<br />????}<br />??];<br />}<br />

  由此,整個(gè)網(wǎng)站的搜索優(yōu)化完畢。

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

  

  ES 是怎么實(shí)現靈活搜索的呢?歡迎閱讀 。

  新 ES 搜索接口的發(fā)布并不意味著(zhù)老的數據庫查詢(xún)接口淘汰,可以同時(shí)保留。按名稱(chēng)搜索資源時(shí)用新接口,更靈活;而根據審核狀態(tài)、搜索某用戶(hù)發(fā)布過(guò)的資源時(shí),可以用老接口,從數據庫查。從而分攤負載,職責分離,讓對的技術(shù)做對的事情!

0 個(gè)評論

要回復文章請先登錄注冊


官方客服QQ群

微信人工客服

QQ人工客服


線(xiàn)

最近中文字幕2019高清,亚洲人成高清在线播放,男生淦哭男生图片动漫有字,国产亚洲精品九九久在线观看,无码av专区丝袜专区