1. 程式人生 > >企業級搜尋系統案例原始碼(已經非常精簡),根據現公司專案搭建,基於 elasticsearch + canal,可支援千萬量級以上實時搜尋。

企業級搜尋系統案例原始碼(已經非常精簡),根據現公司專案搭建,基於 elasticsearch + canal,可支援千萬量級以上實時搜尋。

一、準備工作:

  • IDEA:

  • JDK1.8:

  • mysql資料庫:

安裝完mysql資料庫後,把bin_log設定開啟,如果不確定是否已經開啟,可執行 show variables like ‘log_bin%’;
如果顯示如下,那說明已經開啟bin_log日誌,如果沒有,如果想使用canal進行資料同步,自行百度開啟bin_log
這是個人資料庫截圖,用於演示bin_log是否開啟

接下來,新建資料庫:db_search 並執行準備好的sql檔案 db_search.sql (專案原始碼中包含這個sql檔案,可自行下載)
執行完sql後,資料庫中資料應該是這樣的:
資料庫資料

如果不需要進行欄位的分詞查詢,可不安裝分詞器,但是絕大多數業務場景,都是需要分詞查詢的,並且目前Demo中有分詞程式碼,建議安裝。

開啟連結,下載第一個壓縮包,然後解壓,解壓後應該是這個樣子的:
canal服務端資料夾
bin為啟動資料夾
conf為配置資料夾
lib為相關jar包資料夾
logs為日誌資料夾
前面我們已經安裝好Mysql資料庫,並且已經建好了一個測試資料庫,那麼我們接下來進行 Mysql + canal 服務端的配置
開啟canal的conf資料夾,在裡面新建 articlesubscibe 資料夾,再在 articlesubscibe 資料夾中,複製conf資料夾下example中的instance.properties 檔案並修改配置,如下圖(配置是我本機的配置,可根據實際需要改成自己的):
canal配置資料庫
接下來,找到 canal.properties 配置檔案進行編輯,找到 canal.destinations 配置引數,進行資料夾掃描配置,如下圖:
canal配置檔案圖解


資料夾名稱可自行定義,與上一步新建的資料夾名稱相同即可。然後將conf資料夾下自帶的 example 資料夾刪除,為什麼要刪除呢?因為實際開發中需要同步的資料庫、資料表是很多的,自帶的資料夾是個案例,用於第一次配置時copy,後面我們應該根據實際需要,新建多個資料夾,區分不同的業務監聽,便於後續維護。
至此,conf資料夾應該是這個樣子:
canal中conf資料夾

  • **git安裝(用於程式碼管理,主要是為下一步下載Demo做準備):**略

以上,就是專案的準備工作,接下來就著重講解專案結構,以及資料是如何同步、如何查詢的乾貨。

二、專案結構:

  • 具體描述專案前,我經常想到大象裝冰箱的步驟:

  • 開啟冰箱門、將大象放進去、關上冰箱門

  • 現在做這個專案,也分為三個步驟:

  • 同步實時資料(data-dump)、同步歷史資料(data-migration)、按條件查詢es資料(data-search)

  • 同步實時資料:
    當資料庫中 新增/刪除/更新 一條資料時,需要實時同步到es
    注意!這裡進行實時同步時,需要避免監聽表的批量修改資料,不然會造成資料積壓,一時間同步不過來。
    如果必須要批量修改Mysql表資料時,應先把canal服務端停掉

  • 同步歷史資料:
    當資料庫中已經有舊的資料,且資料量很多,es中沒有時,需要將歷史資料批量灌入到es
    如果沒有歷史資料,需要同步到es,就不需要這一步了

  • 按條件查詢es資料:
    前兩步能夠保證es中的資料完整性,只能算前提條件,這一步是最出活的,做完這一步,能夠查詢出想要的,就算完成

利用git下載完專案後,就可以進行專案部署了,將專案匯入到IDEA中,可以看到es-home主專案,下圖是專案主要結構:
es-home專案主結構

專案採用的相關技術:spring-boot、mybatis、maven、elasticsearch、canal

具體可檢視詳細腦圖:
es-home腦圖1
es-home腦圖2
es-home腦圖3

##data-dump模組:

  • 這個專案主要的功能是實時同步資料,其中包含了canal客戶端程式碼,用於接收canal服務端傳來的資料庫資料

  • 首先啟動 DataDumpApplication.java ,可以看到啟動檔案會載入資料來源、掃描spring下的配置檔案、指定啟動順序(@Order註解)、執行run方法,建立es連結
    DataDumpApplication.java檔案截圖

  • 在啟動完後,ElasticsearchEnvInitRunner.java檔案會接下來執行,因為實現了CommandLineRunner,並且有@Order(2)註解
    這裡會進行索引檢查,如果es中沒有程式碼中的索引,會新建一個索引名稱
    ElasticsearchEnvInitRunner.java檔案

  • 在新建索引過程中,可以對新建的索引進行設定,例如:設定欄位分詞、設定欄位經緯度,用於距離排序
    設定初始索引

  • 接下來,CanalClientRunner.java檔案會繼續執行,因為實現了CommandLineRunner
    這個檔案會和canal服務端建立連結
    CanalClientRunner.java檔案

注:在使用SpringBoot構建專案時,我們通常有一些預先資料的載入。那麼SpringBoot提供了一個簡單的方式來實現–CommandLineRunner。
CommandLineRunner是一個介面,我們需要時,只需實現該介面就行。如果存在多個載入的資料,我們也可以使用@Order註解來排序。

  • 當canal服務端接收到mysql傳過來的bin_log日誌,會通過和客戶端的連結,傳到客戶端 AbstractCanalCoreManager.java 類,客戶端可根據設定批量讀取傳過來的資料,取決於業務資料的變動速度,以及canal客戶端同步到es中的消費速度
    canal處理資料1

  • 消費過程中,由於真正企業中的資料量變動比較大,所以採用多執行緒的方式去消費資料
    canal處理資料2

  • 接下來,通過 entry.getEntryType() 可進行資料庫事物判斷,並分別對資料進行處理,再通過多執行緒依次處理這些資料
    canal處理資料3

  • 分別判斷每條資料是 增/刪/改 哪種型別,分別進行處理。當是刪除時,進行es刪除操作;當是 增/改 時,進行es增改操作
    canal處理資料4

  • 在向es中修改資料前,呼叫了analysisColumn(rowData.getBeforeColumnsList()); 方法,這是對資料庫特殊欄位的處理,因為es接收資料的型別和Mysql不完全一致,所以需要將特殊欄位處理下,目前已知的特殊欄位有:timestamp、datetime、date、time、blob 真的是萬惡的時間,費我青春,害我敲碼!
    處理資料庫特殊欄位

注:時間型別儲存到es中會自動有時區轉換,例如傳入時間是2018-08-21 18:00:00 es中會儲存成 2018-08-21 10:00:00 中間會差8小時,所以需要在查詢資料時,進行時間差的轉換

  • 刪除操作,呼叫的是封裝的刪除方法
    es刪除方法封裝

  • getDateMap()方法,是在對es進行操作的最後一步之前,可對特殊資料進行處理,根據實際的需求來,簡單的需求或者優質的表結構,不需要這一步,經過封裝好的es新增/更新方法,就可以到es了
    es特殊索引資料處理

  • 新增/修改操作,呼叫的是封裝的方法
    es新增/更新 方法封裝

##data-migration模組:

  • 這個專案主要的功能是同步歷史資料,當資料庫中已經有舊的資料,且資料量很多,es中沒有時,需要將歷史資料批量灌入到es。如果沒有歷史資料,需要同步到es,就不需要這一步了

  • 這個專案和data-dump專案有相當一部分相同的程式碼,下面講到的話就簡單略過

  • 首先啟動 DataMigrationApplication.java ,可以看到啟動檔案會掃描spring下的配置檔案
    DataMigrationApplication啟動

  • spring下的配置檔案被掃描,mybatis相關配置也會被載入,此處省略,這裡不是重點。。。

  • 在啟動完後,ElasticSearchInitRunner.java檔案會接下來執行,因為實現了CommandLineRunner
    這裡會進行es連線池檢查

  • 在啟動完後,可以看到專案啟動埠為:8082
    data-migration 模組啟動

  • 接下來在瀏覽器中請求url:http://localhost:8082/api/article/checkindex
    可以進行索引檢查,如果沒有這個索引,會新建索引,這裡同上面data-dump一樣,索引可以設定分詞等屬性

  • 請求介面經過網路中的層層傳遞,一直訪問到ArticleController.java
    進行url請求

  • 然後通過 articleSyncToEsManager.syncDataControl(); 同步資料到ES主控方法
    這個方法,首先通過mybatis批量查詢出要同步錶的歷史資料,再進行迴圈,逐條處理資料
    es主同步方法

  • 當看到 getDateMap()時,如果上面data-dump模組的結構看的夠仔細,那就能接上了,後面是完全一樣的資料處理,接下來的介紹省略~可以參考上面寫到的getDateMap()續讀

##data-search模組:

  • 這個模組是進行資料的查詢,在做這一步前,請確保es中已經有相關資料,不然程式寫對了,也查不出來的,這時es應該可以看到初始資料如下圖:
    es資料展示圖

  • 首先啟動 DataSearchApplication.java ,可以看到啟動檔案會掃描spring下的配置檔案
    DataSearchApplication啟動

  • 在啟動完後,可以看到專案啟動埠為:8083
    data-search模組啟動

  • 可以看到請求返回結果已經查詢到了結果

  • 請求介面經過網路中的層層傳遞,一直訪問到ArticleController.java
    ArticleController被請求

  • 再經過service實現層,進行es查詢,由於es查詢後返回結果被封裝成String,所以還需要轉成Object才方便處理資料,再返回
    service.impl進行es查詢

  • 由於這個模組主要實現的是查詢功能,那麼具體的查詢需要仔細講一下了,serviceImpl進行查詢時,呼叫了 articleSearchManager.queryArticleList(queryArticleSearchVO);

  • 進行查詢時,首先通過 elasticSearchInitClientManager.getElasticClient(); 獲取es連線

  • 再設定索引、設定查詢文章型別、組裝查詢條件

  • 查詢出來時,是es的資料型別 SearchResponse ,然後通過方法轉換成了Map

  • 但是這裡有一點需要注意,存資料到es中時,時間型別是有轉換的,現在是查詢出來,es中儲存的經轉換的型別是字串,需要轉換成Data型別,並且可以留意下有沒有時差,之前提到過的時差如果出現了,這裡也可以進行資料修正

  • 再經過最後資料的組裝,轉成通用型的JSON字串,返回
    es查詢邏輯1

  • 組裝邏輯應該根據實際情況封裝
    組裝查詢邏輯1
    組裝查詢邏輯2

暫時寫到這裡吧,專案整體就是這個樣子,實際使用中會比這個業務場景更復雜,這個Demo還是比較接近實際應用場景的,當然很多高階用法我沒有加在裡面,Demo複雜了就更難上手es了。

關於更多的查詢業務,可自行探索,我也在慢慢探索中,加油。

有什麼問題可以加QQ問,或者留言,看到就回。

作者:Happy王子樂
QQ:820155406
(近期迷上地下城,有富裕金幣的同學,給點金幣用,ID:血色冥靈,古靈精怪,跨六)