企業級搜尋系統案例原始碼(已經非常精簡),根據現公司專案搭建,基於 elasticsearch + canal,可支援千萬量級以上實時搜尋。
一、準備工作:
-
IDEA: 略
-
JDK1.8: 略
-
mysql資料庫: 略
安裝完mysql資料庫後,把bin_log設定開啟,如果不確定是否已經開啟,可執行 show variables like ‘log_bin%’;
如果顯示如下,那說明已經開啟bin_log日誌,如果沒有,如果想使用canal進行資料同步,自行百度開啟bin_log
接下來,新建資料庫:db_search 並執行準備好的sql檔案 db_search.sql (專案原始碼中包含這個sql檔案,可自行下載)
執行完sql後,資料庫中資料應該是這樣的:
如果不需要進行欄位的分詞查詢,可不安裝分詞器,但是絕大多數業務場景,都是需要分詞查詢的,並且目前Demo中有分詞程式碼,建議安裝。
開啟連結,下載第一個壓縮包,然後解壓,解壓後應該是這個樣子的:
bin為啟動資料夾
conf為配置資料夾
lib為相關jar包資料夾
logs為日誌資料夾
前面我們已經安裝好Mysql資料庫,並且已經建好了一個測試資料庫,那麼我們接下來進行 Mysql + canal 服務端的配置
開啟canal的conf資料夾,在裡面新建 articlesubscibe 資料夾,再在 articlesubscibe 資料夾中,複製conf資料夾下example中的instance.properties 檔案並修改配置,如下圖(配置是我本機的配置,可根據實際需要改成自己的):
接下來,找到 canal.properties 配置檔案進行編輯,找到 canal.destinations 配置引數,進行資料夾掃描配置,如下圖:
資料夾名稱可自行定義,與上一步新建的資料夾名稱相同即可。然後將conf資料夾下自帶的 example 資料夾刪除,為什麼要刪除呢?因為實際開發中需要同步的資料庫、資料表是很多的,自帶的資料夾是個案例,用於第一次配置時copy,後面我們應該根據實際需要,新建多個資料夾,區分不同的業務監聽,便於後續維護。
至此,conf資料夾應該是這個樣子:
-
**git安裝(用於程式碼管理,主要是為下一步下載Demo做準備):**略
以上,就是專案的準備工作,接下來就著重講解專案結構,以及資料是如何同步、如何查詢的乾貨。
二、專案結構:
-
具體描述專案前,我經常想到大象裝冰箱的步驟:
-
開啟冰箱門、將大象放進去、關上冰箱門
-
現在做這個專案,也分為三個步驟:
-
同步實時資料(data-dump)、同步歷史資料(data-migration)、按條件查詢es資料(data-search)
-
同步實時資料:
當資料庫中 新增/刪除/更新 一條資料時,需要實時同步到es
注意!這裡進行實時同步時,需要避免監聽表的批量修改資料,不然會造成資料積壓,一時間同步不過來。
如果必須要批量修改Mysql表資料時,應先把canal服務端停掉 -
同步歷史資料:
當資料庫中已經有舊的資料,且資料量很多,es中沒有時,需要將歷史資料批量灌入到es
如果沒有歷史資料,需要同步到es,就不需要這一步了 -
按條件查詢es資料:
前兩步能夠保證es中的資料完整性,只能算前提條件,這一步是最出活的,做完這一步,能夠查詢出想要的,就算完成
利用git下載完專案後,就可以進行專案部署了,將專案匯入到IDEA中,可以看到es-home主專案,下圖是專案主要結構:
專案採用的相關技術:spring-boot、mybatis、maven、elasticsearch、canal
具體可檢視詳細腦圖:
##data-dump模組:
-
這個專案主要的功能是實時同步資料,其中包含了canal客戶端程式碼,用於接收canal服務端傳來的資料庫資料
-
首先啟動 DataDumpApplication.java ,可以看到啟動檔案會載入資料來源、掃描spring下的配置檔案、指定啟動順序(@Order註解)、執行run方法,建立es連結
-
在啟動完後,ElasticsearchEnvInitRunner.java檔案會接下來執行,因為實現了CommandLineRunner,並且有@Order(2)註解
這裡會進行索引檢查,如果es中沒有程式碼中的索引,會新建一個索引名稱
-
在新建索引過程中,可以對新建的索引進行設定,例如:設定欄位分詞、設定欄位經緯度,用於距離排序
-
接下來,CanalClientRunner.java檔案會繼續執行,因為實現了CommandLineRunner
這個檔案會和canal服務端建立連結
注:在使用SpringBoot構建專案時,我們通常有一些預先資料的載入。那麼SpringBoot提供了一個簡單的方式來實現–CommandLineRunner。
CommandLineRunner是一個介面,我們需要時,只需實現該介面就行。如果存在多個載入的資料,我們也可以使用@Order註解來排序。
-
當canal服務端接收到mysql傳過來的bin_log日誌,會通過和客戶端的連結,傳到客戶端 AbstractCanalCoreManager.java 類,客戶端可根據設定批量讀取傳過來的資料,取決於業務資料的變動速度,以及canal客戶端同步到es中的消費速度
-
消費過程中,由於真正企業中的資料量變動比較大,所以採用多執行緒的方式去消費資料
-
接下來,通過 entry.getEntryType() 可進行資料庫事物判斷,並分別對資料進行處理,再通過多執行緒依次處理這些資料
-
分別判斷每條資料是 增/刪/改 哪種型別,分別進行處理。當是刪除時,進行es刪除操作;當是 增/改 時,進行es增改操作
-
在向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小時,所以需要在查詢資料時,進行時間差的轉換
-
刪除操作,呼叫的是封裝的刪除方法
-
getDateMap()方法,是在對es進行操作的最後一步之前,可對特殊資料進行處理,根據實際的需求來,簡單的需求或者優質的表結構,不需要這一步,經過封裝好的es新增/更新方法,就可以到es了
-
新增/修改操作,呼叫的是封裝的方法
##data-migration模組:
-
這個專案主要的功能是同步歷史資料,當資料庫中已經有舊的資料,且資料量很多,es中沒有時,需要將歷史資料批量灌入到es。如果沒有歷史資料,需要同步到es,就不需要這一步了
-
這個專案和data-dump專案有相當一部分相同的程式碼,下面講到的話就簡單略過
-
首先啟動 DataMigrationApplication.java ,可以看到啟動檔案會掃描spring下的配置檔案
-
spring下的配置檔案被掃描,mybatis相關配置也會被載入,此處省略,這裡不是重點。。。
-
在啟動完後,ElasticSearchInitRunner.java檔案會接下來執行,因為實現了CommandLineRunner
這裡會進行es連線池檢查 -
在啟動完後,可以看到專案啟動埠為:8082
-
接下來在瀏覽器中請求url:http://localhost:8082/api/article/checkindex
可以進行索引檢查,如果沒有這個索引,會新建索引,這裡同上面data-dump一樣,索引可以設定分詞等屬性 -
請求介面經過網路中的層層傳遞,一直訪問到ArticleController.java
-
然後通過 articleSyncToEsManager.syncDataControl(); 同步資料到ES主控方法
這個方法,首先通過mybatis批量查詢出要同步錶的歷史資料,再進行迴圈,逐條處理資料
-
當看到 getDateMap()時,如果上面data-dump模組的結構看的夠仔細,那就能接上了,後面是完全一樣的資料處理,接下來的介紹省略~可以參考上面寫到的getDateMap()續讀
##data-search模組:
-
這個模組是進行資料的查詢,在做這一步前,請確保es中已經有相關資料,不然程式寫對了,也查不出來的,這時es應該可以看到初始資料如下圖:
-
首先啟動 DataSearchApplication.java ,可以看到啟動檔案會掃描spring下的配置檔案
-
在啟動完後,可以看到專案啟動埠為:8083
-
可以看到請求返回結果已經查詢到了結果
-
請求介面經過網路中的層層傳遞,一直訪問到ArticleController.java
-
再經過service實現層,進行es查詢,由於es查詢後返回結果被封裝成String,所以還需要轉成Object才方便處理資料,再返回
-
由於這個模組主要實現的是查詢功能,那麼具體的查詢需要仔細講一下了,serviceImpl進行查詢時,呼叫了 articleSearchManager.queryArticleList(queryArticleSearchVO);
-
進行查詢時,首先通過 elasticSearchInitClientManager.getElasticClient(); 獲取es連線
-
再設定索引、設定查詢文章型別、組裝查詢條件
-
查詢出來時,是es的資料型別 SearchResponse ,然後通過方法轉換成了Map
-
但是這裡有一點需要注意,存資料到es中時,時間型別是有轉換的,現在是查詢出來,es中儲存的經轉換的型別是字串,需要轉換成Data型別,並且可以留意下有沒有時差,之前提到過的時差如果出現了,這裡也可以進行資料修正
-
再經過最後資料的組裝,轉成通用型的JSON字串,返回
-
組裝邏輯應該根據實際情況封裝
暫時寫到這裡吧,專案整體就是這個樣子,實際使用中會比這個業務場景更復雜,這個Demo還是比較接近實際應用場景的,當然很多高階用法我沒有加在裡面,Demo複雜了就更難上手es了。
關於更多的查詢業務,可自行探索,我也在慢慢探索中,加油。
有什麼問題可以加QQ問,或者留言,看到就回。
作者:Happy王子樂
QQ:820155406
(近期迷上地下城,有富裕金幣的同學,給點金幣用,ID:血色冥靈,古靈精怪,跨六)