導(dǎo)航:
【黑馬Java筆記+踩坑匯總】JavaSE+JavaWeb+SSM+SpringBoot+瑞吉外賣(mài)+SpringCloud/SpringCloudAlibaba+黑馬旅游+谷粒商城
?黑馬旅游源碼:?
GitHub:
GitHub - vincewm/hotel: 黑馬旅游項(xiàng)目
Gitee:
hotel: 黑馬旅游項(xiàng)目
目錄
1.數(shù)據(jù)聚合
1.1.聚合的種類
1.2.DSL實(shí)現(xiàn)聚合
1.2.1.Bucket聚合語(yǔ)法
1.2.2.聚合結(jié)果排序
1.2.3.通過(guò)query標(biāo)簽限定聚合范圍
1.2.4.度量聚合語(yǔ)法,stats
1.2.5.小結(jié),聚合三要素
1.3.RestAPI實(shí)現(xiàn)聚合
1.3.1.API語(yǔ)法
1.3.2.黑馬旅游業(yè)務(wù)需求,標(biāo)簽隨著搜索結(jié)果變化
1.3.3.業(yè)務(wù)實(shí)現(xiàn)
2.自動(dòng)補(bǔ)全
2.1.pinyin拼音分詞器的介紹和安裝
2.2.自定義分詞器,ik+拼音過(guò)濾
2.2.1 實(shí)現(xiàn)方法?
2.2.2 索引分詞器和搜索分詞器問(wèn)題
2.3.自動(dòng)補(bǔ)全查詢,conmpetion suggester
2.4.實(shí)現(xiàn)酒店搜索框自動(dòng)補(bǔ)全
2.4.1.創(chuàng)建新索引庫(kù),使用自定義分詞器
2.4.2.HotelDoc實(shí)體類添加suggestion字段
2.4.3.重新導(dǎo)入MySQL數(shù)據(jù)到es索引庫(kù)
2.4.4 測(cè)試補(bǔ)全,suggest搜索"rj"結(jié)果“如家”的文檔
2.4.5.自動(dòng)補(bǔ)全查詢的JavaAPI,SuggestBuilder()
2.4.6.實(shí)現(xiàn)旅游項(xiàng)目搜索框自動(dòng)補(bǔ)全
3.mysql與es數(shù)據(jù)同步
3.1.思路分析
3.1.1.方案一:同步調(diào)用
3.1.2.方案二:異步通知
3.1.3.方案三:canal監(jiān)聽(tīng)mysql的binlog
3.1.4.三種方案優(yōu)缺點(diǎn)總結(jié)
3.2.MQ實(shí)現(xiàn)數(shù)據(jù)同步
3.2.1.思路
3.2.2.導(dǎo)入hotel-admin后臺(tái)管理端、修改pom和yml
3.2.3.聲明交換機(jī)、隊(duì)列
3.2.4.后臺(tái)端發(fā)送MQ消息
3.2.5.用戶端接收MQ消息
3.2.6 測(cè)試
3.2.7.vue插件實(shí)現(xiàn)快速拷貝數(shù)據(jù)到表單
4.集群
4.0.概述?
4.1.搭建ES集群
4.1.0.Docker Compose介紹
4.1.1.創(chuàng)建es集群
4.1.2.集群狀態(tài)監(jiān)控,安裝cerebro
4.1.3.創(chuàng)建索引庫(kù)
4.1.4.查看分片效果
4.2.集群腦裂問(wèn)題
4.2.1.集群職責(zé)劃分,四種節(jié)點(diǎn)類型
4.2.2.腦裂問(wèn)題
4.2.3.小結(jié),四種節(jié)點(diǎn)類型
4.3.集群分布式存儲(chǔ)
4.3.1.文檔存儲(chǔ)到分片測(cè)試
4.3.2.分片存儲(chǔ)原理
4.4.集群分布式查詢,協(xié)調(diào)節(jié)點(diǎn)的分散和聚集
4.5.集群故障轉(zhuǎn)移
1.數(shù)據(jù)聚合
聚合(aggregations)可以讓我們極其方便的實(shí)現(xiàn)對(duì)數(shù)據(jù)的統(tǒng)計(jì)、分析、運(yùn)算。例如:
- 什么品牌的手機(jī)最受歡迎?
- 這些手機(jī)的平均價(jià)格、最高價(jià)格、最低價(jià)格?
- 這些手機(jī)每月的銷(xiāo)售情況如何?
實(shí)現(xiàn)這些統(tǒng)計(jì)功能的比數(shù)據(jù)庫(kù)的sql要方便的多,而且查詢速度非??欤梢詫?shí)現(xiàn)近實(shí)時(shí)搜索效果。
1.1.聚合的種類
聚合常見(jiàn)的有三類:
-
桶(Bucket)聚合:用來(lái)對(duì)文檔做分組
- TermAggregation:按照文檔字段值分組,例如按照品牌值分組、按照國(guó)家分組
- Date Histogram:按照日期階梯分組,例如一周為一組,或者一月為一組
-
度量(Metric)聚合:用以計(jì)算一些值,比如:最大值、最小值、平均值等
- Avg:求平均值
- Max:求最大值
- Min:求最小值
- Stats:同時(shí)求max、min、avg、sum等
-
管道(pipeline)聚合:其它聚合的結(jié)果為基礎(chǔ)做聚合
注意:參加聚合的字段必須是不能分詞,例如是keyword、日期、數(shù)值、布爾類型
1.2.DSL實(shí)現(xiàn)聚合
現(xiàn)在,我們要統(tǒng)計(jì)所有數(shù)據(jù)中的酒店品牌有幾種,其實(shí)就是按照品牌對(duì)數(shù)據(jù)分組。此時(shí)可以根據(jù)酒店品牌的名稱做聚合,也就是Bucket聚合。
1.2.1.Bucket聚合語(yǔ)法
語(yǔ)法如下:
GET /hotel/_search
{
"size": 0, // 設(shè)置size為0,結(jié)果中不包含文檔,只包含聚合結(jié)果。如果設(shè)為20,就是既展示20個(gè)brand查詢的"hits",又展示聚合"aggregations"
"aggs": { // 定義聚合
"聚合名": { //給聚合起個(gè)名字,例如brandAgg。查詢結(jié)果里聚合名會(huì)嵌套在"aggregations"里
"terms": { // 聚合的類型,按照品牌值聚合,所以選擇term
"field": "字段名", // 參與聚合的字段,例如brand
"size": 20 // 希望獲取的聚合結(jié)果數(shù)量
? ? ? ? "order": {
? ? ? ? ? "_count": "asc"????????//按升序排序,默認(rèn)是降序
? ? ? ? }
}
}
}
}
1.2.2.聚合結(jié)果排序
默認(rèn)情況下,Bucket聚合會(huì)統(tǒng)計(jì)Bucket內(nèi)的文檔數(shù)量,記為_(kāi)count,并且按照_count降序排序。
我們可以指定order屬性,自定義聚合的排序方式:
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"order": {
"_count": "asc" // 按照_count升序排列
},
"size": 20????????
}
}
}
}
?結(jié)果如圖:
1.2.3.通過(guò)query標(biāo)簽限定聚合范圍
“query” 標(biāo)簽和“aggs”標(biāo)簽是并列的,“query” 標(biāo)簽不為空就是限定了聚合范圍。
默認(rèn)情況下,Bucket聚合是對(duì)索引庫(kù)的所有文檔做聚合,但真實(shí)場(chǎng)景下,用戶會(huì)輸入搜索條件,因此聚合必須是對(duì)搜索結(jié)果聚合。那么聚合必須添加限定條件。
我們可以限定要聚合的文檔范圍,只要添加query條件即可。
?需求:只對(duì)200元以下的文檔聚合
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200 // 只對(duì)200元以下的文檔聚合
}
}
},
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}
這次,聚合得到的品牌明顯變少了:
1.2.4.度量聚合語(yǔ)法,stats
上節(jié)課,我們對(duì)酒店按照品牌分組,形成了一個(gè)個(gè)桶。
需求:現(xiàn)在我們需要對(duì)桶內(nèi)的酒店做運(yùn)算,獲取每個(gè)品牌的用戶評(píng)分的min、max、avg等值。
這就要用到Metric聚合了,例如stat聚合:就可以獲取min、max、avg等結(jié)果。
語(yǔ)法如下:
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "字段名",
"size": 20
},
"aggs": { // 是brands聚合的子聚合,也就是分組后對(duì)每組分別計(jì)算
"聚合名如score_stats": { // 聚合名稱
"stats": { // 聚合類型,這里stats可以計(jì)算min、max、avg等。stats是statistics統(tǒng)計(jì)縮寫(xiě)
"field": "score" // 聚合字段,這里是score
}
}
}
}
}
}
這次的score_stats聚合是在brandAgg的聚合內(nèi)部嵌套的子聚合。因?yàn)槲覀冃枰诿總€(gè)桶分別計(jì)算。
我們還可以給聚合結(jié)果做個(gè)排序,例如按照每個(gè)桶的酒店平均分做排序:
1.2.5.小結(jié),聚合三要素
aggs代表聚合,與query同級(jí),此時(shí)query的作用是?
- 限定聚合的的文檔范圍
聚合必須的三要素:
- 聚合名稱
- 聚合類型
- 聚合字段
聚合可配置屬性有:
- size:指定聚合結(jié)果數(shù)量
- order:指定聚合結(jié)果排序方式
- field:指定聚合字段
1.3.RestAPI實(shí)現(xiàn)聚合
1.3.1.API語(yǔ)法
聚合條件與query條件同級(jí)別,因此需要使用request.source()來(lái)指定聚合條件。
聚合條件的語(yǔ)法:
注意:request.source().aggregation(),聚合不是負(fù)數(shù),因?yàn)殡m然DSL是"aggs",但它不是數(shù)組,所以不是request.source().aggregations()
聚合結(jié)果:
聚合的結(jié)果也與查詢結(jié)果不同,API也比較特殊。不過(guò)同樣是JSON逐層解析:
注意:response.getAggregations().get("brandAgg")的返回結(jié)果要是Terms,注意包別導(dǎo)錯(cuò)了,提示的第一個(gè)不是es的包。
@Test public void aggregation()throws IOException{ SearchRequest request = new SearchRequest("hotel"); request.source().size(0).aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(20)); SearchResponse response = client.search(request, RequestOptions.DEFAULT); Terms brandTerms =response.getAggregations().get("brandAgg"); List<? extends Terms.Bucket> buckets = brandTerms.getBuckets(); for(Terms.Bucket bucket:buckets){ System.out.println(bucket.getKeyAsString()+":"+bucket.getDocCount()); } }
1.3.2.黑馬旅游業(yè)務(wù)需求,標(biāo)簽隨著搜索結(jié)果變化
需求:搜索頁(yè)面的品牌、城市等信息不應(yīng)該是在頁(yè)面寫(xiě)死,而是通過(guò)聚合索引庫(kù)中的酒店數(shù)據(jù)得來(lái)的:
分析:
目前,頁(yè)面的城市列表、星級(jí)列表、品牌列表都是寫(xiě)死的,并不會(huì)隨著搜索結(jié)果的變化而變化。但是用戶搜索條件改變時(shí),搜索結(jié)果會(huì)跟著變化。
例如:用戶搜索“東方明珠”,那搜索的酒店肯定是在上海東方明珠附近,因此,城市只能是上海,此時(shí)城市列表中就不應(yīng)該顯示北京、深圳、杭州這些信息了。
也就是說(shuō),搜索結(jié)果中包含哪些城市,頁(yè)面就應(yīng)該列出哪些城市;搜索結(jié)果中包含哪些品牌,頁(yè)面就應(yīng)該列出哪些品牌。
如何得知搜索結(jié)果中包含哪些品牌?如何得知搜索結(jié)果中包含哪些城市?
使用聚合功能,利用Bucket聚合,對(duì)搜索結(jié)果中的文檔基于品牌分組、基于城市分組,就能得知包含哪些品牌、哪些城市了。
因?yàn)槭菍?duì)搜索結(jié)果聚合,因此聚合是限定范圍的聚合,也就是說(shuō)聚合的限定條件跟搜索文檔的條件一致。
查看瀏覽器可以發(fā)現(xiàn),前端其實(shí)已經(jīng)發(fā)出了這樣的一個(gè)請(qǐng)求:
請(qǐng)求參數(shù)與搜索文檔的參數(shù)完全一致。
返回值類型就是頁(yè)面要展示的最終結(jié)果:
結(jié)果是一個(gè)Map結(jié)構(gòu):
- key是字符串,城市、星級(jí)、品牌、價(jià)格
- value是集合,例如多個(gè)城市的名稱
1.3.3.業(yè)務(wù)實(shí)現(xiàn)
在cn.itcast.hotel.web
包的HotelController
中添加一個(gè)方法,遵循下面的要求:
- 請(qǐng)求方式:
POST
- 請(qǐng)求路徑:
/hotel/filters
- 請(qǐng)求參數(shù):
RequestParams
,與搜索文檔的參數(shù)一致 - 返回值類型:
Map<String, List<String>>
代碼:
@PostMapping("filters")
public Map<String, List<String>> getFilters(@RequestBody RequestParams params){
return hotelService.getFilters(params);
}
這里調(diào)用了IHotelService中的getFilters方法,尚未實(shí)現(xiàn)。
在cn.itcast.hotel.service.IHotelService
中定義新方法:
Map<String, List<String>> filters(RequestParams params);
在cn.itcast.hotel.service.impl.HotelServiceImpl
中實(shí)現(xiàn)該方法:
@Override
public Map<String, List<String>> filters(RequestParams params) {
try {
// 1.準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
// 2.準(zhǔn)備DSL
// 2.1.query查詢城市、品牌等基本信息
buildBasicQuery(params, request);
// 2.2.設(shè)置size
request.source().size(0);
// 2.3.聚合,根據(jù)
buildAggregation(request);
// 3.發(fā)出請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析結(jié)果
Map<String, List<String>> result = new HashMap<>();
Aggregations aggregations = response.getAggregations();
// 4.1.根據(jù)品牌名稱,獲取品牌結(jié)果
List<String> brandList = getAggByName(aggregations, "brandAgg");
result.put("brand", brandList);
// 4.2.根據(jù)品牌名稱,獲取品牌結(jié)果
List<String> cityList = getAggByName(aggregations, "cityAgg");
result.put("city", cityList);
// 4.3.根據(jù)品牌名稱,獲取品牌結(jié)果
List<String> starList = getAggByName(aggregations, "starAgg");
result.put("starName", starList);
return result;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//三個(gè)聚合,分別聚合品牌、城市、星級(jí)
private void buildAggregation(SearchRequest request) {
request.source().aggregation(AggregationBuilders
.terms("brandAgg")
.field("brand")
.size(100)
);
request.source().aggregation(AggregationBuilders
.terms("cityAgg")
.field("city")
.size(100)
);
request.source().aggregation(AggregationBuilders
.terms("starAgg")
.field("starName")
.size(100)
);
}
private List<String> getAggByName(Aggregations aggregations, String aggName) {
// 4.1.根據(jù)聚合名稱獲取聚合結(jié)果
Terms brandTerms = aggregations.get(aggName);
// 4.2.獲取buckets
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
// 4.3.遍歷
List<String> brandList = new ArrayList<>();
for (Terms.Bucket bucket : buckets) {
// 4.4.獲取key
String key = bucket.getKeyAsString();
brandList.add(key);
}
return brandList;
}
測(cè)試:?
2.自動(dòng)補(bǔ)全
當(dāng)用戶在搜索框輸入字符時(shí),我們應(yīng)該提示出與該字符有關(guān)的搜索項(xiàng),如圖:
這種根據(jù)用戶輸入的字母,提示完整詞條的功能,就是自動(dòng)補(bǔ)全了。
因?yàn)樾枰鶕?jù)拼音字母來(lái)推斷,因此要用到拼音分詞功能。
2.1.pinyin拼音分詞器的介紹和安裝
要實(shí)現(xiàn)根據(jù)字母做補(bǔ)全,就必須對(duì)文檔按照拼音分詞。在GitHub上恰好有elasticsearch的拼音分詞插件。地址:
https://github.com/medcl/elasticsearch-analysis-pinyin
課前資料中也提供了拼音分詞器的安裝包:
安裝方式與IK分詞器一樣,分三步:
? ①解壓
? ②上傳到虛擬機(jī)中,elasticsearch的plugin目錄
/var/lib/docker/volumes/es-plugins/_data
? ③重啟elasticsearch
docker restart es
? ④測(cè)試
詳細(xì)安裝步驟可以參考IK分詞器的安裝過(guò)程。
測(cè)試用法如下:
POST /_analyze
{
"text": "如家酒店還不錯(cuò)",
"analyzer": "pinyin"
}
結(jié)果:
2.2.自定義分詞器,ik+拼音過(guò)濾
自定義分詞器適合在創(chuàng)建索引庫(kù)時(shí)使用,不能在搜索時(shí)候用?
2.2.1 實(shí)現(xiàn)方法?
默認(rèn)的拼音分詞器會(huì)將每個(gè)漢字單獨(dú)分為拼音,而我們希望的是每個(gè)詞條形成一組拼音,需要對(duì)拼音分詞器做個(gè)性化定制,形成自定義分詞器。
elasticsearch中分詞器(analyzer)的組成包含三部分:
- character filters:在tokenizer之前對(duì)特殊字符進(jìn)行處理。例如刪除字符、替換字符
- tokenizer:將文本按照一定的規(guī)則切割成詞條(term)。例如keyword,就是不分詞;還有ik_smart
- tokenizer filter:將tokenizer輸出的詞條做進(jìn)一步處理,tokenizer的詞條結(jié)果依然在,只是處理后新加了一些。例如大小寫(xiě)轉(zhuǎn)換、同義詞處理、拼音處理等
拼音分詞器處理文檔流程:
自定義分詞器,把ik分詞結(jié)果過(guò)濾成拼音:
settings-analysis下定義分詞器analyzer和filter
PUT /test
{
//設(shè)置分詞器和過(guò)濾器
"settings": {
"analysis": {
"analyzer": { // 自定義分詞器
"my_analyzer": { // 分詞器名稱,這里不需要指定character filters過(guò)濾器,因?yàn)闆](méi)有特殊字符需要處理
"tokenizer": "ik_max_word",
"filter": "my_py_filter" //對(duì)ik分詞結(jié)果做進(jìn)一步處理
}
},
"filter": { // 自定義tokenizer filter
"my_py_filter": { // 過(guò)濾器名稱
"type": "pinyin", // 過(guò)濾器類型,填寫(xiě)分詞器名,這里是pinyin
"keep_full_pinyin": false,//eg: 劉德華> [liu,de,hua], default: true
"keep_joined_full_pinyin": true,//eg: 劉德華> [liudehua], default: false
"keep_original": true,//保留原始輸入,默認(rèn)false
"limit_first_letter_length": 16,//
"remove_duplicated_term": true,//
"none_chinese_pinyin_tokenize": false//?默認(rèn)true。eg: liudehuaalibaba13zhuanghan -> liu,de,hua,a,li,ba,ba,13,zhuang,han
}
}
}
},
//創(chuàng)建索引
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "my_analyzer",????????//拼音分詞器適合在創(chuàng)建索引時(shí)使用,不能在搜索時(shí)候用
"search_analyzer": "ik_smart"?????//如果搜索時(shí)用拼音分詞器,搜索"獅子愛(ài)跳舞",會(huì)搜出"虱子"等同音字
}
}
}
}
注意:
拼音分詞器適合在創(chuàng)建索引時(shí)使用,不能在搜索時(shí)候用,如果搜索時(shí)用拼音分詞器,搜索"獅子愛(ài)跳舞",會(huì)搜出"虱子"等同音字。
測(cè)試:自定義分詞器搜索結(jié)果既有ik_smart,還有拼音分詞的結(jié)果
GET /test/_analyze
{
"analyzer": "my_analyzer"
, "text": "今天天氣好"
}
2.2.2 索引分詞器和搜索分詞器問(wèn)題
在使用ik+拼音過(guò)濾的分詞器時(shí),建議創(chuàng)建的字段的索引分詞器設(shè)為自定義分詞器,搜索分詞器設(shè)為ik分詞器。防止搜索時(shí)搜出拼音諧音的情況。
指定索引、搜索分詞器,并創(chuàng)建索引:
PUT /test
{
//設(shè)置分詞器和過(guò)濾器
"settings": {
"analysis": {
"analyzer": { // 自定義分詞器
"my_analyzer": { // 分詞器名稱,這里不需要指定character filters過(guò)濾器,因?yàn)闆](méi)有特殊字符需要處理
"tokenizer": "ik_max_word",
"filter": "my_py_filter" //對(duì)ik分詞結(jié)果做進(jìn)一步處理
}
},
"filter": { // 自定義tokenizer filter
"my_py_filter": { // 過(guò)濾器名稱
"type": "pinyin", // 過(guò)濾器類型,填寫(xiě)分詞器名,這里是pinyin
"keep_full_pinyin": false,//eg: 劉德華> [liu,de,hua], default: true
"keep_joined_full_pinyin": true,//eg: 劉德華> [liudehua], default: false
"keep_original": true,//保留原始輸入,默認(rèn)false
"limit_first_letter_length": 16,//
"remove_duplicated_term": true,//
"none_chinese_pinyin_tokenize": false//?默認(rèn)true。eg: liudehuaalibaba13zhuanghan -> liu,de,hua,a,li,ba,ba,13,zhuang,han
}
}
}
},
//創(chuàng)建索引
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "my_analyzer",????????//拼音分詞器適合在創(chuàng)建索引時(shí)使用,不能在搜索時(shí)候用
"search_analyzer": "ik_smart"?????//如果搜索時(shí)用拼音分詞器,搜索"獅子愛(ài)跳舞",會(huì)搜出"虱子"等同音字
}
}
}
}
建議索引分詞器設(shè)為自定義分詞器,搜索分詞器設(shè)為ik分詞器。防止搜索時(shí)搜出拼音諧音問(wèn)題。
問(wèn)題模擬,索引、搜索都用自定義分詞器:
DELETE /test PUT /test { "settings": { "analysis": { "analyzer": { "my_analyzer":{ "tokenizer":"ik_smart", "filter":"py_filter" } }, "filter": { "py_filter":{ "type":"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" //統(tǒng)一ik+拼音過(guò)濾 } } } } POST /test/_doc/1 { "name":"獅子" } POST /test/_doc/2 { "name":"師資" } GET /test/_search { "query": { "match": { "name": "獅子愛(ài)跳舞" } } }
可以看見(jiàn),明明搜索的是獅子,卻搜出了諧音師資。因?yàn)樗阉鳌蔼{子愛(ài)跳舞”時(shí),ik+拼音過(guò)濾后的結(jié)果里有“shizi”,而師資的分詞結(jié)果也有“shizi”,所以就搜出了師資。
如果搜索“shizi”,出現(xiàn)結(jié)果正常,是獅子和師資。
解決辦法:索引分詞器設(shè)為自定義分詞器,搜索分詞器設(shè)為ik分詞器。防止搜索時(shí)搜出拼音諧音問(wèn)題。
明顯不是我們想要的,所以要讓它搜索時(shí)候只用ik分詞,不要拼音過(guò)濾就搜不出諧音了,而添加新文檔時(shí)還要它進(jìn)行分詞拼音,以便于我們可以搜拼音時(shí)也能搜出對(duì)應(yīng)字段。
PUT /test
{
//設(shè)置分詞器和過(guò)濾器
"settings": {
...自定義分詞器
},
//創(chuàng)建索引
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "my_analyzer",????????//創(chuàng)建文檔時(shí)自定義分詞器,ik+拼音過(guò)濾
"search_analyzer": "ik_smart"?????//搜索時(shí)只用ik分詞器
}
}
}
}
總結(jié):
如何使用拼音分詞器?
①下載pinyin分詞器
②解壓并放到elasticsearch的plugin目錄
③重啟即可
如何自定義分詞器?
①創(chuàng)建索引庫(kù)時(shí),在settings中配置,可以包含三部分
②character filter
③tokenizer
④filter
拼音分詞器注意事項(xiàng)?
- 為了避免搜索到同音字,搜索時(shí)不要使用拼音分詞器
2.3.自動(dòng)補(bǔ)全查詢,conmpetion suggester
補(bǔ)全效果預(yù)覽:錄入?["天蒼蒼", "野茫茫"]、["天府", "天下"]、["世界", "黃天"]。suggest搜索“天”,可以搜索出前兩個(gè)文檔。suggest搜索“天蒼”,只能搜索出第一個(gè)文檔。
elasticsearch提供了Completion Suggester查詢來(lái)實(shí)現(xiàn)自動(dòng)補(bǔ)全功能。這個(gè)查詢會(huì)匹配以用戶輸入內(nèi)容開(kāi)頭的詞條并返回。為了提高補(bǔ)全查詢的效率,對(duì)于文檔中字段的類型有一些約束:
-
參與補(bǔ)全查詢的字段必須是completion類型,數(shù)據(jù)是字符串?dāng)?shù)組。completion譯為完成
-
字段的內(nèi)容一般是用來(lái)補(bǔ)全的多個(gè)詞條形成的數(shù)組。
創(chuàng)建索引庫(kù):
// 創(chuàng)建索引庫(kù)
PUT test
{
"mappings": {
"properties": {
"title":{
"type": "completion"
}
}
}
}
插入數(shù)據(jù):
// 示例數(shù)據(jù)
POST test/_doc
{
"title": ["天蒼蒼", "野茫茫"]
}
POST test/_doc
{
"title": ["天府", "天下"]
}
POST test/_doc
{
"title": ["世界", "黃天"]
}
自動(dòng)補(bǔ)全查詢:
// 自動(dòng)補(bǔ)全查詢
GET /test/_search
{
"suggest": {
"titleSuggest": { //例如自定義查詢名稱
"text": "天", // 關(guān)鍵字
"completion": {
"field": "title", // 補(bǔ)全查詢的字段,例如title
"skip_duplicates": true, // 跳過(guò)重復(fù)的
"size": 10 // 獲取前10條結(jié)果
}
}
}
}
搜索結(jié)果:
{
"took" : 275,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"suggest" : {
"my_suggest" : [
{
"text" : "天",
"offset" : 0,
"length" : 1,
"options" : [
{
"text" : "天下",
"_index" : "test2",
"_type" : "_doc",
"_id" : "AEutP4MBg4Wtm5vtyLdE",
"_score" : 1.0,
"_source" : {
"name" : [
"天府",
"天下"
]
}
},
{
"text" : "天蒼蒼",
"_index" : "test2",
"_type" : "_doc",
"_id" : "AUutP4MBg4Wtm5vtyLdI",
"_score" : 1.0,
"_source" : {
"name" : [
"天蒼蒼",
"野茫茫"
]
}
}
]
}
]
}
}
2.4.實(shí)現(xiàn)酒店搜索框自動(dòng)補(bǔ)全
現(xiàn)在,我們的hotel索引庫(kù)還沒(méi)有設(shè)置拼音分詞器,需要修改索引庫(kù)中的配置。但是我們知道索引庫(kù)是無(wú)法修改的,只能刪除然后重新創(chuàng)建。
另外,我們需要添加一個(gè)字段,用來(lái)做自動(dòng)補(bǔ)全,將brand、suggestion、city等都放進(jìn)去,作為自動(dòng)補(bǔ)全的提示。
因此,總結(jié)一下,我們需要做的事情包括:
-
修改hotel索引庫(kù)結(jié)構(gòu),設(shè)置自定義拼音分詞器
-
修改索引庫(kù)的name、all字段,使用自定義分詞器
-
索引庫(kù)添加一個(gè)新字段suggestion,類型為completion類型,使用自定義的分詞器
-
給HotelDoc類添加suggestion字段,內(nèi)容包含brand、business
-
重新導(dǎo)入數(shù)據(jù)到hotel庫(kù)
2.4.1.創(chuàng)建新索引庫(kù),使用自定義分詞器
①ik+拼音過(guò)濾分詞器,給ik分詞設(shè)置自定義過(guò)濾器,給分詞進(jìn)一步處理成拼音。索引使用自定義分詞器,搜索使用ik分詞器。②關(guān)鍵字+拼音過(guò)濾分詞器,代碼補(bǔ)全使用的分詞器。 ?
代碼如下:
//酒店數(shù)據(jù)索引庫(kù)。先刪除舊的,再新的
DELETE /hotel
PUT /hotel
{
"settings": {
"analysis": {
"analyzer": {
"text_anlyzer": { //ik+拼音過(guò)濾
"tokenizer": "ik_max_word",
"filter": "py"
},
"completion_analyzer": { //keyword+拼音過(guò)濾,相當(dāng)于又保持關(guān)鍵詞,又新加定制版拼音分詞
"tokenizer": "keyword",
"filter": "py"
}
},
"filter": {
"py": {
"type": "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": {
"id":{
"type": "keyword"
},
"name":{
"type": "text",
"analyzer": "text_anlyzer", //新建文檔時(shí)分詞器用ik+拼音過(guò)濾
"search_analyzer": "ik_smart", //搜索分詞器用ik
"copy_to": "all"
},
"address":{
"type": "keyword",
"index": false
},
"price":{
"type": "integer"
},
"score":{
"type": "integer"
},
"brand":{
"type": "keyword",
"copy_to": "all"
},
"city":{
"type": "keyword"
},
"starName":{
"type": "keyword"
},
"business":{
"type": "keyword",
"copy_to": "all"
},
"location":{
"type": "geo_point"
},
"pic":{
"type": "keyword",
"index": false
},
"all":{
"type": "text",
"analyzer": "text_anlyzer",
"search_analyzer": "ik_smart"
},
"suggestion":{ //補(bǔ)全字段suggestion
"type": "completion", //補(bǔ)全字段類型必須completion
"analyzer": "completion_analyzer" //補(bǔ)全分詞器,keyword+拼音過(guò)濾
}
}
}
}
2.4.2.HotelDoc實(shí)體類添加suggestion字段
HotelDoc中要添加一個(gè)字段,用來(lái)做自動(dòng)補(bǔ)全,內(nèi)容可以是酒店品牌、城市、商圈等信息。按照自動(dòng)補(bǔ)全字段的要求,最好是這些字段的數(shù)組。
因此我們?cè)贖otelDoc中添加一個(gè)suggestion字段,類型為List<String>
,然后將brand、city、business等信息放到里面。
代碼如下:
package cn.itcast.hotel.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business; //商圈,例如虹橋機(jī)場(chǎng)/國(guó)家會(huì)展中心
private String location;
private String pic;
private Object distance;
private Boolean isAD;
private List<String> suggestion; //放自動(dòng)補(bǔ)全的list列表,這里只補(bǔ)全搜索商圈和品牌
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
// 組裝suggestion,把品牌和商圈放進(jìn)去
if(this.business.contains("/")){
// business有多個(gè)值,例如“例如虹橋機(jī)場(chǎng)/國(guó)家會(huì)展中心”,需要切割
String[] arr = this.business.split("/");
// 添加元素
this.suggestion = new ArrayList<>();
this.suggestion.add(this.brand);
Collections.addAll(this.suggestion, arr);
}else {
this.suggestion = Arrays.asList(this.brand, this.business);
}
}
}
2.4.3.重新導(dǎo)入MySQL數(shù)據(jù)到es索引庫(kù)
重新執(zhí)行之前編寫(xiě)的導(dǎo)入數(shù)據(jù)功能
@Test
public void bulk() throws IOException {
List<Hotel> hotels = hotelService.list();
for(Hotel hotel:hotels){
HotelDoc hotelDoc = new HotelDoc(hotel);
client.index(new IndexRequest("hotel").id(hotel.getId().toString()).source(JSON.toJSONString(hotelDoc),XContentType.JSON),RequestOptions.DEFAULT);
}
}
查詢所有 ,可以看到新的酒店數(shù)據(jù)中包含了suggestion:
GET /hotel/_search
{
"query": {
"match_all": {}
}
}
2.4.4 測(cè)試補(bǔ)全,suggest搜索"rj"結(jié)果“如家”的文檔
因?yàn)?strong>suggestion字段自定義分詞器是keyword+拼音過(guò)濾,所以搜索“如”搜不出“如家”。搜索“如家”可以搜索出brand為“如家”的一條文檔(搜索條件有跳過(guò)重復(fù))。搜索“rj”,可以搜索出brand為“如家”的文檔
測(cè)試:?
GET /hotel/_search
{
"suggest": {
"my_suggest": {
"text": "rj",
"completion":{
"field": "suggestion",
"skip_duplicates": true,
"size": 10
}
}
}
}
因?yàn)橹付ㄌ^(guò)重復(fù),所以搜索結(jié)果僅一條:
{
"took" : 6,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"suggest" : {
"my_suggest" : [
{
"text" : "如家",
"offset" : 0,
"length" : 2,
"options" : [
{
"text" : "如家",
"_index" : "hotel",
"_type" : "_doc",
"_id" : "415600",
"_score" : 1.0,
"_source" : {
"address" : "三間房鄉(xiāng)褡褳坡村青年溝西側(cè)558號(hào)",
"brand" : "如家",
"business" : "傳媒大學(xué)/管莊地區(qū)",
"city" : "北京",
"id" : 415600,
"location" : "39.923212, 116.560023",
"name" : "如家酒店(北京朝陽(yáng)北路傳媒大學(xué)褡褳坡地鐵站店)",
"pic" : "https://m.tuniucdn.com/fb3/s1/2n9c/3NezpxNZWQMdNXibwbMkQuAZjDyJ_w200_h200_c1_t0.jpg",
"price" : 259,
"score" : 47,
"starName" : "二鉆",
"suggestion" : [
"如家",
"傳媒大學(xué)",
"管莊地區(qū)"
]
}
}
]
}
]
}
}
2.4.5.自動(dòng)補(bǔ)全查詢的JavaAPI,SuggestBuilder()
之前我們學(xué)習(xí)了自動(dòng)補(bǔ)全查詢的DSL,而沒(méi)有學(xué)習(xí)對(duì)應(yīng)的JavaAPI,這里給出一個(gè)示例:
suggest和query是平級(jí)的。?
而自動(dòng)補(bǔ)全的結(jié)果也比較特殊,解析的代碼如下:
2.4.6.實(shí)現(xiàn)旅游項(xiàng)目搜索框自動(dòng)補(bǔ)全
查看前端頁(yè)面,可以發(fā)現(xiàn)當(dāng)我們?cè)谳斎肟蜴I入時(shí),前端會(huì)發(fā)起ajax請(qǐng)求:
返回值是補(bǔ)全詞條的集合,類型為List<String>
1)在cn.itcast.hotel.web
包下的HotelController
中添加新接口,接收新的請(qǐng)求:
@GetMapping("suggestion")
public List<String> getSuggestions(@RequestParam("key") String prefix) {
return hotelService.getSuggestions(prefix);
}
2)在cn.itcast.hotel.service
包下的IhotelService
中添加方法:
List<String> getSuggestions(String prefix);
3)在cn.itcast.hotel.service.impl.HotelService
中實(shí)現(xiàn)該方法:
建議截圖補(bǔ)全的es代碼,貼到屏幕上,層層解析?
@Override
public List<String> getSuggestions(String prefix) {
try {
// 1.準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
// 2.準(zhǔn)備DSL
request.source().suggest(new SuggestBuilder().addSuggestion( //new SuggestBuilder()不是SuggestBuilders
"suggestions", //自定義補(bǔ)全的名字,后面根據(jù)它解析response
SuggestBuilders.completionSuggestion("suggestion") //字段名
.prefix(prefix) //需要補(bǔ)全的文本
.skipDuplicates(true)
.size(10)
));
// 3.發(fā)起請(qǐng)求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析結(jié)果
Suggest suggest = response.getSuggest();
// 4.1.根據(jù)補(bǔ)全查詢名稱,獲取補(bǔ)全結(jié)果,注意返回值和ctrl+alt+v生成的內(nèi)容不一樣
CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
// 4.2.獲取options
List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
// 4.3.遍歷
List<String> list = new ArrayList<>(options.size());
for (CompletionSuggestion.Entry.Option option : options) {
String text = option.getText().toString();
list.add(text);
}
return list;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
測(cè)試成功:
3.mysql與es數(shù)據(jù)同步
elasticsearch中的酒店數(shù)據(jù)來(lái)自于mysql數(shù)據(jù)庫(kù),因此mysql數(shù)據(jù)發(fā)生改變時(shí),elasticsearch也必須跟著改變,這個(gè)就是elasticsearch與mysql之間的數(shù)據(jù)同步。
3.1.思路分析
常見(jiàn)的數(shù)據(jù)同步方案有三種:
- 同步調(diào)用
- 異步通知
- 監(jiān)聽(tīng)binlog
3.1.1.方案一:同步調(diào)用
方案一:同步調(diào)用
管理端只能訪問(wèn)mysql,用戶端只能訪問(wèn)es,兩者間隔離,符合微服務(wù)規(guī)范。?
基本步驟如下:
- hotel-demo對(duì)外提供接口,用來(lái)修改elasticsearch中的數(shù)據(jù)
- 酒店管理服務(wù)在完成數(shù)據(jù)庫(kù)操作后,直接調(diào)用hotel-demo提供的接口,
缺點(diǎn):耦合度高?
3.1.2.方案二:異步通知
方案二:異步通知
流程如下:
- hotel-admin對(duì)mysql數(shù)據(jù)庫(kù)數(shù)據(jù)完成增、刪、改后,發(fā)送MQ消息
- hotel-demo監(jiān)聽(tīng)MQ,接收到消息后完成elasticsearch數(shù)據(jù)修改
優(yōu)點(diǎn):性能高,耦合度低
缺點(diǎn):依賴于mq消息隊(duì)列可靠性?
3.1.3.方案三:canal監(jiān)聽(tīng)mysql的binlog
canal譯為管道,運(yùn)河。
回顧主從復(fù)制,主庫(kù)開(kāi)啟binlog,從庫(kù)根據(jù)主庫(kù)binlog的增刪改信息,進(jìn)行相同操作。
流程如下:
- 給mysql開(kāi)啟binlog功能
- mysql完成增、刪、改操作都會(huì)記錄在binlog中
- hotel-demo基于canal監(jiān)聽(tīng)binlog變化,實(shí)時(shí)更新elasticsearch中的內(nèi)容
優(yōu)點(diǎn):耦合度最低
缺點(diǎn):mysql壓力增加?
3.1.4.三種方案優(yōu)缺點(diǎn)總結(jié)
方式一:同步調(diào)用
- 優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,粗暴
- 缺點(diǎn):業(yè)務(wù)耦合度高
方式二:異步通知
- 優(yōu)點(diǎn):低耦合,實(shí)現(xiàn)難度一般
- 缺點(diǎn):依賴mq的可靠性
方式三:監(jiān)聽(tīng)binlog
- 優(yōu)點(diǎn):完全解除服務(wù)間耦合
- 缺點(diǎn):開(kāi)啟binlog增加數(shù)據(jù)庫(kù)負(fù)擔(dān)、實(shí)現(xiàn)復(fù)雜度高
3.2.MQ實(shí)現(xiàn)數(shù)據(jù)同步
3.2.1.思路
利用課前資料提供的hotel-admin項(xiàng)目作為酒店管理的微服務(wù)。當(dāng)酒店數(shù)據(jù)發(fā)生增、刪、改時(shí),要求對(duì)elasticsearch中數(shù)據(jù)也要完成相同操作。
使用RabbitMQ的發(fā)布/訂閱模型topic話題模式,支持不同的消息根據(jù)routingKey被不同的隊(duì)列消費(fèi)。先聲明交換機(jī)、隊(duì)列,增刪兩個(gè)routingKey。只需要增刪兩個(gè)隊(duì)列,restapi里增改是一致的,id存在則修改,id不存在則新增。 ?
忘了就回顧:SpringCloud基礎(chǔ)4——RabbitMQ和SpringAMQP?
步驟:
-
導(dǎo)入課前資料提供的hotel-admin項(xiàng)目,啟動(dòng)并測(cè)試酒店數(shù)據(jù)的CRUD
-
聲明exchange、queue、RoutingKey
-
在hotel-admin中的增、刪、改業(yè)務(wù)中完成消息發(fā)送
-
在hotel-demo中完成消息監(jiān)聽(tīng),并更新elasticsearch中數(shù)據(jù)
-
啟動(dòng)并測(cè)試數(shù)據(jù)同步功能
3.2.2.導(dǎo)入hotel-admin后臺(tái)管理端、修改pom和yml
導(dǎo)入課前資料提供的hotel-admin項(xiàng)目:
修改pom
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
修改yml:
server:
port: 8099
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/heima?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 1234
main:
banner-mode: off
rabbitmq:
host: 192.168.200.131
port: 5672
username: itcast
password: 123321
virtual-host: / #虛擬主機(jī)
#logging:
# level:
# cn.itcast: debug
# pattern:
# dateformat: MM-dd HH:mm:ss:SSS
# type-aliases-package:com.vince.hotel.pojo
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
banner: false
?運(yùn)行后,訪問(wèn) http://localhost:8099
其中包含了酒店的CRUD功能:
3.2.3.聲明交換機(jī)、隊(duì)列
MQ結(jié)構(gòu)如圖:
0)開(kāi)啟RabbitMQ
docker ps -a
docker start 容器名
管理端:
http://ip地址:15672/
SpringCloud基礎(chǔ)4——RabbitMQ_springcloud rabbitmq_vincewm的博客-CSDN博客
1)引入依賴、yml
在hotel-admin、hotel-demo中引入rabbitmq的依賴:
<!--amqp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
yml:
server:
port: 8099
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/heima?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 1234
main:
banner-mode: off
rabbitmq:
host: 192.168.200.131
port: 5672
username: itcast
password: 123321
virtual-host: / #虛擬主機(jī)
#logging:
# level:
# cn.itcast: debug
# pattern:
# dateformat: MM-dd HH:mm:ss:SSS
# type-aliases-package:com.vince.hotel.pojo
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
banner: false
2)常量類,聲明隊(duì)列交換機(jī)名稱
在hotel-admin和hotel-demo中的cn.itcast.hotel.constatnts
包下新建一個(gè)類MqConstants
:
package cn.itcast.hotel.constants;
public class MqConstants {
/**
* 交換機(jī)
*/
public final static String HOTEL_EXCHANGE = "hotel.topic";
/**
* 監(jiān)聽(tīng)新增和修改的隊(duì)列
*/
public final static String HOTEL_INSERT_QUEUE = "hotel.insert.queue";
/**
* 監(jiān)聽(tīng)刪除的隊(duì)列
*/
public final static String HOTEL_DELETE_QUEUE = "hotel.delete.queue";
/**
* 新增或修改的RoutingKey
*/
public final static String HOTEL_INSERT_KEY = "hotel.insert";
/**
* 刪除的RoutingKey
*/
public final static String HOTEL_DELETE_KEY = "hotel.delete";
}
3)聲明隊(duì)列交換機(jī)
topic模式回顧:話題模式,通配符。
Topic
類型的Exchange
與Direct
相比,都是可以根據(jù)RoutingKey
把消息路由到不同的隊(duì)列。只不過(guò)Topic
類型Exchange
可以讓隊(duì)列在綁定Routing key
?的時(shí)候使用通配符!也可以在接收消息時(shí),使用注解方式@RabbitListener的bindings,適用于消息隊(duì)列少的情況:
@RabbitListener(bindings = @QueueBinding( value = @Queue(name = "topic.queue1"), exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC), key = "china.#" )) public void listenTopicQueue1(String msg){ System.out.println("消費(fèi)者接收到topic.queue1的消息:【" + msg + "】"); }
?在config包下創(chuàng)建MqConfig,聲明隊(duì)列、交換機(jī):
package cn.itcast.hotel.config;
import cn.itcast.hotel.constants.MqConstants;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MqConfig {
@Bean
public TopicExchange topicExchange(){
//第二個(gè)參數(shù)為是否持久化,第三個(gè)參數(shù)為是否自動(dòng)刪除。兩個(gè)參數(shù)默認(rèn)值就是持久化、不自動(dòng)刪除
return new TopicExchange(MqConstants.HOTEL_EXCHANGE, true, false);
}
@Bean
public Queue insertQueue(){
return new Queue(MqConstants.HOTEL_INSERT_QUEUE, true);
}
@Bean
public Queue deleteQueue(){
return new Queue(MqConstants.HOTEL_DELETE_QUEUE, true);
}
@Bean
public Binding insertQueueBinding(){
return BindingBuilder.bind(insertQueue()).to(topicExchange()).with(MqConstants.HOTEL_INSERT_KEY);
}
@Bean
public Binding deleteQueueBinding(){
return BindingBuilder.bind(deleteQueue()).to(topicExchange()).with(MqConstants.HOTEL_DELETE_KEY);
}
}
為什么只需要增刪兩個(gè)隊(duì)列,不用“改”?隊(duì)列?
在RestClient的API中,全量修改與新增的API完全一致,判斷依據(jù)是ID:
- 如果新增時(shí),ID已經(jīng)存在,則修改
- 如果新增時(shí),ID不存在,則新增
3.2.4.后臺(tái)端發(fā)送MQ消息
在hotel-admin中的增、刪、改業(yè)務(wù)中分別發(fā)送MQ消息,消息內(nèi)容為id,到時(shí)候用戶端根據(jù)id增刪改:
3.2.5.用戶端接收MQ消息
管理端hotel-admin不能直接調(diào)用es,想對(duì)es實(shí)現(xiàn)增刪改要通過(guò)給用戶端hotel-demo發(fā)送新增(修改)、刪除消息。
hotel-demo接收到MQ消息要做的事情包括:
- 新增消息:根據(jù)傳遞的hotel的id查詢hotel信息,然后新增一條數(shù)據(jù)到索引庫(kù)
- 刪除消息:根據(jù)傳遞的hotel的id刪除索引庫(kù)中的一條數(shù)據(jù)
0) 環(huán)境準(zhǔn)備
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
yml
spring:
rabbitmq:
host: 192.168.200.131
port: 5672
username: itcast
password: 123321
virtual-host: / #虛擬主機(jī)
復(fù)制管理端的MqConstants.java和MqConfig到用戶端
1)service接口新增新增、刪除業(yè)務(wù)
首先在hotel-demo的cn.itcast.hotel.service
包下的IHotelService
中新增新增、刪除業(yè)務(wù)
//mp的增刪方法是saveById和removeById,所以這里并不沖突
void deleteById(Long id);
void insertById(Long id);
2)service實(shí)現(xiàn)類實(shí)現(xiàn)業(yè)務(wù)
給hotel-demo中的cn.itcast.hotel.service.impl
包下的HotelService中實(shí)現(xiàn)業(yè)務(wù):
//mp的增刪方法是saveById和removeById,所以這里并不沖突
@Override
public void deleteById(Long id) {
try {
// 1.準(zhǔn)備Request
DeleteRequest request = new DeleteRequest("hotel", id.toString());
// 2.發(fā)送請(qǐng)求
client.delete(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void insertById(Long id) {
try {
// 0.根據(jù)id查詢酒店數(shù)據(jù)
Hotel hotel = getById(id);
// 轉(zhuǎn)換為文檔類型
HotelDoc hotelDoc = new HotelDoc(hotel);
// 1.準(zhǔn)備Request對(duì)象
IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
// 2.準(zhǔn)備Json文檔
request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
// 3.發(fā)送請(qǐng)求
client.index(request, RequestOptions.DEFAULT);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
坑點(diǎn):刪除文檔的request別忘了設(shè)置id,不然會(huì)刪除所有數(shù)據(jù)。
3)編寫(xiě)監(jiān)聽(tīng)器
在mq
包下新增一個(gè)類:
package cn.itcast.hotel.mq;
import cn.itcast.hotel.constants.MqConstants;
import cn.itcast.hotel.service.IHotelService;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
//別忘了@Component,被ioc容器管理,一直監(jiān)聽(tīng)
@Component
public class HotelListener {
@Autowired
private IHotelService hotelService;
/**
* 監(jiān)聽(tīng)酒店新增或修改的業(yè)務(wù)
* @param id 酒店id
*/
@RabbitListener(queues = MqConstants.HOTEL_INSERT_QUEUE)
public void listenHotelInsertOrUpdate(Long id){
hotelService.insertById(id);
}
/**
* 監(jiān)聽(tīng)酒店刪除的業(yè)務(wù)
* @param id 酒店id
*/
@RabbitListener(queues = MqConstants.HOTEL_DELETE_QUEUE)
public void listenHotelDelete(Long id){
hotelService.deleteById(id);
}
}
3.2.6 測(cè)試
運(yùn)行管理端和用戶端服務(wù)后,打開(kāi)rabbitmq服務(wù)端: http://ip地址:15672
可以看到隊(duì)列、交換機(jī)創(chuàng)建成功:
?
交換機(jī)綁定關(guān)系:
先用戶端搜索一個(gè)酒店,然后在管理端修改酒店信息 :
可以看到隊(duì)列的消息記錄:
用戶端搜索后的數(shù)據(jù)也改變了:
再刪除酒店,發(fā)現(xiàn)成功。
3.2.7.vue插件實(shí)現(xiàn)快速拷貝數(shù)據(jù)到表單
安裝地址
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=zh
拷貝數(shù)據(jù)并保存的辦法:
粘貼即可快速填充表單:
4.集群
4.0.概述?
單機(jī)的elasticsearch做數(shù)據(jù)存儲(chǔ),必然面臨兩個(gè)問(wèn)題:海量數(shù)據(jù)存儲(chǔ)問(wèn)題、單點(diǎn)故障問(wèn)題。
- 海量數(shù)據(jù)存儲(chǔ)問(wèn)題:將索引庫(kù)從邏輯上拆分為N個(gè)分片(shard),存儲(chǔ)到多個(gè)節(jié)點(diǎn)
- 單點(diǎn)故障問(wèn)題:將分片數(shù)據(jù)在不同節(jié)點(diǎn)備份(replica )?
ES集群相關(guān)概念:
一個(gè)集群里有多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都是一個(gè)es實(shí)例,?每個(gè)節(jié)點(diǎn)保存了自己的分片和一個(gè)其他節(jié)點(diǎn)備份的分片。
-
集群(cluster):一組擁有共同的 集群名 的 節(jié)點(diǎn)。
-
節(jié)點(diǎn)(node)?:集群中的一個(gè) Elasticearch 實(shí)例
-
分片(shard):索引可以被拆分為不同的部分進(jìn)行存儲(chǔ),稱為分片。在集群環(huán)境下,一個(gè)索引的不同分片可以拆分到不同的節(jié)點(diǎn)中
解決問(wèn)題:數(shù)據(jù)量太大,單點(diǎn)存儲(chǔ)量有限的問(wèn)題。
此處,我們把數(shù)據(jù)分成3片:shard0、shard1、shard2
-
主分片(Primary shard):相對(duì)于副本分片的定義。
-
副本分片(Replica shard):每個(gè)主分片可以有一個(gè)或者多個(gè)副本,數(shù)據(jù)和主分片一樣。
?
數(shù)據(jù)備份可以保證高可用,但是每個(gè)分片備份一份,所需要的節(jié)點(diǎn)數(shù)量就會(huì)翻一倍,成本實(shí)在是太高了!
為了在高可用和成本間尋求平衡,我們可以這樣做:
- 首先對(duì)數(shù)據(jù)分片,存儲(chǔ)到不同節(jié)點(diǎn)
- 然后對(duì)每個(gè)分片進(jìn)行備份,放到對(duì)方節(jié)點(diǎn),完成互相備份
這樣可以大大減少所需要的服務(wù)節(jié)點(diǎn)數(shù)量,如圖,我們以3分片,每個(gè)分片備份一份為例:
現(xiàn)在,每個(gè)分片都有1個(gè)備份,存儲(chǔ)在3個(gè)節(jié)點(diǎn):
- 節(jié)點(diǎn)node0:保存了分片0和1
- node1:保存了分片0和2
- node2:保存了分片1和2
4.1.搭建ES集群
4.1.0.Docker Compose介紹
Docker Compose是一個(gè)用來(lái)定義和運(yùn)行復(fù)雜應(yīng)用的Docker工具,將一個(gè)或多個(gè)容器組合成一個(gè)完整的應(yīng)用程序。一個(gè)使用Docker容器的應(yīng)用,通常由多個(gè)容器組成。使用Docker Compose不再需要使用shell腳本來(lái)啟動(dòng)容器。
Compose 通過(guò)一個(gè)配置文件來(lái)管理多個(gè)Docker容器,在配置文件中,所有的容器通過(guò)services來(lái)定義,然后使用docker-compose腳本來(lái)啟動(dòng),停止和重啟應(yīng)用,和應(yīng)用中的服務(wù)以及所有依賴服務(wù)的容器,非常適合組合使用多個(gè)容器進(jìn)行開(kāi)發(fā)的場(chǎng)景。
Docker Compose 使用的三個(gè)步驟:
- 使用 Dockerfile 定義應(yīng)用程序的環(huán)境
- 使用 docker-compose.yml 定義構(gòu)成應(yīng)用程序的服務(wù),這樣它們可以在隔離環(huán)境中一起運(yùn)行
- 執(zhí)行 docker-compose up 命令(后面加-d是在后臺(tái)運(yùn)行)來(lái)啟動(dòng)并運(yùn)行整個(gè)應(yīng)用程序
4.1.1.創(chuàng)建es集群
創(chuàng)建同一個(gè)集群的三個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都是一個(gè)es實(shí)例:
①首先編寫(xiě)一個(gè)docker-compose.yml文件,上傳到/root,內(nèi)容如下:
注意端口占用問(wèn)題:ports:-9200:9200,左邊是centos的端口,右邊是容器內(nèi)部端口,只有左邊會(huì)發(fā)生端口占用問(wèn)題,前面安裝過(guò)單點(diǎn)es的默認(rèn)接口是9200,注意一下。
#創(chuàng)建三個(gè)es容器,容器名和節(jié)點(diǎn)名都是es01、es02、es03。
version: '2.2'
services:
es01:
image: elasticsearch:7.12.1 #鏡像名稱
container_name: es01 #容器名稱
environment:
- node.name=es01
#集群名稱。三個(gè)容器集群名稱一樣,es會(huì)自動(dòng)把他們組裝成一個(gè)集群。
- cluster.name=es-docker-cluster
#集群中其他節(jié)點(diǎn)的ip地址;docker容器之間通過(guò)名字直接相互訪問(wèn)
- discovery.seed_hosts=es02,es03
#集群中可以參與選舉的主節(jié)點(diǎn)
- cluster.initial_master_nodes=es01,es02,es03
- "ES_JAVA_OPTS=-Xms512m -Xmx512m" #內(nèi)存大小,最小和最大內(nèi)存
volumes:
- data01:/usr/share/elasticsearch/data
ports:
- 9200:9200 #對(duì)外端口9200:對(duì)內(nèi)端口9200
networks:
- elastic
es02:
image: elasticsearch:7.12.1
container_name: es02
environment:
- node.name=es02
#集群名稱。三個(gè)容器集群名稱一樣,es會(huì)自動(dòng)把他們組裝成一個(gè)集群。
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es01,es03
- cluster.initial_master_nodes=es01,es02,es03
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- data02:/usr/share/elasticsearch/data
ports:
- 9201:9200
networks:
- elastic
es03:
image: elasticsearch:7.12.1
container_name: es03
environment:
- node.name=es03
- cluster.name=es-docker-cluster
#集群名稱。三個(gè)容器集群名稱一樣,es會(huì)自動(dòng)把他們組裝成一個(gè)集群。
- discovery.seed_hosts=es01,es02
- cluster.initial_master_nodes=es01,es02,es03
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- data03:/usr/share/elasticsearch/data
networks:
- elastic
ports:
- 9202:9200
volumes:
data01:
driver: local
data02:
driver: local
data03:
driver: local
networks:
elastic:
driver: bridge
②修改權(quán)限:?
es運(yùn)行需要修改一些linux系統(tǒng)權(quán)限,修改/etc/sysctl.conf
文件
vi /etc/sysctl.conf
添加下面的內(nèi)容,或?qū)⒆⑨尨蜷_(kāi):
vm.max_map_count=262144
然后執(zhí)行命令,讓配置生效:
sysctl -p
③通過(guò)docker-compose up啟動(dòng)集群:
docker-compose up -d
4.1.2.集群狀態(tài)監(jiān)控,安裝cerebro
kibana可以監(jiān)控es集群,不過(guò)新版本需要依賴es的x-pack 功能,配置比較復(fù)雜。
這里推薦使用cerebro來(lái)監(jiān)控es集群狀態(tài),官方網(wǎng)址:https://github.com/lmenezes/cerebro
課前資料已經(jīng)提供了安裝包:
windows解壓即可使用,非常方便。
解壓好的目錄如下:
進(jìn)入對(duì)應(yīng)的bin目錄:
雙擊其中的cerebro.bat文件即可啟動(dòng)服務(wù)。
訪問(wèn)http://localhost:9000 即可進(jìn)入管理界面:
輸入你的elasticsearch的任意節(jié)點(diǎn)的地址和端口,點(diǎn)擊connect即可:
綠色的條,代表集群處于綠色(健康狀態(tài))。
4.1.3.創(chuàng)建索引庫(kù)
1)方法一:利用kibana的DevTools創(chuàng)建索引庫(kù)
在DevTools中輸入指令:
PUT /itcast
{
"settings": {
"number_of_shards": 3, // 分片數(shù)量,shard譯為碎片
"number_of_replicas": 1 // 副本數(shù)量,replica譯為副本,復(fù)制品。
},
"mappings": {
"properties": {
// mapping映射定義 ...
}
}
}
2)方法二:利用cerebro創(chuàng)建索引庫(kù)
利用cerebro還可以創(chuàng)建索引庫(kù):
填寫(xiě)索引庫(kù)信息:
點(diǎn)擊右下角的create按鈕:
4.1.4.查看分片效果
回到首頁(yè),即可查看索引庫(kù)分片效果:
4.2.集群腦裂問(wèn)題
4.2.1.集群職責(zé)劃分,四種節(jié)點(diǎn)類型
集群職責(zé)劃分:
-
候選主節(jié)點(diǎn)(master eligible):管理集群狀態(tài),處理索引庫(kù)增刪請(qǐng)求。
-
數(shù)據(jù)節(jié)點(diǎn)(data):對(duì)記錄的增刪改查。
-
接待節(jié)點(diǎn)(ingest):數(shù)據(jù)存儲(chǔ)前的預(yù)處理。
協(xié)作節(jié)點(diǎn)(coordinating):將請(qǐng)求路由其他節(jié)點(diǎn),合并處理結(jié)果并返回。
主節(jié)點(diǎn)的類型也是master eligible,它是備選主節(jié)點(diǎn)選出來(lái)的。
eligible?譯為合格的,合適的。
ingest譯為接待、吸收。
coordinating譯為協(xié)調(diào),合作。
默認(rèn)情況下,集群中的任何一個(gè)節(jié)點(diǎn)都同時(shí)具備上述四種角色。
但是真實(shí)的集群一定要將集群職責(zé)分離:
職責(zé)分離可以讓我們根據(jù)不同節(jié)點(diǎn)的需求分配不同的硬件去部署。而且避免業(yè)務(wù)之間的互相干擾。
典型的es集群職責(zé)劃分:LB是負(fù)載均衡
4.2.2.腦裂問(wèn)題
選舉master條件:當(dāng)一個(gè)節(jié)點(diǎn)發(fā)現(xiàn)包括自己在內(nèi)的多數(shù)派的master-eligible節(jié)點(diǎn)認(rèn)為集群沒(méi)有master時(shí),就可以發(fā)起master選舉。
選舉master過(guò)程:
-
備選主節(jié)點(diǎn)首先根據(jù)節(jié)點(diǎn)id(第一次啟動(dòng)時(shí)生成的隨機(jī)字符串)排序,第一個(gè)節(jié)點(diǎn)暫定master;
-
如果有(n+1)/2個(gè)節(jié)點(diǎn)投票它是mater,并且它自己也給自己投票,則它當(dāng)選master;
-
否則就暫定第二個(gè)節(jié)點(diǎn)為master,以此類推。
腦裂:master故障,集群選舉出新master后舊master又恢復(fù)了,導(dǎo)致集群出現(xiàn)了兩個(gè)master。 ?
"腦裂"成因:
-
網(wǎng)絡(luò)延遲導(dǎo)致誤判:集群間的網(wǎng)絡(luò)延遲導(dǎo)致一些節(jié)點(diǎn)訪問(wèn)不到master, 認(rèn)為master 掛掉了從而選舉出新的master,并對(duì)master上的分片和副本標(biāo)紅,分配新的主分片
-
主節(jié)點(diǎn)負(fù)載過(guò)高導(dǎo)致誤判:主節(jié)點(diǎn)的角色既為master又為data,訪問(wèn)量較大時(shí)可能會(huì)導(dǎo)致ES停止響應(yīng)造成大面積延遲,此時(shí)其他節(jié)點(diǎn)得不到主節(jié)點(diǎn)的響應(yīng)認(rèn)為主節(jié)點(diǎn)掛掉了,會(huì)重新選取主節(jié)點(diǎn)
-
內(nèi)存回收導(dǎo)致誤判:data 節(jié)點(diǎn)上的ES進(jìn)程占用的內(nèi)存較大,引發(fā)JVM的大規(guī)模內(nèi)存回收,造成ES進(jìn)程失去響應(yīng),從而長(zhǎng)時(shí)間沒(méi)ping通主節(jié)點(diǎn),導(dǎo)致誤判主節(jié)點(diǎn)下線
-
主節(jié)點(diǎn)故障。
解決方案:
-
調(diào)大超時(shí)時(shí)間減少誤判:discovery.zen.ping_timeout節(jié)點(diǎn)狀態(tài)的響應(yīng)時(shí)間,默認(rèn)為3s,可以適當(dāng)調(diào)大,如果master在該響應(yīng)時(shí)間的范圍內(nèi)沒(méi)有做出響應(yīng)應(yīng)答,判斷該節(jié)點(diǎn)已經(jīng)掛掉了。調(diào)大參數(shù)(如6s,discovery.zen.ping_timeout:6),可適當(dāng)減少誤判
-
選舉觸發(fā)條件:discovery.zen.minimum_master_nodes:(備選主節(jié)點(diǎn)數(shù)量/ 2) +1。該參數(shù)是用于控制選舉條件,只有(n / 2) +1個(gè)備選主節(jié)點(diǎn)認(rèn)為主節(jié)點(diǎn)故障才開(kāi)始選舉。在es7.0以后,已經(jīng)成為默認(rèn)配置,因此一般不會(huì)發(fā)生腦裂問(wèn)題
-
master和data分離:即master節(jié)點(diǎn)與data節(jié)點(diǎn)分離,限制角色
-
主節(jié)點(diǎn)配置為:node.master: true,node.data: false
-
從節(jié)點(diǎn)配置為:node.master: false,node.data: true ?
-
腦裂是由集群中的節(jié)點(diǎn)失聯(lián)導(dǎo)致的。
腦裂情況演示:?
例如一個(gè)集群中,主節(jié)點(diǎn)與其它節(jié)點(diǎn)失聯(lián):
此時(shí),node2和node3認(rèn)為node1宕機(jī)(但其實(shí)只是阻塞了),就會(huì)重新選主:
宕機(jī),即down機(jī)、死機(jī)。
指操作系統(tǒng)無(wú)法從一個(gè)嚴(yán)重系統(tǒng)錯(cuò)誤中恢復(fù)過(guò)來(lái),或系統(tǒng)硬件層面出問(wèn)題,以致系統(tǒng)長(zhǎng)時(shí)間無(wú)響應(yīng),而不得不重新啟動(dòng)計(jì)算機(jī)的現(xiàn)象。
當(dāng)node3當(dāng)選后,集群繼續(xù)對(duì)外提供服務(wù),node2和node3自成集群,node1自成集群,兩個(gè)集群數(shù)據(jù)不同步,出現(xiàn)數(shù)據(jù)差異。
當(dāng)網(wǎng)絡(luò)恢復(fù)后,因?yàn)榧褐杏袃蓚€(gè)master節(jié)點(diǎn),集群狀態(tài)的不一致,出現(xiàn)腦裂的情況:
解決腦裂?
解決腦裂的方案是,要求選票超過(guò) ( eligible節(jié)點(diǎn)數(shù)量 + 1 )/ 2 才能當(dāng)選為主,因此eligible節(jié)點(diǎn)數(shù)量最好是奇數(shù)。對(duì)應(yīng)配置項(xiàng)是discovery.zen.minimum_master_nodes,在es7.0以后,已經(jīng)成為默認(rèn)配置,因此一般不會(huì)發(fā)生腦裂問(wèn)題
例如:3個(gè)節(jié)點(diǎn)形成的集群,選票必須超過(guò) (3 + 1) / 2 ,也就是2票。node3得到node2和node3的選票,當(dāng)選為主。node1只有自己1票,沒(méi)有當(dāng)選。集群中依然只有1個(gè)主節(jié)點(diǎn),沒(méi)有出現(xiàn)腦裂。
4.2.3.小結(jié),四種節(jié)點(diǎn)類型
master eligible節(jié)點(diǎn)的作用是什么?
data節(jié)點(diǎn)的作用是什么?
coordinator節(jié)點(diǎn)的作用是什么?
4.3.集群分布式存儲(chǔ)
當(dāng)新增文檔時(shí),應(yīng)該保存到不同分片,保證數(shù)據(jù)均衡,那么coordinating node如何確定數(shù)據(jù)該存儲(chǔ)到哪個(gè)分片呢?
4.3.1.文檔存儲(chǔ)到分片測(cè)試
一個(gè)集群里有多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都是一個(gè)es實(shí)例,?每個(gè)節(jié)點(diǎn)保存了自己的分片和一個(gè)其他節(jié)點(diǎn)備份的分片。?
在9200端口的es插入三條數(shù)據(jù):
測(cè)試可以看到,三條數(shù)據(jù)分別在不同分片:
4.3.2.分片存儲(chǔ)原理
分布式新增如何確定分片??
elasticsearch會(huì)通過(guò)hash算法來(lái)計(jì)算文檔應(yīng)該存儲(chǔ)到哪個(gè)分片,跟文檔id和分片數(shù)量有關(guān):
說(shuō)明:
- _routing默認(rèn)是文檔的id
- 算法與分片數(shù)量有關(guān),因此索引庫(kù)一旦創(chuàng)建,分片數(shù)量不能修改!
新增文檔的流程如下:
解讀:
4.4.集群分布式查詢,協(xié)調(diào)節(jié)點(diǎn)的分散和聚集
elasticsearch的查詢分成兩個(gè)階段:
4.5.集群故障轉(zhuǎn)移
集群的master節(jié)點(diǎn)會(huì)監(jiān)控集群中的節(jié)點(diǎn)狀態(tài),如果發(fā)現(xiàn)有節(jié)點(diǎn)宕機(jī),會(huì)立即將宕機(jī)節(jié)點(diǎn)的分片數(shù)據(jù)遷移到其它節(jié)點(diǎn),確保數(shù)據(jù)安全,這個(gè)叫做故障轉(zhuǎn)移。
故障轉(zhuǎn)移流程演示:
1)例如一個(gè)集群結(jié)構(gòu)如圖:
現(xiàn)在,node1是主節(jié)點(diǎn),其它兩個(gè)節(jié)點(diǎn)是從節(jié)點(diǎn)。
2)突然,node1發(fā)生了故障:
宕機(jī)后的第一件事,需要重新選主,例如選中了node2:
node2成為主節(jié)點(diǎn)后,會(huì)檢測(cè)集群監(jiān)控狀態(tài),發(fā)現(xiàn):shard-1、shard-0沒(méi)有副本節(jié)點(diǎn)。因此需要將node1上的數(shù)據(jù)遷移到node2、node3:
實(shí)際演示:
現(xiàn)在有三個(gè)節(jié)點(diǎn),其中es01是主節(jié)點(diǎn)。
令es01宕機(jī)
docker-compose stop es01
此時(shí)es01節(jié)點(diǎn)上的1主分片和0副本分片沒(méi)了,主節(jié)點(diǎn)轉(zhuǎn)到es03,控制es01節(jié)點(diǎn)的數(shù)據(jù)遷移到es02和03。
故障轉(zhuǎn)移后,所有分片都有主分片和副本分片:
?
?再恢復(fù)es01,發(fā)現(xiàn)主節(jié)點(diǎn)es03遷移出兩個(gè)分片到es01:
docker-compose start es01
- master節(jié)點(diǎn):對(duì)CPU要求高,但是內(nèi)存要求低
- data節(jié)點(diǎn):對(duì)CPU和內(nèi)存要求都高
- coordinating節(jié)點(diǎn):對(duì)網(wǎng)絡(luò)帶寬、CPU要求高
- 參與集群選主
- 主節(jié)點(diǎn)可以管理集群狀態(tài)、管理分片信息、處理創(chuàng)建和刪除索引庫(kù)的請(qǐng)求
- 數(shù)據(jù)的CRUD
-
路由請(qǐng)求到其它節(jié)點(diǎn)
-
合并查詢到的結(jié)果,返回給用戶
- 1)新增一個(gè)id=1的文檔
- 2)對(duì)id做hash運(yùn)算,假如得到的是2,則應(yīng)該存儲(chǔ)到shard-2
- 3)shard-2的主分片在node3節(jié)點(diǎn),將數(shù)據(jù)路由到node3
- 4)保存文檔
- 5)同步給shard-2的副本replica-2,在node2節(jié)點(diǎn)
- 6)返回結(jié)果給coordinating-node節(jié)點(diǎn)
-
scatter phase:分散階段,coordinating node會(huì)把請(qǐng)求分發(fā)到每一個(gè)分片(只有data節(jié)點(diǎn)保存分片數(shù)據(jù))文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-415318.html
-
gather phase:聚集階段,coordinating node匯總data node的搜索結(jié)果,并處理為最終結(jié)果集返回給用戶文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-415318.html
到了這里,關(guān)于ElasticSearch基礎(chǔ)3——聚合、補(bǔ)全、集群。黑馬旅游檢索高亮+自定義分詞器+自動(dòng)補(bǔ)全+前后端消息同步的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!