1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 6772字,需瀏覽 14分鐘

         ·

        2021-07-30 16:35

        使用 ES + 云開發(fā)實戰(zhàn)優(yōu)化網(wǎng)站搜索

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

        ES + 云開發(fā)搜索優(yōu)化實戰(zhàn)

        本文大綱:

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

        背景

        我開發(fā)的編程導(dǎo)航網(wǎng)站已經(jīng)上線 6 個月了,但是從上線之初,網(wǎng)站一直存在一個很嚴(yán)重的問題,就是搜索功能并不好用。

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

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

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

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

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

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

        技術(shù)選型

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

        前端全文搜索

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

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

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

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

        然后搜索就可以了:

        idx.search("魚皮")

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

        后端全文搜索

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

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

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

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

        Algolia 檢索服務(wù)

        選擇

        那么我的編程導(dǎo)航網(wǎng)站選擇哪種實現(xiàn)方式呢?

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

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

        ES 安裝

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

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

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

        云 ES 集群架構(gòu)圖

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

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

        ES 公共服務(wù)

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

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

        實現(xiàn)

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

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

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

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

        然后是編寫增刪改查。這里做一步 抽象,通過 switch 等分支語句,根據(jù)請求參數(shù)來區(qū)分操作、要操作的數(shù)據(jù)等,這樣就不用把每個操作都獨立寫成一個接口了。

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

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

        具體的增刪改查代碼就不贅述了,對著 ES Node 的官方文檔看一遍就行了,后面會把代碼開源到編程導(dǎo)航倉庫中(https://github.com/liyupi/code-nav)。

        本地調(diào)試

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

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

        tcb fn run 
          --name <functionName>
          --params "{\"op\": \"add\"}"
          --path <functionPath>

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

        遠(yuǎn)程測試

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

        接下來試著編寫一個其他的函數(shù)來訪問公共 ES 服務(wù),比如插入資源到 ES,通過 callFunction 請求:

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

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

        內(nèi)網(wǎng)配置

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

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

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

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

        數(shù)據(jù)索引

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

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

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

        "name": {
          "type""text",
          "analyzer""ik_max_word",
          "search_analyzer""ik_smart",
          "fields": {
            "keyword": {
              "type""keyword",
              "ignore_above"256
            }
          }
        }

        而點贊數(shù)應(yīng)設(shè)置為 "long" 類型,只允許傳入數(shù)字:

        "likeNum": {
          "type""long"
        }

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

        "aliases" : { 
          "resource": {}
        }

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

        數(shù)據(jù)同步

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

        這里有幾種同步策略。

        雙寫

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

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

        1. 會改動以前的代碼,每個寫數(shù)據(jù)庫的地方都要補充寫入 ES。

        2. 會存在一邊兒寫入失敗、另一邊兒成功的情況,導(dǎo)致數(shù)據(jù)庫和 ES 的數(shù)據(jù)不一致。

        那有沒有對現(xiàn)有代碼 侵入更小 的方法呢?

        定時同步

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

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

        實時同步

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

        阿里有個開源項目叫 Canal ,能夠?qū)崟r監(jiān)聽 MySQL 數(shù)據(jù)庫,并推送通知給下游,感興趣的朋友可以看看。

        Canal 項目

        實現(xiàn)

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

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

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

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

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

        數(shù)據(jù)檢索

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

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

        Kibana 調(diào)試

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

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

        // 傳了資源名
        if (name) {
          // 拼接查詢語句
          query.bool.should = [
            {
              match: {
                name
              }
            }
          ];
        }

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

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

        ES 是怎么實現(xiàn)靈活搜索的呢?歡迎閱讀 這篇文章 。

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



        以上就是本期分享,我是魚皮,點贊 + 在看 還是要求一下的,祝大家都能心想事成、發(fā)大財、行大運。

        往期推薦

        遠(yuǎn)程開發(fā),確實爽!

        5 分鐘!我上線了新網(wǎng)站!

        當(dāng)我搜索【牙簽】,驚了!

        我寫代碼的十八般兵器!

        在騰訊,我的試用期總結(jié)!

        瀏覽 35
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            男人操女人视频软件下载 | 91精品xxxx瑜伽裤日本 | 伊人网老司机 | 北条麻妃91人妻互换 | 大香蕉免费网 | 欧美伊人电影 | 黄色视频网站大全 | 手机天堂手机板 | 在线观看国产一级片 | 国产一级a一级a毛片视频黑人 |