Elasticsearch 分布式搜索引擎 速学
一.?elasticsearch入門
1. Es的概述
????????elasticsearch是一款非常強大的開源搜索引擎,具備非常多強大功能,可以幫助我們從海量數據中快速找到需要的內容,它結合kibana、Logstash、Beats,也就是elastic stack(ELK)。它被廣泛應用在日志數據分析、實時監控等領域,而elasticsearch是elastic stack的核心,負責存儲、搜索、分析數據,底層是基于lucene來實現的,而Lucene是一個Java語言的搜索引擎類庫,是Apache公司的頂級項目,由DougCutting于1999年研發。參考官網?。
2. 倒排索引
????????文檔(Document):用來搜索的數據,其中的每一條數據就是一個文檔。例如一個網頁、一個商品信息
????????詞條(Term):對文檔數據或用戶搜索數據,利用某種算法分詞,得到的具備含義的詞語就是詞條。例如:我是中國人,就可以分為:我、是、中國人、中國、國人這樣的幾個詞條
? ? ? ? 將文檔中指定列的內容分成各個詞條,先找到用戶要搜索的詞條,根據詞條得到保護詞條的文檔的id,然后根據id獲取文檔。
3. Es和Mysql對比
| Table 表 | Index 索引 | 索引(index),就是文檔的集合,類似數據庫的表(table) |
| Row 一行數據 | Document 文檔 | 文檔(Document),就是一條條的數據,類似數據庫中的行(Row),文檔都是JSON格式 |
| Column 一列? ? ?? | Field 列字段 | 字段(Field),就是JSON文檔中的字段,類似數據庫中的列(Column) |
| Schema 對列字段類型約束 | Mapping 映射 | Mapping是索引中文檔的約束,例如字段類型約束。類似數據庫的表結構(Schema) |
| SQL 語法 (insert、select) | DSL 語法 (get、post、delete、update) | DSL是elasticsearch提供的JSON風格的請求語句,用來操作elasticsearch,實現CRUD |
4.?IK分詞器
? ? ? ? 4.1?安裝ik插件
# 進入容器內部 docker exec -it elasticsearch /bin/bash# 在線下載并安裝 ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip#退出 exit #重啟容器 docker restart elasticsearch? ? ? ? 4.2?啟動容器
# 重啟容器 docker restart es# 查看es日志 docker logs -f es? ? ? ? 4.3?IK分詞器包含兩種模式
-
ik_smart:最少切分
-
ik_max_word:最細切分
5.?擴展詞詞典
? ? ? ? 5.1?打開IK分詞器config目錄
? ? ? ? 5.2?在IKAnalyzer.cfg.xml配置文件內容添加
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties><comment>IK Analyzer 擴展配置</comment><!--用戶可以在這里配置自己的擴展字典 *** 添加擴展詞典--><entry key="ext_dict">ext.dic</entry> </properties>? ? ? ? 5.3 啟動容器
docker restart es# 查看 日志 docker logs -f elasticsearch二.?索引庫的CRUD
1.?創建索引庫和映射
PUT?/索引庫名稱 {"mappings":?{"properties":?{"字段名":{"type":?"text","analyzer":?"ik_smart"},"字段名2":{"type":?"keyword","index":?"false"},"字段名3":{"properties":?{"子字段":?{"type":?"keyword"}}}}} }2.?查詢索引庫
GET /索引庫名3.?修改索引庫
PUT?/索引庫名/_mapping {"properties":?{"新字段名":{"type":?"integer"}} }4.?刪除索引庫
DELETE /索引庫名三.?文檔操作
1.新增文檔
POST?/索引庫名/_doc/文檔id {"字段1":?"值1","字段2":?"值2","字段3":?{"子屬性1":?"值3","子屬性2":?"值4"},// ... }2.?查詢文檔
GET /{索引庫名稱}/_doc/{id}3.?刪除文檔
DELETE /{索引庫名}/_doc/id值4.?修改文檔
? ? ? ? 4.1 全量修改
PUT?/{索引庫名}/_doc/文檔id {"字段1":?"值1","字段2":?"值2",// ... 略 }? ? ? ? 4.2?增量修改
POST?/{索引庫名}/_update/文檔id {"doc": {"字段名":?"新的值",} }四.?RestAPI
ES官方提供了各種不同語言的客戶端,用來操作ES。這些客戶端的本質就是組裝DSL語句,通過http請求發送給ES官方文檔
其中的Java Rest Client又包括兩種:
-
Java Low Level Rest Client
-
Java High Level Rest Client
1.?Java HighLevel Rest Client客戶端AP
????????JavaRestClient操作elasticsearch的流程基本類似。核心是client.indices()方法來獲取索引庫的操作對象。
索引庫操作的基本步驟:
-
初始化RestHighLevelClient
-
創建XxxIndexRequest。XXX是Create、Get、Delete
-
準備DSL( Create時需要,其它是無參)
-
發送請求。調用RestHighLevelClient#indices().xxx()方法,xxx是create、exists、delete
2.?RestClient操作文檔
文檔操作的基本步驟:
-
初始化RestHighLevelClient
-
創建XxxRequest。XXX是Index、Get、Update、Delete、Bulk
-
準備參數(Index、Update、Bulk時需要)
-
發送請求。調用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk
-
解析結果(Get時需要)
五.?DSL查詢文檔
1.?DSL查詢分類
-
查詢所有:
????????查詢出所有數據,一般測試用。例如:match_all
-
全文檢索(full text)查詢:
????????利用分詞器對用戶輸入內容分詞,然后去倒排索引庫中匹配。例如: match_query????????multi_match_query
-
精確查詢:
????????根據精確詞條值查找數據,一般是查找keyword、數值、日期、boolean等類型字段。例如:ids????????range????????term
-
地理(geo)查詢:
????????根據經緯度查詢。例如:geo_distance????????geo_bounding_box
-
復合(compound)查詢:
????????復合查詢可以將上述各種查詢條件組合起來,合并查詢條件。例如:bool? ? function_score
2.?全文檢索查詢
????????常見的全文檢索查詢包括:match查詢:單字段查詢????????multi_match查詢:多字段查詢,任意一個字段符合條件就算符合查詢條件
? ? ? ? 2.1 match查詢語法如下:
GET?/indexName/_search {"query":?{"match":?{"FIELD":?"TEXT"}} }? ? ? ? 2.2 mulit_match語法如下:
GET?/indexName/_search {"query":?{"multi_match":?{"query":?"TEXT","fields":?["FIELD1",?" FIELD12"]}} }3. 精準查詢
????????精確查詢一般是查找keyword、數值、日期、boolean等類型字段。所以不會對搜索條件分詞。常見的有:term:根據詞條精確值查詢????????range:根據值的范圍查詢
????????3.1?term查詢
//?term查詢 GET?/indexName/_search {"query":?{"term":?{"FIELD":?{"value":?"VALUE"}}} }? ? ? ? 3.2?range查詢
//?range查詢 GET?/indexName/_search {"query":?{"range":?{"FIELD":?{"gte":?10, // 這里的gte代表大于等于,gt則代表大于"lte":?20 // lte代表小于等于,lt則代表小于}}} }4. 地理坐標查詢
? ? ? ? 4.1?矩形范圍查詢
//?geo_bounding_box查詢 GET?/indexName/_search {"query":?{"geo_bounding_box":?{"FIELD":?{"top_left":?{ // 左上點"lat":?31.1,"lon":?121.5},"bottom_right":?{ // 右下點"lat":?30.9,"lon":?121.7}}}} }? ? ? ? 4.2?附近查詢
//?geo_distance 查詢 GET?/indexName/_search {"query":?{"geo_distance":?{"distance":?"15km", // 半徑"FIELD":?"31.21,121.5" // 圓心}} }5.?復合查詢
????????復合(compound)查詢:復合查詢可以將其它簡單查詢組合起來,實現更復雜的搜索邏輯。常見的有兩種:fuction score:算分函數查詢,可以控制文檔相關性算分,控制文檔排名????????bool query:布爾查詢,利用邏輯關系組合多個其它的查詢,實現復雜搜索
? ? ? ? 5.1?相關性算分
[{"_score"?:?17.850193,"_source"?:?{"name"?:?"虹橋如家酒店真不錯",}},{"_score"?:?12.259849,"_source"?:?{"name"?:?"外灘如家酒店真不錯",}},{"_score"?:?11.91091,"_source"?:?{"name"?:?"迪士尼如家酒店真不錯",}} ]? ? ? ? 5.2?布爾查詢
GET?/hotel/_search {"query":?{"bool":?{"must":?[{"term":?{"city":?"上海"?}}],"should":?[{"term":?{"brand":?"皇冠假日"?}},{"term":?{"brand":?"華美達"?}}],"must_not":?[{?"range":?{?"price":?{?"lte":?500?}?}}],"filter":?[{?"range":?{"score":?{?"gte":?45?}?}}]}} }六.?搜索結果處理
1.?普通字段排序
????????keyword、數值、日期類型排序的語法基本一致。
GET?/indexName/_search {"query":?{"match_all":?{}},"sort":?[{"FIELD":?"desc"??//?排序字段、排序方式ASC、DESC}] }2.?地理坐標排序
????????地理坐標排序略有不同。
GET?/indexName/_search {"query":?{"match_all":?{}},"sort":?[{"_geo_distance"?:?{"FIELD"?:?"緯度,經度", // 文檔中geo_point類型的字段名、目標坐標點"order"?:?"asc", // 排序方式"unit"?:?"km" // 排序的距離單位}}] }3.?分頁
????????elasticsearch 默認情況下只返回top10的數據。而如果要查詢更多數據就需要修改分頁參數了。elasticsearch中通過修改from、size參數來控制要返回的分頁結果:from:從第幾個文檔開始????????size:總共查詢幾個文檔
? ? ? ? 3.1 基本的分頁
GET?/hotel/_search {"query":?{"match_all":?{}},"from":?0,?//?分頁開始的位置,默認為0"size":?10,?//?期望獲取的文檔總數"sort":?[{"price":?"asc"}] }????????3.2 深度分頁問題
GET?/hotel/_search {"query":?{"match_all":?{}},"from":?990,?//?分頁開始的位置,默認為0"size":?10,?//?期望獲取的文檔總數"sort":?[{"price":?"asc"}] }4.?高亮
GET?/hotel/_search {"query":?{"match":?{"FIELD":?"TEXT" // 查詢條件,高亮一定要使用全文檢索查詢}},"highlight":?{"fields":?{?//?指定要高亮的字段"FIELD":?{"pre_tags":?"<em>",??//?用來標記高亮字段的前置標簽"post_tags":?"</em>"?//?用來標記高亮字段的后置標簽}}} }七.?RestClient查詢文檔
????????基本步驟是:創建SearchRequest對象 --->準備Request.source(),也就是DSL(QueryBuilders來構建查詢條件、傳入Request.source() 的 query() 方法) --->發送請求,得到結果 --->解析結果(參考JSON結果,從外到內,逐層解析)
1.?match查詢
@Test void testMatch() throws IOException {// 1.準備RequestSearchRequest request = new SearchRequest("hotel");// 2.準備DSLrequest.source().query(QueryBuilders.matchQuery("all", "如家"));// 3.發送請求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析響應handleResponse(response);}2. 精確查詢
????????精確查詢主要是兩者:term:詞條精確匹配????????range:范圍查詢
3.?布爾查詢
@Test void testBool() throws IOException {// 1.準備RequestSearchRequest request = new SearchRequest("hotel");// 2.準備DSL// 2.1.準備BooleanQueryBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// 2.2.添加termboolQuery.must(QueryBuilders.termQuery("city", "杭州"));// 2.3.添加rangeboolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));request.source().query(boolQuery);// 3.發送請求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析響應handleResponse(response);}4.?排序、分頁
@Test void testPageAndSort() throws IOException {// 頁碼,每頁大小int page = 1, size = 5;// 1.準備RequestSearchRequest request = new SearchRequest("hotel");// 2.準備DSL// 2.1.queryrequest.source().query(QueryBuilders.matchAllQuery());// 2.2.排序 sortrequest.source().sort("price", SortOrder.ASC);// 2.3.分頁 from、sizerequest.source().from((page - 1) * size).size(5);// 3.發送請求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析響應handleResponse(response);}5.?高亮
????????高亮的代碼與之前代碼差異較大,有兩點:查詢的DSL:其中除了查詢條件,還需要添加高亮條件,同樣是與query同級。????????結果解析:結果除了要解析_source文檔數據,還要解析高亮結果
? ? ? ? 5.1?高亮請求構建
@Test void testHighlight() throws IOException {// 1.準備RequestSearchRequest request = new SearchRequest("hotel");// 2.準備DSL// 2.1.queryrequest.source().query(QueryBuilders.matchQuery("all", "如家"));// 2.2.高亮request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));// 3.發送請求SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 4.解析響應handleResponse(response); }八.?數據聚合
1.聚合的種類
? ? ? ? 1.1 聚合的常見種類
Bucket:對文檔數據分組,并統計每組數量
Metric:對文檔數據做計算,例如avg
Pipeline:基于其它聚合結果再做聚合
? ? ? ? 1.2 參與聚合的字段類型
keyword????????數值????????日期????????布爾
2. DSL實現聚合
? ? ? ? 2.1 聚合必須的三要素
聚合名稱????????聚合類型????????聚合字段
? ? ? ? 2.2 聚合可配置屬性
size:指定聚合結果數量
order:指定聚合結果排序方式
field:指定聚合字段
3. 自動補全
? ? ? ? 3.1 拼音分詞器
????????要實現根據字母做補全,就必須對文檔按照拼音分詞。在GitHub上恰好有elasticsearch的拼音分詞插件。地址
????????安裝方式與IK分詞器一樣,分三步:解壓--->上傳到虛擬機中,elasticsearch的plugin目錄--->重啟elasticsearch--->測試
? ? ? ? 3.2 自定義分詞器
????????elasticsearch中分詞器(analyzer)的組成包含三部分:character filters:在tokenizer之前對文本進行處理。例如刪除字符、替換字符????????tokenizer:將文本按照一定的規則切割成詞條(term)。例如keyword,就是不分詞;還有ik_smart????????tokenizer filter:將tokenizer輸出的詞條做進一步處理。例如大小寫轉換、同義詞處理、拼音處理等
//聲明自定義分詞器的語法 PUT?/test {"settings":?{"analysis":?{"analyzer":?{?//?自定義分詞器"my_analyzer":?{??//?分詞器名稱"tokenizer":?"ik_max_word","filter":?"py"}},"filter":?{?//?自定義tokenizer?filter"py":?{?//?過濾器名稱"type":?"pinyin",?//?過濾器類型,這里是pinyin"keep_full_pinyin":?false,"keep_joined_full_pinyin":?true,"keep_original":?true,"limit_first_letter_length":?16,"remove_duplicated_term":?true,"none_chinese_pinyin_tokenize":?false}}}},"mappings":?{"properties":?{"name":?{"type":?"text","analyzer":?"my_analyzer","search_analyzer":?"ik_smart"}}} }? ? ? ? 3.3 自動補全查詢
????????elasticsearch提供了Completion Suggester查詢來實現自動補全功能。這個查詢會匹配以用戶輸入內容開頭的詞條并返回。為了提高補全查詢的效率,對于文檔中字段的類型有一些約束:????????參與補全查詢的字段必須是completion類型。????????字段的內容一般是用來補全的多個詞條形成的數組。
//?創建索引庫 PUT?test {"mappings":?{"properties":?{"title":{"type":?"completion"}}} } //?創建索引庫 PUT?test {"mappings":?{"properties":?{"title":{"type":?"completion"}}} } // 示例數據,插入數據 POST?test/_doc {"title":?["Sony",?"WH-1000XM3"] } POST?test/_doc {"title":?["SK-II",?"PITERA"] } POST?test/_doc {"title":?["Nintendo",?"switch"] } //?自動補全查詢,查詢的DSL語句 GET?/test/_search {"suggest":?{"title_suggest":?{"text":?"s",?//?關鍵字"completion":?{"field":?"title",?//?補全查詢的字段"skip_duplicates":?true,?//?跳過重復的"size":?10?//?獲取前10條結果}}} }九.?數據同步
????????elasticsearch中的酒店數據來自于mysql數據庫,因此mysql數據發生改變時,elasticsearch也必須跟著改變,這個就是elasticsearch與mysql之間的數據同步。
1. 同步調用
基本步驟:hotel-demo對外提供接口,用來修改elasticsearch中的數據????????酒店管理服務在完成數據庫操作后,直接調用hotel-demo提供的接口,
2.?異步通知
流程:hotel-admin對mysql數據庫數據完成增、刪、改后,發送MQ消息????????hotel-demo監聽MQ,接收到消息后完成elasticsearch數據修改
3.?監聽binlog
流程:給mysql開啟binlog功能????????mysql完成增、刪、改操作都會記錄在binlog中????????hotel-demo基于canal監聽binlog變化,實時更新elasticsearch中的內容
4.?選擇
方式一:同步調用
-
優點:實現簡單,粗暴
-
缺點:業務耦合度高
方式二:異步通知
-
優點:低耦合,實現難度一般
-
缺點:依賴mq的可靠性
方式三:監聽binlog
-
優點:完全解除服務間耦合
-
缺點:開啟binlog增加數據庫負擔、實現復雜度高
十. 集群
????????單機的elasticsearch做數據存儲,必然面臨兩個問題:海量數據存儲問題、單點故障問題。海量數據存儲問題:將索引庫從邏輯上拆分為N個分片(shard),存儲到多個節點????????單點故障問題:將分片數據在不同節點備份(replica )
1.?ES集群相關概念
-
集群(cluster):一組擁有共同的 cluster name 的 節點。
-
節點(node) :集群中的一個 Elasticearch 實例
-
分片(shard):索引可以被拆分為不同的部分進行存儲,稱為分片。在集群環境下,一個索引的不同分片可以拆分到不同的節點中
-
主分片(Primary shard):相對于副本分片的定義。
-
副本分片(Replica shard)每個主分片可以有一個或者多個副本,數據和主分片一樣。
2.?集群腦裂問題
? ? ? ? 2.1?集群職責劃分
? ? ? ? ?2.2?腦裂問題
????????腦裂是因為集群中的節點失聯導致的。解決腦裂的方案是,要求選票超過 ( eligible節點數量 + 1 )/ 2 才能當選為主,因此eligible節點數量最好是奇數。對應配置項是discovery.zen.minimum_master_nodes,在es7.0以后,已經成為默認配置,因此一般不會發生腦裂問題。
? ? ? ? 2.3?集群分布式查詢
????????elasticsearch的查詢分成兩個階段:scatter phase:分散階段,coordinating node會把請求分發到每一個分片????????gather phase:聚集階段,coordinating node匯總data node的搜索結果,并處理為最終結果集返回給用戶
?????????2.4?集群故障轉移
????????集群的master節點會監控集群中的節點狀態,如果發現有節點宕機,會立即將宕機節點的分片數據遷移到其它節點,確保數據安全,這個叫做故障轉移。
總結
以上是生活随笔為你收集整理的Elasticsearch 分布式搜索引擎 速学的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java——获取网页源代码
- 下一篇: 复利的特点