国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

成都模板建站代理seo站長(zhǎng)工具下載

成都模板建站代理,seo站長(zhǎng)工具下載,可信網(wǎng)站 認(rèn)證規(guī)則,圖片設(shè)計(jì)制作哪個(gè)軟件好手機(jī)前言 elastic表示可伸縮,search表示查詢。所以es的核心即為查詢。通常情況下,我們的數(shù)據(jù)可以分為三類:結(jié)構(gòu)化數(shù)據(jù)、非結(jié)構(gòu)化數(shù)據(jù)、半結(jié)構(gòu)化數(shù)據(jù)。 結(jié)構(gòu)化數(shù)據(jù):一般會(huì)用特定的結(jié)構(gòu)來(lái)組織和管理數(shù)據(jù),表現(xiàn)為二維表結(jié)構(gòu)?!?article class="baidu_pl">

前言

elastic表示可伸縮,search表示查詢。所以es的核心即為查詢。通常情況下,我們的數(shù)據(jù)可以分為三類:結(jié)構(gòu)化數(shù)據(jù)、非結(jié)構(gòu)化數(shù)據(jù)、半結(jié)構(gòu)化數(shù)據(jù)。
結(jié)構(gòu)化數(shù)據(jù):一般會(huì)用特定的結(jié)構(gòu)來(lái)組織和管理數(shù)據(jù),表現(xiàn)為二維表結(jié)構(gòu)。這些數(shù)據(jù)信息一般是有關(guān)系的,所以可以保存到關(guān)系型數(shù)據(jù)庫(kù)如MySQL、Oracle中,并可以通過(guò)SQL語(yǔ)句來(lái)查詢。
– 優(yōu)點(diǎn):方便管理、方便查詢。
– 缺點(diǎn):擴(kuò)展結(jié)構(gòu)較為困難。
非結(jié)構(gòu)化數(shù)據(jù):無(wú)法用二維表結(jié)構(gòu)來(lái)表現(xiàn)的數(shù)據(jù)。如日志、文檔、報(bào)表、圖片、視頻等。這種數(shù)據(jù)維度廣、數(shù)據(jù)量大,所以存儲(chǔ)和查詢的成本較大。一般會(huì)將這種數(shù)據(jù)保存到noSQL數(shù)據(jù)庫(kù)當(dāng)中,如redis、mongoDB、HBase。這種數(shù)據(jù)庫(kù)一般以 K-V 結(jié)構(gòu)保存,通過(guò)key來(lái)查詢數(shù)據(jù),相對(duì)較快。
半結(jié)構(gòu)化數(shù)據(jù):將數(shù)據(jù)的結(jié)構(gòu)和內(nèi)容混在一起,沒(méi)有明確的區(qū)分。如XML,一般保存在redis、mongoDB、HBase中。
– 缺點(diǎn):不容易查詢內(nèi)容。
但是生活中很多情況下數(shù)據(jù)并非關(guān)系型結(jié)構(gòu)化的數(shù)據(jù),無(wú)法模糊查詢、也無(wú)法遍歷匹配。為了解決這種問(wèn)題,于是有了es。

一、ElasticSearch概述

The Elastic Stack包括ElasticSearch、Kibana、Beats和Logstash。能夠安全可靠的獲取任何來(lái)源、任何格式的數(shù)據(jù),然后實(shí)時(shí)地對(duì)數(shù)據(jù)進(jìn)行搜索、分析和可視化。ElasticSearch簡(jiǎn)稱ES,是一個(gè)開(kāi)源的高擴(kuò)展的分布式全文搜索引擎,是整個(gè)Elastic Stack技術(shù)棧的核心。它可以近乎實(shí)時(shí)的存儲(chǔ)、檢索數(shù)據(jù)。

全文搜索引擎:

可以理解為全棧搜索。如在csdn中,用戶可以寫一些文章, 其他用戶根據(jù)內(nèi)容詞匯、關(guān)鍵字等進(jìn)行搜索,查詢網(wǎng)站內(nèi)所有匹配的文章,并以列表的形式展現(xiàn)結(jié)果。傳統(tǒng)的數(shù)據(jù)庫(kù)進(jìn)行這樣的檢索時(shí)效率較低。即使進(jìn)行SQL的優(yōu)化,效果也不會(huì)有顯著變化。所以在生產(chǎn)環(huán)境中,這種常規(guī)的搜索方式效果較差。
這就需要我們采用專門用于全文搜索的搜索引擎。至于具體使用那種搜索引擎,需要考慮如下幾點(diǎn)進(jìn)行分析:

  • 除了搜索文本之外,還需要服務(wù)器來(lái)處理分析查詢
  • 搜索服務(wù)器也需要是集群,而且可擴(kuò)展
  • 對(duì)數(shù)據(jù)進(jìn)行大量的分析,統(tǒng)計(jì)出不同的指標(biāo)

二、ElasticSearch使用

Windows版本的ES安裝很簡(jiǎn)單,解壓即安裝完畢。解壓后,進(jìn)入/bin目錄,點(diǎn)擊elasticsearch.bat文件啟動(dòng)es服務(wù)。
在這里插入圖片描述

9300 端口為 ElasticSearch 集群間組件的通信端口
9200 端口為瀏覽器訪問(wèn)的http協(xié)議 RESTful 端口

如果直接通過(guò)瀏覽器向 ElasticSearch 發(fā)送請(qǐng)求,那么需要在發(fā)送的請(qǐng)求中包含 HTTP 標(biāo)準(zhǔn)的方法,而 HTTP 的大部分特性僅支持GETPOST方法。為了方便的進(jìn)行客戶端的訪問(wèn),可以使用Postman軟件。
Postman能夠發(fā)送任何類型的 HTTP 請(qǐng)求(GET, HEAD, POST, PUT…)不僅能夠表單提交,也可以附帶任何類型請(qǐng)求體。
在這里插入圖片描述

2.1 RESTful

es支持分布式、RESTful 風(fēng)格的搜索和分析,這就表示es允許采用RESTful風(fēng)格的方式發(fā)請(qǐng)求進(jìn)行軟件訪問(wèn)。RESTful(Representational State Transfer 資源狀態(tài)轉(zhuǎn)換)是一種軟件架構(gòu)風(fēng)格,比如 HTTP 就遵循了REST原則:
如在web中資源的唯一標(biāo)識(shí)是 URI(統(tǒng)一資源路徑)http://localhost:9200/test/test.txt 路徑中不包含對(duì)資源的操作,如增加、修改不應(yīng)該存在在路徑中。RESTful風(fēng)格架構(gòu)就要求遵循統(tǒng)一的接口原則,包含一組受限制的預(yù)定義操作,不論什么樣的資源都應(yīng)該通過(guò)使用相同的接口對(duì)資源進(jìn)行訪問(wèn)。這里的接口應(yīng)該符合標(biāo)準(zhǔn)HTTP方法,比如 GET、POST、PUT、DELETE、HEAD等請(qǐng)求。
路徑是對(duì)資源的定位,方法是對(duì)資源的操作。按照HTTP的方法暴露資源,那資源將具有安全性冪等性的特性。如 GET、HEAD都是安全的,無(wú)論訪問(wèn)多少次,都不會(huì)改變資源的狀態(tài)。PUT、DELETE都是冪等性的,無(wú)論對(duì)資源操作多少次,結(jié)果都是一樣的,后面的請(qǐng)求不會(huì)比第一回請(qǐng)求產(chǎn)生更多的影響。
所以當(dāng)我們采用RESTful風(fēng)格向es發(fā)出請(qǐng)求之后,es會(huì)返回相應(yīng),返回相應(yīng)的數(shù)據(jù)格式是JSON格式。我們可以在請(qǐng)求體中發(fā)送JSON格式的字符串給服務(wù)器,服務(wù)器拿到后做相應(yīng)的處理。所以es中數(shù)據(jù)的發(fā)送和數(shù)據(jù)的處理都是以JSON為標(biāo)準(zhǔn)格式的。
為什么采用JSON格式:因?yàn)镴SON格式更容易轉(zhuǎn)換成字符串在網(wǎng)絡(luò)中傳遞,而且識(shí)別會(huì)更加容易。

2.2 客戶端 Postman

如果直接通過(guò)瀏覽器向ES服務(wù)器發(fā)送請(qǐng)求,那么需要在發(fā)送的請(qǐng)求中包含HTTP標(biāo)準(zhǔn)的方法,而HTTP的大部分特性僅支持 GETPOST 方法。所以為了能夠方便的進(jìn)行客戶端的訪問(wèn),可以使用Postman軟件。Postman是一款網(wǎng)頁(yè)調(diào)試工具,能夠發(fā)送任何類型的HTTP請(qǐng)求(GET、HEAD、PSOT、PUT…)不僅能夠表單提交,且可以附帶任何類型請(qǐng)求體。

2.3 數(shù)據(jù)格式

ElasticSearch 是面向文檔型數(shù)據(jù)庫(kù),一條數(shù)據(jù)就是一個(gè)文檔。ElasticSearch 里存儲(chǔ)文檔數(shù)據(jù)和關(guān)系型數(shù)據(jù)庫(kù)MySQL存儲(chǔ)數(shù)據(jù)的概念類比如下:
在這里插入圖片描述
在關(guān)系型數(shù)據(jù)庫(kù)中,索引是為了優(yōu)化查詢?cè)O(shè)計(jì)的數(shù)據(jù)庫(kù)對(duì)象。沒(méi)有索引也能進(jìn)行檢索,只是速度會(huì)有所降低。而ES軟件專門用于全文檢索數(shù)據(jù),所以索引是整個(gè)搜索引擎中的關(guān)鍵。ES為了做到快速準(zhǔn)確的查詢,使用了一個(gè)特殊的概念來(lái)進(jìn)行數(shù)據(jù)的存儲(chǔ)和查詢。我們稱之為倒排索引。

正排(正向)索引
在這里插入圖片描述

通過(guò)文章編號(hào)快速查詢到文章內(nèi)容。我們將文章編號(hào)設(shè)置為主鍵,同時(shí)生成主鍵索引。通過(guò)主鍵索引快速關(guān)聯(lián)到對(duì)應(yīng)信息。
在正排索引中,如果想要查詢文章中包含哪些熱門詞匯,會(huì)比較麻煩。我們需要做模糊查詢,而且對(duì)每條數(shù)據(jù)都需要進(jìn)行遍歷,效率明顯下降。且查詢內(nèi)容的大小寫、時(shí)態(tài)等因素都會(huì)影響準(zhǔn)確率。此時(shí)需要換種方式來(lái)將索引和數(shù)據(jù)關(guān)聯(lián)。

倒排索引
在這里插入圖片描述
與以往的查詢方式相反(通過(guò)主鍵ID關(guān)聯(lián)文件內(nèi)容,再查詢關(guān)鍵字)。倒排索引是通過(guò)關(guān)鍵字查詢主鍵ID,再關(guān)聯(lián)文章內(nèi)容,查詢效率較快。
倒排索引中,強(qiáng)調(diào)關(guān)鍵字文檔編號(hào)的關(guān)聯(lián),表的作用反而不那么明顯。所以在 ES7.x 中,Type的概念被刪除。

2.2 HTTP操作

2.2.1 索引操作

1)創(chuàng)建索引

對(duì)比關(guān)系型數(shù)據(jù)庫(kù),創(chuàng)建索引就等同于創(chuàng)建數(shù)據(jù)庫(kù)。在MySQL中對(duì)數(shù)據(jù)進(jìn)行操作,需要知道連接的數(shù)據(jù)庫(kù)database和對(duì)應(yīng)的表,在es中叫Index索引。

在 Postman 中,向ES服務(wù)器發(fā) PUT 請(qǐng)求:http://127.0.0.1:9200/shopping
在這里插入圖片描述
acknowledge:true 響應(yīng)成功
index:shopping 當(dāng)前索引為shopping
PUT具有冪等性,所以發(fā)出同樣的請(qǐng)求,結(jié)果是一樣的。而POST不具有冪等性,兩次操作的結(jié)果可能不一樣。

PUT 和 PUSH 的區(qū)別:
PUT
PUT請(qǐng)求用于在Elasticsearch中創(chuàng)建或更新一個(gè)文檔。當(dāng)使用PUT請(qǐng)求時(shí),需要指定文檔的唯一標(biāo)識(shí)符(ID),然后提供文檔的內(nèi)容。如果指定的文檔ID已存在,Elasticsearch將更新該文檔;如果文檔ID不存在,它將創(chuàng)建一個(gè)新的文檔。
PUSH
Push是一種與索引或更新具有一定關(guān)聯(lián)的操作無(wú)關(guān)的API。它允許向特定字段添加新的元素。該API用于數(shù)組類型的字段,例如nested字段或array字段。使用Push可以向現(xiàn)有文檔字段添加新的元素,而不必使用GET和PUT來(lái)檢索和創(chuàng)建整個(gè)文檔。

2)查看所有索引

GET 獲取索引的相關(guān)信息
在這里插入圖片描述

在Postman中,向ES服務(wù)器發(fā)GET請(qǐng)求: http://127.0.0.1:9200/_cat/indices?v

3)查看單個(gè)索引

在Postman中,向服務(wù)器發(fā)GET請(qǐng)求:http://127.0.0.1:9200/shopping

{"shopping"【索引名】: { "aliases"【別名】: {},"mappings"【映射】: {},"settings"【設(shè)置】: {"index"【設(shè)置 - 索引】: {"creation_date"【設(shè)置 - 索引 - 創(chuàng)建時(shí)間】: "1614265373911","number_of_shards"【設(shè)置 - 索引 - 主分片數(shù)量】: "1","number_of_replicas"【設(shè)置 - 索引 - 副分片數(shù)量】: "1","uuid"【設(shè)置 - 索引 - 唯一標(biāo)識(shí)】: "eI5wemRERTumxGCc1bAk2A","version"【設(shè)置 - 索引 - 版本】: {"created": "7080099"},"provided_name"【設(shè)置 - 索引 - 名稱】: "shopping"}}}
}

4)刪除索引

在Postman中,向ES服務(wù)器發(fā)DELETE請(qǐng)求:http://127.0.0.1:9200/shopping
重新訪問(wèn)索引時(shí),服務(wù)器返回響應(yīng):索引不存在。

2.2.2 文檔操作

1)創(chuàng)建文檔

在Postman中,向ES服務(wù)器發(fā)POST請(qǐng)求:http://127.0.0.1:9200/shopping/_doc
注意,此處發(fā)送請(qǐng)求的方式必須是POST,不能是PUT,不然會(huì)發(fā)生錯(cuò)誤。這是由于數(shù)據(jù)創(chuàng)建成功之后,會(huì)返回一個(gè) id ,可以認(rèn)為是剛剛創(chuàng)建數(shù)據(jù)的標(biāo)識(shí),由es軟件隨機(jī)生成。所以同樣的請(qǐng)求多次執(zhí)行之后,返回的結(jié)果不一樣。
所以 POST 不是冪等性,但 PUT 操作必須是冪等性的,所以不能使用 PUT 操作。

{"title":"小米手機(jī)","category":"小米","images":"http://www.gulixueyuan.com/xm.jpg","price":3999.00
}

服務(wù)器相應(yīng)結(jié)果如下:

{"_index"【索引】: "shopping","_type"【類型-文檔】: "_doc","_id"【唯一標(biāo)識(shí)】: "Xhsa2ncBlvF_7lxyCE9G", #可以類比為 MySQL 中的主鍵,隨機(jī)生成"_version"【版本】: 1,"result"【結(jié)果】: "created", #這里的 create 表示創(chuàng)建成功"_shards"【分片】: {"total"【分片 - 總數(shù)】: 2,"successful"【分片 - 成功】: 1,"failed"【分片 - 失敗】: 0},"_seq_no": 0,"_primary_term": 1
}

自定義id:http://127.0.0.1:9200/shopping/_doc/1001
如果采用上面的方式提交兩次 PUSH 請(qǐng)求操作,那么該操作也是冪等性的,相當(dāng)于 PUT 操作。也就是說(shuō),如果增加數(shù)據(jù)時(shí)明確數(shù)據(jù)主鍵,那么請(qǐng)求方式也可以是PUT
或者,為了明確當(dāng)前的操作是新增,可以把_doc改為 _createhttp://127.0.0.1:9200/shopping/_create/1002

2)查看文檔

<1> 主鍵查詢

可以用 GET 的方式發(fā)送一條新的請(qǐng)求即可:http://127.0.0.1:9200/shopping/_doc/1001

<2> 全量查詢

上面的查詢語(yǔ)句中,1001類似主鍵,結(jié)果就類似于主鍵查詢的結(jié)果。
一個(gè)主鍵id只能獲取一條數(shù)據(jù)。如果想要查詢索引下面的所有數(shù)據(jù),可以采用:http://127.0.0.1:9200/shopping/_search

3)修改文檔

完全覆蓋性的修改,具有冪等性,可以使用PUT方法進(jìn)行操作。http://127.0.0.1:9200/shopping/_doc
在這里插入圖片描述

{"title":"小米手機(jī)","category":"小米","images":"http://www.gulixueyuan.com/xm.jpg","price":4999.00
}

4)修改字段

局部數(shù)據(jù)需要更新時(shí),由于每次更新的結(jié)果不一樣,所以不是冪等性,不能用PUT方式,只能用POST方式。http://127.0.0.1:9200/shopping/_update/1001

{"doc":{"title":"華為手機(jī)"}
}

在命令行中需要明確對(duì)誰(shuí)進(jìn)行修改,所以需要使用命令_update,如果使用_doc,可能會(huì)被認(rèn)為是新增。

5)刪除文檔(字段)

使用DELETE方法,http://127.0.0.1:9200/shopping/_doc/1001

2.2.3 映射操作

有的查詢可以分詞查詢,有的不能,必須全部匹配。在MySQL中,一個(gè)表的字段、類型、長(zhǎng)度信息都屬于表的結(jié)構(gòu)信息,在es中也有類似概念,稱為映射。
創(chuàng)建映射PUThttp://127.0.0.1:9200/user/_mapping

{"properties" : {"name" : {"type" : "text",	// 可以分詞"index" : "true"	// 可以被索引},"sex" : {"type" : "keyword",	// 不能分詞,必須完整匹配"index" : "true"},"tel" : {"type" : "keyword","index" : "false"	// 不能被索引},}
}

2.2.4 高級(jí)查詢

1)條件查詢

請(qǐng)求路徑:GEThttp://127.0.0.1:9200/shopping/_search?q=category:小米
請(qǐng)求體:GEThttp://127.0.0.1:9200/shopping/_search
【Body -> row -> JSON】

// 條件查詢
{"query" : {"match" : {"category" : "小米"}}
}// 全量查詢
{"query" : {"match_all" : {}}
}// 分頁(yè)查詢
{"query" : {"match" : {"category" : "小米"},"from" : 0,"size" : 2	//每頁(yè)查詢的數(shù)據(jù)2條}
}// 指定查詢字段
{"query" : {"match" : {"category" : "小米"},"from" : 0,"size" : 2,"_source" : ["title"]	// 指定title字段}
}// 查詢排序
{"query" : {"match" : {"category" : "小米"},"from" : 0,"size" : 2,"_source" : ["title"],"sort" : {"price" : {"order" : "desc"}}}
}// 多條件查詢 邏輯與&& = must,邏輯或|| = should
// 小米手機(jī),且價(jià)格=1999
{"query" : {"bool" : {"must" : [{"match" : {"catatory" : "小米"}},{"match" : {"price" : "1999.00"}}]}
}
// 小米或華為手機(jī)
{"query" : {"bool" : {"should" : [{"match" : {"catatory" : "小米"}},{"match" : {"catatory" : "華為"}}]}
}// 范圍查詢
{"query" : {"bool" : {"should" : [{"match" : {"catatory" : "小米"}},{"match" : {"catatory" : "華為"}},"filter" : {"range" : {"price" : {"gt" : 5000	// 價(jià)格大于5000}}}]}
}// 模糊查詢(全文檢索)
// 文字即使不正確,也能查詢出數(shù)據(jù)。保存文檔數(shù)據(jù)時(shí),es會(huì)對(duì)文檔進(jìn)行分詞拆解,并將拆解后的數(shù)據(jù)保存到倒排索引中。所以即使使用文字的一部分,也能檢索到數(shù)據(jù)。這種檢索方式就稱為全文檢索。
{"query" : {"match" : {"category" : "米"}}
}// 完全匹配
{"query" : {"match_phrase" : {"category" : "米"}}
}// 高亮顯示
{"query" : {"match_phrase" : {"category" : "米"}},"highliaht" : {"fields" : {"cayegory" : {}}}
}

在這里插入圖片描述

2)聚合查詢

// 聚合查詢
{"aggs" : {"price_group" : {	// 名稱,隨意起名"terms" : {	// 分組"field" : "price"	// 分組字段}}},"size" : 0	// 只返回聚合結(jié)果而不返回具體的文檔數(shù)據(jù)
}

這里的terms是一個(gè)聚合類型(aggregation type),用于對(duì)指定字段進(jìn)行分組。比如價(jià)格為“1999”的商品數(shù)量為1,價(jià)格為“2999”的商品數(shù)量為3。

terms可以換成其他聚合類型,如: “sum” (求和聚合):用于對(duì)指定字段的值進(jìn)行求和操作。它會(huì)計(jì)算指定字段值的總和。
"avg"(平均值聚合):用于對(duì)指定字段的值進(jìn)行平均值計(jì)算。它會(huì)計(jì)算指定字段值的平均值。
"max"(最大值聚合):用于查找指定字段的最大值。它會(huì)返回指定字段中的最大值。
"min"(最小值聚合):用于查找指定字段的最小值。它會(huì)返回指定字段中的最小值。
"count"(計(jì)數(shù)聚合):用于計(jì)算文檔數(shù)量。它會(huì)返回匹配指定條件的文檔數(shù)量。
"date_histogram"(日期直方圖聚合):用于按時(shí)間范圍對(duì)文檔進(jìn)行分組,并統(tǒng)計(jì)每個(gè)時(shí)間范圍內(nèi)的文檔數(shù)量。

2.2.5 Java API操作

準(zhǔn)備操作

pom依賴

<dependencies><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.8.0</version></dependency><!-- elasticsearch 的客戶端 --><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.8.0</version></dependency><!-- elasticsearch 依賴 2.x 的 log4j --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.8.2</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.8.2</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.9</version></dependency><!-- junit 單元測(cè)試 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>
</dependencies>

創(chuàng)建ES客戶端

// 創(chuàng)建客戶端對(duì)象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);
...
// 關(guān)閉客戶端連接
client.close();

注意:9200 端口為 Elasticsearch 的 Web 通信端口,localhost 為啟動(dòng) ES 服務(wù)的主機(jī)名

1)索引

1. 創(chuàng)建索引
// 創(chuàng)建索引 - 請(qǐng)求對(duì)象
CreateIndexRequest request = new CreateIndexRequest("user");// 發(fā)送請(qǐng)求,獲取響應(yīng)
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
boolean acknowledged = response.isAcknowledged();// 響應(yīng)狀態(tài)
System.out.println("操作狀態(tài) = " + acknowledged);
2. 查詢索引
// 查詢索引 - 請(qǐng)求對(duì)象
GetIndexRequest request = new GetIndexRequest("user");// 發(fā)送請(qǐng)求,獲取響應(yīng)
GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
System.out.println("aliases:"+response.getAliases());
System.out.println("mappings:"+response.getMappings());	// 查看索引結(jié)構(gòu)
System.out.println("settings:"+response.getSettings());	// 查看配置
3. 刪除索引
// 刪除索引 - 請(qǐng)求對(duì)象
DeleteIndexRequest request = new DeleteIndexRequest("user");
// 發(fā)送請(qǐng)求,獲取響應(yīng)
AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
// 操作結(jié)果
System.out.println("操作結(jié)果 : " + response.isAcknowledged());

2)文檔

1. 新增 / 批量新增
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);// 新增文檔 - 請(qǐng)求對(duì)象
IndexRequest request = new IndexRequest();
// 設(shè)置索引及唯一性標(biāo)識(shí)
request.index("user").id("1001");// 創(chuàng)建數(shù)據(jù)對(duì)象
User user = new User();
user.setName("zhangsan");
user.setAge(30);
user.setSex("男");// 向ES插入數(shù)據(jù),必須將數(shù)據(jù)轉(zhuǎn)換成JSON格式
ObjectMapper mapper = new ObjectMapper();
String userJson = mapper.writeValueAsString(user);
// 添加文檔數(shù)據(jù),數(shù)據(jù)格式為 JSON 格式
request.source(userJson, XContentType.JSON);
// 客戶端發(fā)送請(qǐng)求,獲取響應(yīng)對(duì)象
IndexResponse reponse = client.index(request, RequestOptions.DEFAULT);// 打印結(jié)果信息
System.out.println("_index:" + response.getIndex());
System.out.println("_id:" + response.getId());
System.out.println("_result:" + response.getResult());client.close();

批量新增

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);// 批量新增數(shù)據(jù)
BulkRequest request = new BulkRequest();
request.add(new IndexRequest().index("user").id("1001").source(XContentType.JSON, "name", "zhangsan"));
request.add(new IndexRequest().index("user").id("1002").source(XContentType.JSON, "name", "lisi"));// 客戶端發(fā)送請(qǐng)求,獲取響應(yīng)對(duì)象
BulkResponse responses = client.bult(request, RequestOptions.DEFAULT);//打印結(jié)果信息
System.out.println("took:" + responses.getTook());
System.out.println("items:" + responses.getItems());client.close();
2. 修改
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);// 新增文檔 - 請(qǐng)求對(duì)象
UpdateRequest request = new UpdatRequest();
// 設(shè)置索引及唯一性標(biāo)識(shí)
request.index("user").id("1001");
request.dox(XContentType.JSON, "sex", "女");// 客戶端發(fā)騷那個(gè)請(qǐng)求,獲取響應(yīng)對(duì)象
UpdatResponse reponse = client.update(request, RequestOptions.DEFAULT);// 打印結(jié)果信息
System.out.println("_result:" + response.getResult());client.close();
3. 查詢
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);//1.創(chuàng)建請(qǐng)求對(duì)象
GetRequest request = new GetRequest().index("user").id("1001");//2.客戶端發(fā)送請(qǐng)求,獲取響應(yīng)對(duì)象
GetResponse response = client.get(request, RequestOptions.DEFAULT);//3.打印結(jié)果信息
System.out.println("_index:" + response.getIndex());
System.out.println("_type:" + response.getType());
System.out.println("_id:" + response.getId());
System.out.println("source:" + response.getSourceAsString());client.close();
4. 刪除 / 批量刪除
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);//1.創(chuàng)建請(qǐng)求對(duì)象
DeleteRequest request = new DeleteRequest().index("user").id("1001");//2.客戶端發(fā)送請(qǐng)求,獲取響應(yīng)對(duì)象
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);//3.打印結(jié)果信息
System.out.println("source:" + response.toString());client.close();

批量刪除

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);// 批量刪除數(shù)據(jù)
BulkRequest request = new BulkRequest();
request.add(new DeleteRequest().index("user").id("1001"));
request.add(new DeleteRequest().index("user").id("1002"));// 客戶端發(fā)送請(qǐng)求,獲取響應(yīng)對(duì)象
BulkResponse responses = client.bult(request, RequestOptions.DEFAULT);//打印結(jié)果信息
System.out.println("took:" + responses.getTook());
System.out.println("items:" + responses.getItems());client.close();
5. 高級(jí)查詢
全量查詢
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"))
);// 1. 全量查詢:matchAllQuery
SearchRequest request = new SearchRequest();
request.indices("user");request.source(new SearchSourceBuilder.query(QueryBuilders.matchAllQuery()));SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 2. 條件查詢:termQuery
SearchRequest request = new SearchRequest();
request.indices("user");request.source(new SearchSourceBuilder.query(QueryBuilders.termQuery("age", 30)));SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 3. 分頁(yè)查詢
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder.query(QueryBuilders.matchAllQuery());
builder.from(0);
builder.size(2);
request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 4. 查詢排序
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder.query(QueryBuilders.matchAllQuery());
builder.sort("age", SortOrder.DESC);
request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 5. 過(guò)濾字段
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder.query(QueryBuilders.matchAllQuery());
String excludes = {"age"};
String indludes = {"name"}
builder.fetchSource(indludes, excludes );
request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 6. 組合查詢
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();boolQueryBuilder.must(QueryBuilders.matchQuery("age", 30));
boolQueryBuilder.must(QueryBuilders.matchQuery("sex", "男"));builder.query(boolQueryBuilder);request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 7. 范圍查詢
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder();
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age");rangeQueryBuilder.gte(30)
rangeQueryBuilder.lte(40);builder.query(rangeQueryBuilder);request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 8. 模糊查詢
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.fuzzyQuery("name","wangwu").fuzziness(Fuzziness.ONE));request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 9. 高亮查詢
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder();
TermsQueryBuilders termsQueryBuilder = QueryBuilders.termQuery("name", "zhangsan");HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font color='red'>");
highlightBuilder.postTags("<font color='red'>");
highlightBuilder.field("name");
// 設(shè)置高亮構(gòu)建對(duì)象
builder.highlighter(highlightBuilder);
builder.query(termsQueryBuilder);request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 10. 聚合查詢
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder();AggregationBuilder aggregationBuilder = AggregationBuilders.max("max_age").filed("age");
builder.aggregation(aggregationBuilder );builder.query(termsQueryBuilder);request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}// 11. 分組查詢
SearchRequest request = new SearchRequest();
request.indices("user");SearchSourceBuilder builder = new SearchSourceBuilder();AggregationBuilder aggregationBuilder = AggregationBuilders.terms("ageGroup").filed("age");
builder.aggregation(aggregationBuilder );builder.query(termsQueryBuilder);request.source(builder);
SearchResponse response= client.search(request, RequestOptions.DEFAULT);SearchHits hits = response.getHits();System.out.println("source:" + response.getHits());for( SearchHit hit : hits ) {System.out.println(hit.getSourceAsString());
}client.close();

三、進(jìn)階

3.1 概念

1. 索引(Index)

一個(gè)索引就是一個(gè)擁有幾分相似特征的文檔的集合。比如說(shuō),你可以有一個(gè)客戶數(shù)據(jù)的索引,另一個(gè)產(chǎn)品目錄的索引,還有一個(gè)訂單數(shù)據(jù)的索引。一個(gè)索引由一個(gè)名字來(lái)標(biāo)識(shí)(必須全部是小寫字母),并且當(dāng)我們要對(duì)這個(gè)索引中的文檔進(jìn)行索引、搜索、更新和刪除的時(shí)候,都要使用到這個(gè)名字。在一個(gè)集群中,可以定義任意多的索引。
能搜索的數(shù)據(jù)必須索引,這樣的好處是可以提高查詢速度,比如:新華字典前面的目錄就是索引的意思,目錄可以提高查詢速度。

Elasticsearch 索引的精髓:一切設(shè)計(jì)都是為了提高搜索的性能。

2. 類型(Type)

7.x 之后默認(rèn)不再支持自定義索引類型(默認(rèn)類型為:_doc)

3. 文檔(Document)

一個(gè)文檔是一個(gè)可被索引的基礎(chǔ)信息單元,也就是一條數(shù)據(jù)。
比如:你可以擁有某一個(gè)客戶的文檔,某一個(gè)產(chǎn)品的一個(gè)文檔,當(dāng)然,也可以擁有某個(gè)訂單的一個(gè)文檔。文檔以 JSON(Javascript Object Notation)格式來(lái)表示,而 JSON 是一個(gè)到處存在的互聯(lián)網(wǎng)數(shù)據(jù)交互格式。
在一個(gè) index/type 里面,可以存儲(chǔ)任意多的文檔。

4. 字段(Filed)

相當(dāng)于是數(shù)據(jù)表的字段,對(duì)文檔數(shù)據(jù)根據(jù)不同屬性進(jìn)行的分類標(biāo)識(shí)。

5. 映射(Mapping)

mapping 是處理數(shù)據(jù)的方式和規(guī)則方面做一些限制,如:某個(gè)字段的數(shù)據(jù)類型、默認(rèn)值、分析器、是否被索引等等。這些都是映射里面可以設(shè)置的,其它就是處理 ES 里面數(shù)據(jù)的一些使用規(guī)則設(shè)置也叫做映射,按著最優(yōu)規(guī)則處理數(shù)據(jù)對(duì)性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能對(duì)性能更好。

6. 分片(Shards)

可以理解成MySQL數(shù)據(jù)庫(kù)中的分表。
一個(gè)索引可以存儲(chǔ)超出單個(gè)節(jié)點(diǎn)硬件限制的大量數(shù)據(jù)。比如,一個(gè)具有 10 億文檔數(shù)據(jù)的索引占據(jù) 1TB 的磁盤空間,而任一節(jié)點(diǎn)都可能沒(méi)有這樣大的磁盤空間。或者單個(gè)節(jié)點(diǎn)處理搜索請(qǐng)求,響應(yīng)太慢。為了解決這個(gè)問(wèn)題,Elasticsearch 提供了將索引劃分成多份的能力,每一份就稱之為分片。當(dāng)你創(chuàng)建一個(gè)索引的時(shí)候,你可以指定你想要的分片的數(shù)量。每個(gè)分片本身也是一個(gè)功能完善并且獨(dú)立的“索引”,這個(gè)“索引”可以被放置到集群中的任何節(jié)點(diǎn)上。

分片很重要,主要有兩方面的原因:
1)允許你水平分割 / 擴(kuò)展你的內(nèi)容容量。
2)允許你在分片之上進(jìn)行分布式的、并行的操作,進(jìn)而提高性能/吞吐量。
至于一個(gè)分片怎樣分布,它的文檔怎樣聚合和搜索請(qǐng)求,是完全由 Elasticsearch 管理的,對(duì)于作為用戶來(lái)說(shuō),這些都是透明的,無(wú)需過(guò)分關(guān)心。

7. 副本(Replicas)

在一個(gè)網(wǎng)絡(luò) / 云的環(huán)境里,失敗隨時(shí)都可能發(fā)生,在某個(gè)分片/節(jié)點(diǎn)不知怎么的就處于離線狀態(tài),或者由于任何原因消失了,這種情況下,有一個(gè)故障轉(zhuǎn)移機(jī)制是非常有用并且是強(qiáng)烈推薦的。為此目的,Elasticsearch 允許你創(chuàng)建分片的一份或多份拷貝,這些拷貝叫做復(fù)制分片(副本)。

復(fù)制分片之所以重要,有兩個(gè)主要原因:
在分片/節(jié)點(diǎn)失敗的情況下,提供了高可用性。因?yàn)檫@個(gè)原因,注意到復(fù)制分片從不與原/主要(original/primary)分片置于同一節(jié)點(diǎn)上是非常重要的。
擴(kuò)展你的搜索量/吞吐量,因?yàn)樗阉骺梢栽谒械母北旧喜⑿羞\(yùn)行。

分配分片和副本

{"settings" : {"number_of_shards" : 3,"number_of_replicas" : 1}
}

8. 分配(Allocation)

將分片分配給某個(gè)節(jié)點(diǎn)的過(guò)程,包括分配主分片或者副本。如果是副本,還包含從主分片復(fù)制數(shù)據(jù)的過(guò)程。這個(gè)過(guò)程是由 master 節(jié)點(diǎn)完成的。

3.2 系統(tǒng)框架

在這里插入圖片描述
一個(gè)運(yùn)行中的 Elasticsearch 實(shí)例稱為一個(gè)節(jié)點(diǎn),而集群是由一個(gè)或者多個(gè)擁有相同cluster.name 配置的節(jié)點(diǎn)組成, 它們共同承擔(dān)數(shù)據(jù)和負(fù)載的壓力。當(dāng)有節(jié)點(diǎn)加入集群中或者從集群中移除節(jié)點(diǎn)時(shí),集群將會(huì)重新平均分布所有的數(shù)據(jù)。
作為用戶,我們可以將請(qǐng)求發(fā)送到集群中的任何節(jié)點(diǎn) ,包括主節(jié)點(diǎn)。 每個(gè)節(jié)點(diǎn)都知道任意文檔所處的位置,并且能夠?qū)⑽覀兊恼?qǐng)求直接轉(zhuǎn)發(fā)到存儲(chǔ)我們所需文檔的節(jié)點(diǎn)。 無(wú)論我們將請(qǐng)求發(fā)送到哪個(gè)節(jié)點(diǎn),它都能負(fù)責(zé)從各個(gè)包含我們所需文檔的節(jié)點(diǎn)收集回?cái)?shù)據(jù),并將最終結(jié)果返回給客戶端。 Elasticsearch 當(dāng)一個(gè)節(jié)點(diǎn)被選舉成為主節(jié)點(diǎn)時(shí), 它將負(fù)責(zé)管理集群范圍內(nèi)的所有變更,例如增加、刪除索引,或者增加、刪除節(jié)點(diǎn)等。 而主節(jié)點(diǎn)并不需要涉及到文檔級(jí)別的變更和搜索等操作,所以當(dāng)集群只擁有一個(gè)主節(jié)點(diǎn)的情況下,即使流量的增加它也不會(huì)成為瓶頸。 任何節(jié)點(diǎn)都可以成為主節(jié)點(diǎn)。我們的示例集群就只有一個(gè)節(jié)點(diǎn),所以它同時(shí)也成為了主節(jié)點(diǎn)。對(duì)這一切的管理都是透明的。

寫流程

新建、索引和刪除 請(qǐng)求都是 操作, 必須在主分片上面完成之后才能被復(fù)制到相關(guān)的副本分片。
在這里插入圖片描述

參數(shù):consistency
含義:consistency,即一致性。在默認(rèn)設(shè)置下,即使僅僅是在試圖執(zhí)行一個(gè)_寫_操作之前,主分片都會(huì)要求 必須要有 規(guī)定數(shù)量(quorum)(或者換種說(shuō)法,也即必須要有大多數(shù))的分片副本處于活躍可用狀態(tài),才會(huì)去執(zhí)行_寫_操作(其中分片副本可以是主分片或者副本分片)。這是為了避免在發(fā)生網(wǎng)絡(luò)分區(qū)故障(network partition)的時(shí)候進(jìn)行_寫_操作,進(jìn)而導(dǎo)致數(shù)據(jù)不一致。_規(guī)定數(shù)量_即:
int( (primary + number_of_replicas) / 2 ) + 1
consistency 參數(shù)的值可以設(shè)為 one (只要主分片狀態(tài) ok 就允許執(zhí)行_寫_操作),all(必須要主分片和所有副本分片的狀態(tài)沒(méi)問(wèn)題才允許執(zhí)行_寫_操作), 或quorum 。默認(rèn)值為 quorum , 即大多數(shù)的分片副本狀態(tài)沒(méi)問(wèn)題就允許執(zhí)行_寫_操作。
注意,規(guī)定數(shù)量 的計(jì)算公式中 number_of_replicas 指的是在索引設(shè)置中的設(shè)定副本分片數(shù),而不是指當(dāng)前處理活動(dòng)狀態(tài)的副本分片數(shù)。如果你的索引設(shè)置中指
定了當(dāng)前索引擁有三個(gè)副本分片,那規(guī)定數(shù)量的計(jì)算結(jié)果即:
int( (primary + 3 replicas) / 2 ) + 1 = 3
如果此時(shí)你只啟動(dòng)兩個(gè)節(jié)點(diǎn),那么處于活躍狀態(tài)的分片副本數(shù)量就達(dá)不到規(guī)定數(shù)量,也因此您將無(wú)法索引和刪除任何文檔。

參數(shù):timeout
含義:如果沒(méi)有足夠的副本分片會(huì)發(fā)生什么? Elasticsearch 會(huì)等待,希望更多的分片出現(xiàn)。默認(rèn)情況下,它最多等待 1 分鐘。 如果你需要,你可以使用 timeout 參數(shù)使它更早終止: 100 100 毫秒,30s 是 30 秒。

讀流程在這里插入圖片描述

更新流程

部分更新一個(gè)文檔結(jié)合了先前說(shuō)明的讀取和寫入流程,步驟如下:
在這里插入圖片描述

  1. 客戶端向 Node 1 發(fā)送更新請(qǐng)求。
  2. 它將請(qǐng)求轉(zhuǎn)發(fā)到主分片所在的 Node 3 。
  3. Node 3 從主分片檢索文檔,修改 _source 字段中的 JSON ,并且嘗試重新索引主分片的文檔。如果文檔已經(jīng)被另一個(gè)進(jìn)程修改,它會(huì)重試步驟 3 ,超過(guò) retry_on_conflict 次后放棄。
  4. 如果 Node 3 成功地更新文檔,它將新版本的文檔并行轉(zhuǎn)發(fā)到 Node 1 和 Node 2 上的副本分片,重新建立索引。一旦所有副本分片都返回成功, Node 3 向協(xié)調(diào)節(jié)點(diǎn)也返回成功,協(xié)調(diào)節(jié)點(diǎn)向客戶端返回成功。

分片原理

傳統(tǒng)的數(shù)據(jù)庫(kù)每個(gè)字段存儲(chǔ)單個(gè)值,但這對(duì)全文檢索并不夠。文本字段中的每個(gè)單詞需
要被搜索,對(duì)數(shù)據(jù)庫(kù)意味著需要單個(gè)字段有索引多值的能力。最好的支持是一個(gè)字段多個(gè)值
需求的數(shù)據(jù)結(jié)構(gòu)是倒排索引

倒排索引

Elasticsearch 使用一種稱為倒排索引的結(jié)構(gòu),它適用于快速的全文搜索。
所謂的正向索引,就是搜索引擎會(huì)將待搜索的文件都對(duì)應(yīng)一個(gè)文件 ID,搜索時(shí)將這個(gè)ID 和搜索關(guān)鍵字進(jìn)行對(duì)應(yīng),形成 K-V 對(duì),然后對(duì)關(guān)鍵字進(jìn)行統(tǒng)計(jì)計(jì)數(shù)。
在這里插入圖片描述

但是互聯(lián)網(wǎng)上收錄在搜索引擎中的文檔的數(shù)目是個(gè)天文數(shù)字,這樣的索引結(jié)構(gòu)根本無(wú)法滿足實(shí)時(shí)返回排名結(jié)果的要求。所以,搜索引擎會(huì)將正向索引重新構(gòu)建為倒排索引,即把文件ID對(duì)應(yīng)到關(guān)鍵詞的映射轉(zhuǎn)換為關(guān)鍵詞到文件ID的映射,每個(gè)關(guān)鍵詞都對(duì)應(yīng)著一系列的文件,這些文件中都出現(xiàn)這個(gè)關(guān)鍵詞。一個(gè)倒排索引由文檔中所有不重復(fù)詞的列表構(gòu)成,對(duì)于其中每個(gè)詞,有一個(gè)包含它的文檔列表。
在這里插入圖片描述

文檔搜索

早期的全文檢索會(huì)為整個(gè)文檔集合建立一個(gè)很大的倒排索引并將其寫入到磁盤。 一旦新的索引就緒,舊的就會(huì)被其替換,這樣最近的變化便可以被檢索到。
倒排索引被寫入磁盤后是 不可改變 的:它永遠(yuǎn)不會(huì)修改。

動(dòng)態(tài)更新索引

如何在保留不變性的前提下實(shí)現(xiàn)倒排索引的更新?答案是: 用更多的索引。通過(guò)增加新的補(bǔ)充索引來(lái)反映新近的修改,而不是直接重寫整個(gè)倒排索引。每一個(gè)倒排索引都會(huì)被輪流查詢到,從最早的開(kāi)始查詢完后再對(duì)結(jié)果進(jìn)行合并。

文檔分析

首先我們通過(guò) Postman 發(fā)送 GET 請(qǐng)求查詢分詞效果

# GET http://localhost:9200/_analyze
{"text":"測(cè)試單詞"
}

ES 的默認(rèn)分詞器無(wú)法識(shí)別中文中測(cè)試、單詞這樣的詞匯,而是簡(jiǎn)單的將每個(gè)字拆完分為一個(gè)詞。這樣的結(jié)果顯然不符合我們的使用要求,所以我們需要下載 ES 對(duì)應(yīng)版本的中文分詞器。我們這里采用 IK 中文分詞器,下載地址為:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0
將解壓后的后的文件夾放入 ES 根目錄下的 plugins 目錄下,重啟 ES 即可使用。我們這次加入新的查詢參數(shù)"analyzer":"ik_max_word

# GET http://localhost:9200/_analyze
{"text":"測(cè)試單詞","analyzer":"ik_max_word"
}// ik_max_word:會(huì)將文本做最細(xì)粒度的拆分
// ik_smart:會(huì)將文本做最粗粒度的拆分

ES 中也可以進(jìn)行擴(kuò)展詞匯,首先進(jìn)入 ES 根目錄中的 plugins 文件夾下的 ik 文件夾,進(jìn)入 config 目錄,創(chuàng)建 custom.dic文件,寫入 單詞 。同時(shí)打開(kāi) IKAnalyzer.cfg.xml 文件,將新建的 custom.dic 配置其中,重啟 ES 服務(wù)器。
在這里插入圖片描述

自定義分析器

# PUT http://localhost:9200/my_index
{"settings": {"analysis": {"char_filter": {"&_to_and": {"type": "mapping","mappings": [ "&=> and "]}},"filter": {"my_stopwords": {"type": "stop","stopwords": [ "the", "a" ]}},"analyzer": {"my_analyzer": {"type": "custom","char_filter": [ "html_strip", "&_to_and" ],"tokenizer": "standard","filter": [ "lowercase", "my_stopwords" ]}}
}}}

結(jié)果展示:

{"tokens": [{"token": "quick","start_offset": 4,"end_offset": 9,"type": "<ALPHANUM>","position": 1},{"token": "and","start_offset": 10,"end_offset": 11,"type": "<ALPHANUM>","position": 2},{"token": "brown","start_offset": 12,"end_offset": 17,"type": "<ALPHANUM>","position": 3},{"token": "fox","start_offset": 18,"end_offset": 21,"type": "<ALPHANUM>","position": 4}]
}

文檔處理

當(dāng)我們使用 index API 更新文檔 ,可以一次性讀取原始文檔,做我們的修改,然后重新索引 整個(gè)文檔 。 最近的索引請(qǐng)求將獲勝:無(wú)論最后哪一個(gè)文檔被索引,都將被唯一存儲(chǔ)在 Elasticsearch 中。如果其他人同時(shí)更改這個(gè)文檔,他們的更改將丟失。在數(shù)據(jù)庫(kù)領(lǐng)域中,有兩種方法通常被用來(lái)確保并發(fā)更新時(shí)變更不會(huì)丟失:悲觀并發(fā)控制、樂(lè)觀并發(fā)控制。

樂(lè)觀并發(fā)控制
當(dāng)我們之前討論 index ,GET 和 delete 請(qǐng)求時(shí),我們指出每個(gè)文檔都有一個(gè)_version(版本)號(hào),當(dāng)文檔被修改時(shí)版本號(hào)遞增。 Elasticsearch 使用這個(gè) version 號(hào)來(lái)確保變更以正確順序得到執(zhí)行。如果舊版本的文檔在新版本之后到達(dá),它可以被簡(jiǎn)單的忽略。我們可以利用 version 號(hào)來(lái)確保應(yīng)用中相互沖突的變更不會(huì)導(dǎo)致數(shù)據(jù)丟失。我們通過(guò)指定想要修改文檔的 version 號(hào)來(lái)達(dá)到這個(gè)目的。 如果該版本不是當(dāng)前版本號(hào),我們的請(qǐng)求將會(huì)失敗。
老的版本 es 使用 version,但是新版本不支持了,會(huì)報(bào)下面的錯(cuò)誤,提示我們用 if_seq_noif_primary_term
POST: http://127.0.0.1:9200/shopping/_doc/1001?if_seq_no=0&if_primary_term=0

外部系統(tǒng)版本控制
如果你的主數(shù)據(jù)庫(kù)已經(jīng)有了版本號(hào) — 或一個(gè)能作為版本號(hào)的字段值比如 timestamp?—那么你就可以在 Elasticsearch 中通過(guò)增加 version_type=external 到查詢字符串的方式重用這些相同的版本號(hào), 版本號(hào)必須是大于零的整數(shù), 且小于 9.2E+18?— 一個(gè) Java 中 long類型的正值。
外部版本號(hào)的處理方式和我們之前討論的內(nèi)部版本號(hào)的處理方式有些不同,Elasticsearch 不是檢查當(dāng)前 _version 和請(qǐng)求中指定的版本號(hào)是否相同, 而是檢查當(dāng)前_version 是否 小于 指定的版本號(hào)。 如果請(qǐng)求成功,外部的版本號(hào)作為文檔的新 _version 進(jìn)行存儲(chǔ)。
POST: http://127.0.0.1:9200/shopping/_doc/1001?version=3&version_type=external

四、框架集成

Spring Data 框架基礎(chǔ)

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.6.RELEASE</version><relativePath/></parent><groupId>com.atguigu.es</groupId><artifactId>springdata-elasticsearch</artifactId><version>1.0</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId></dependency></dependencies>
</project>

增加配置文件

在 resources 目錄中增加 application.properties 文件

# es 服務(wù)地址
elasticsearch.host=127.0.0.1
# es 服務(wù)端口
elasticsearch.port=9200
# 配置日志級(jí)別,開(kāi)啟 debug 日志
logging.level.com.atguigu.es=debug

SpringBoot 主程序

package com.atguigu.es;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SpringDataElasticSearchMainApplication {public static void main(String[] args) {SpringApplication.run(SpringDataElasticSearchMainApplication.class,args);}
}

數(shù)據(jù)實(shí)體類

package com.atguigu.es;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Document(indexName = "product", shards = 3, replicas = 1)
public class Product {@Idprivate Long id;//商品唯一標(biāo)識(shí)@Filed(type = FieldType.Text)private String title;//商品名稱@Filed(type = FieldType.Keyword)private String category;//分類名稱@Filed(type = FieldType.Keyword)private Double price;//商品價(jià)格@Filed(type = FieldType.Keyword, index = false)	// 不做索引關(guān)聯(lián)private String images;//圖片地址
}

配置類

package com.atguigu.es;import lombok.Data;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
// 關(guān)聯(lián)配置文件
@ConfigurationProperties(prefix = "elasticsearch")
@Configuration
@Data
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {private String host ;private Integer port ;//重寫父類方法@Overridepublic RestHighLevelClient elasticsearchClient() {RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);return restHighLevelClient;}
}

DAO 數(shù)據(jù)訪問(wèn)對(duì)象

package com.atguigu.es;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductDao extends ElasticsearchRepository<Product,Long> {
}

索引操作

package com.atguigu.es;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESIndexTest {//注入 ElasticsearchRestTemplate@Autowiredprivate ElasticsearchRestTemplate elasticsearchRestTemplate;//創(chuàng)建索引并增加映射配置@Testpublic void createIndex(){//創(chuàng)建索引,系統(tǒng)初始化會(huì)自動(dòng)創(chuàng)建索引System.out.println("創(chuàng)建索引");}@Testpublic void deleteIndex(){//創(chuàng)建索引,系統(tǒng)初始化會(huì)自動(dòng)創(chuàng)建索引boolean flg = elasticsearchRestTemplate.deleteIndex(Product.class);System.out.println("刪除索引 = " + flg);}
}

文檔操作

package com.atguigu.es;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;import java.util.ArrayList;
import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESProductDaoTest {@Autowiredprivate ProductDao productDao;/*** 新增*/@Testpublic void save(){Product product = new Product();product.setId(2L);product.setTitle("華為手機(jī)");product.setCategory("手機(jī)");product.setPrice(2999.0);product.setImages("http://www.atguigu/hw.jpg");productDao.save(product);}//修改@Testpublic void update(){Product product = new Product();product.setId(1L);product.setTitle("小米 2 手機(jī)");product.setCategory("手機(jī)");product.setPrice(9999.0);product.setImages("http://www.atguigu/xm.jpg");productDao.save(product);}//根據(jù) id 查詢@Testpublic void findById(){Product product = productDao.findById(1L).get();System.out.println(product);}//查詢所有@Testpublic void findAll(){Iterable<Product> products = productDao.findAll();for (Product product : products) {System.out.println(product);}}//刪除@Testpublic void delete(){Product product = new Product();product.setId(1L);productDao.delete(product);}//批量新增@Testpublic void saveAll(){List<Product> productList = new ArrayList<>();for (int i = 0; i < 10; i++) {Product product = new Product();product.setId(Long.valueOf(i));product.setTitle("["+i+"]小米手機(jī)");product.setCategory("手機(jī)");product.setPrice(1999.0+i);product.setImages("http://www.atguigu/xm.jpg");productList.add(product);}productDao.saveAll(productList);}//分頁(yè)查詢@Testpublic void findByPageable(){//設(shè)置排序(排序方式,正序還是倒序,排序的 id)Sort sort = Sort.by(Sort.Direction.DESC,"id");int currentPage=0;//當(dāng)前頁(yè),第一頁(yè)從 0 開(kāi)始,1 表示第二頁(yè)int pageSize = 5;//每頁(yè)顯示多少條//設(shè)置查詢分頁(yè)PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);//分頁(yè)查詢Page<Product> productPage = productDao.findAll(pageRequest);for (Product Product : productPage.getContent()) {System.out.println(Product);}}
}

文檔搜索

package com.atguigu.es;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataESSearchTest {@Autowiredprivate ProductDao productDao;/*** term 查詢* search(termQueryBuilder) 調(diào)用搜索方法,參數(shù)查詢構(gòu)建器對(duì)象*/@Testpublic void termQuery(){TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米");Iterable<Product> products = productDao.search(termQueryBuilder);for (Product product : products) {System.out.println(product);}}/*** term 查詢加分頁(yè)*/@Testpublic void termQueryByPage(){int currentPage= 0 ;int pageSize = 5;//設(shè)置查詢分頁(yè)PageRequest pageRequest = PageRequest.of(currentPage, pageSize);TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小米");Iterable<Product> products = productDao.search(termQueryBuilder,pageRequest);for (Product product : products) {System.out.println(product);}}
}

Spark Streaming - 集成

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.atguigu.es</groupId><artifactId>sparkstreaming-elasticsearch</artifactId><version>1.0</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.apache.spark</groupId><artifactId>spark-core_2.12</artifactId><version>3.0.0</version></dependency><dependency><groupId>org.apache.spark</groupId><artifactId>spark-streaming_2.12</artifactId><version>3.0.0</version></dependency><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.8.0</version></dependency><!-- elasticsearch 的客戶端 --><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.8.0</version></dependency><!-- elasticsearch 依賴 2.x 的 log4j --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-api</artifactId><version>2.8.2</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.8.2</version></dependency>
<!-- <dependency>-->
<!-- <groupId>com.fasterxml.jackson.core</groupId>-->
<!-- <artifactId>jackson-databind</artifactId>-->
<!-- <version>2.11.1</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; junit 單元測(cè)試 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>junit</groupId>-->
<!-- <artifactId>junit</artifactId>-->
<!-- <version>4.12</version>-->
<!-- </dependency>--></dependencies>
</project>

功能實(shí)現(xiàn)

package com.atguigu.esimport org.apache.http.HttpHost
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.ReceiverInputDStream
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.elasticsearch.action.index.IndexRequest
import org.elasticsearch.client.indices.CreateIndexRequest
import org.elasticsearch.client.{RequestOptions, RestClient, RestHighLevelClient}
import org.elasticsearch.common.xcontent.XContentTypeimport java.util.Date
object SparkStreamingESTest {def main(args: Array[String]): Unit = {val sparkConf = new SparkConf().setMaster("local[*]").setAppName("ESTest")val ssc = new StreamingContext(sparkConf, Seconds(3))val ds: ReceiverInputDStream[String] = ssc.socketTextStream("localhost", 9999)ds.foreachRDD(rdd => {data => {new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200)))// 設(shè)置索引及唯一性標(biāo)識(shí)val ss = data.split(" ")// 新增文檔 - 請(qǐng)求對(duì)象val request = new IndexRequest()request.index("product").id(ss(0))val json = s"""| { "data" : "${ss(1)}"}|""".stripMargin// 添加文檔數(shù)據(jù),數(shù)據(jù)格式為 JSON 格式request.source(json, XContentType.JSON)// 客戶端發(fā)送請(qǐng)求,獲取響應(yīng)對(duì)象val response = client.index(request, RequestOptions.DEFAULT)System.out.println("_index:" + response.getIndex());System.out.println("_id:" + response.getId());System.out.println("_result:" + response.getResult());client.close()}})ssc.start()ssc.awaitTermination()}
}

Flink - 集成

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.atguigu.es</groupId><artifactId>flink-elasticsearch</artifactId><version>1.0</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.apache.flink</groupId><artifactId>flink-scala_2.12</artifactId><version>1.12.0</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-scala_2.12</artifactId><version>1.12.0</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-clients_2.12</artifactId><version>1.12.0</version></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-connector-elasticsearch7_2.11</artifactId><version>1.12.0</version></dependency><!-- jackson --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.11.1</version></dependency></dependencies>
</project>

功能實(shí)現(xiàn)

package com.atguigu.es;
import org.apache.flink.api.common.functions.RuntimeContext;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.elasticsearch.ElasticsearchSinkFunction;
import org.apache.flink.streaming.connectors.elasticsearch.RequestIndexer;
import org.apache.flink.streaming.connectors.elasticsearch7.ElasticsearchSink;
import org.apache.http.HttpHost;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.Requests;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class FlinkElasticsearchSinkTest {public static void main(String[] args) throws Exception {// 構(gòu)建Flink環(huán)境對(duì)象StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();// Source:數(shù)據(jù)的輸入DataStreamSource<String> source = env.socketTextStream("localhost", 9999);// 使用ESBuilder構(gòu)建輸出List<HttpHost> hosts = new ArrayList<>();hosts.add(new HttpHost("localhost", 9200, "http"));ElasticsearchSink.Builder<String> esBuilder = new ElasticsearchSink.Builder<>(hosts, new ElasticsearchSinkFunction<String>() {@Overridepublic void process(String s, RuntimeContext runtimeContext, RequestIndexer requestIndexer){Map<String, String> jsonMap = new HashMao<>();jsonMap.put("data", s);IndexRequest indexRequest = Requests.indexRequest();indexRequest.index("flink-index");indexRequest.id("9001");indexRequest.source(jsonMap);requestIndexer.add(indexRequest);}})// Sink:數(shù)據(jù)的輸出esBuilder.setBuilFlushMaxActions(1);source.addSink(esSinkBuilder.build());env.execute("flink-es");}
}

五、優(yōu)化

5.1 硬件選擇

ES重度使用磁盤,磁盤能處理的吞吐量越大,節(jié)點(diǎn)久越穩(wěn)定。優(yōu)化磁盤I/O的技巧:

使用SSD。SSD(Solid State Drive 固態(tài)硬盤)相對(duì)于傳統(tǒng)的機(jī)械硬盤(HDD),SSD采用閃存存儲(chǔ)技術(shù),沒(méi)有機(jī)械部件,因此速度更快、耐用性更高。
使用RAID 0。RAID(Redundant Array of Independent Disks)是一種數(shù)據(jù)存儲(chǔ)技術(shù),用于將多個(gè)物理硬盤組合成一個(gè)邏輯卷,以提高數(shù)據(jù)的性能、容錯(cuò)能力或同時(shí)兼具兩者。RAID 0,也被稱為條帶化(striping),是RAID級(jí)別中的一種。它通過(guò)將數(shù)據(jù)分散存儲(chǔ)在多個(gè)硬盤上,以提高讀寫性能。在RAID 0中,數(shù)據(jù)被分成多個(gè)固定大小的塊,并按順序分配到各個(gè)硬盤上。因此,當(dāng)讀取或?qū)懭霐?shù)據(jù)時(shí),可以同時(shí)從多個(gè)硬盤中讀取或?qū)懭?#xff0c;大大提高了數(shù)據(jù)傳輸速度。
使用多塊硬盤,并允許ES通過(guò)多個(gè)path.data目錄配置把數(shù)據(jù)條帶化分配到他們上面。
不要使用遠(yuǎn)程掛載的存儲(chǔ)。遠(yuǎn)程掛載的存儲(chǔ)是指通過(guò)網(wǎng)絡(luò)連接將遠(yuǎn)程存儲(chǔ)設(shè)備(通常是網(wǎng)絡(luò)存儲(chǔ)設(shè)備)連接到本地計(jì)算機(jī),并將其作為本地計(jì)算機(jī)的一個(gè)目錄或驅(qū)動(dòng)器來(lái)使用。比如 NFS 或者 SMB/CIFS。

5.2 分片策略

分片和副本的設(shè)計(jì)為 ES 提供了支持分布式和故障轉(zhuǎn)移的特性,但分片和副本不是可以無(wú)限分配的。分片在設(shè)置好之后是不能修改的,由于路由計(jì)算規(guī)則,數(shù)據(jù)的存儲(chǔ)位置是可以通過(guò)計(jì)算得到的,一旦分片動(dòng)態(tài)變化,可能會(huì)導(dǎo)致數(shù)據(jù)查詢不到。所以分片一定是在創(chuàng)建索引時(shí)久設(shè)定好的。

合理設(shè)置分片數(shù)

設(shè)計(jì)原則:
每個(gè)分片占用的硬盤容量不超過(guò) ES 的最大 JVM 的堆空間設(shè)置(一般設(shè)置不超過(guò) 32G,參考下文
的 JVM 設(shè)置原則),因此,如果索引的總?cè)萘吭?500G 左右,那分片大小在 16 個(gè)左右即可;當(dāng)然,
最好同時(shí)考慮原則 2。
考慮一下 node 數(shù)量,一般一個(gè)節(jié)點(diǎn)有時(shí)候就是一臺(tái)物理機(jī),如果分片數(shù)過(guò)多,大大超過(guò)了節(jié)點(diǎn)數(shù),
很可能會(huì)導(dǎo)致一個(gè)節(jié)點(diǎn)上存在多個(gè)分片,一旦該節(jié)點(diǎn)故障,即使保持了 1 個(gè)以上的副本,同樣有可能
會(huì)導(dǎo)致數(shù)據(jù)丟失,集群無(wú)法恢復(fù)。所以, 一般都設(shè)置分片數(shù)不超過(guò)節(jié)點(diǎn)數(shù)的 3 倍。
主分片,副本和節(jié)點(diǎn)最大數(shù)之間數(shù)量,我們分配的時(shí)候可以參考以下關(guān)系:
節(jié)點(diǎn)數(shù)<=主分片數(shù)(副本數(shù)+1)*

推遲分片策略

對(duì)于節(jié)點(diǎn)瞬時(shí)中斷的問(wèn)題,默認(rèn)情況,集群會(huì)等待一分鐘來(lái)查看節(jié)點(diǎn)是否會(huì)重新加入,如果這個(gè)節(jié)點(diǎn)在此期間重新加入,重新加入的節(jié)點(diǎn)會(huì)保持其現(xiàn)有的分片數(shù)據(jù),不會(huì)觸發(fā)新的分片分配。這樣就可以減少 ES 在自動(dòng)再平衡可用分片時(shí)所帶來(lái)的極大開(kāi)銷。
通過(guò)修改參數(shù) delayed_timeout ,可以延長(zhǎng)再均衡的時(shí)間,可以全局設(shè)置也可以在索引級(jí)別進(jìn)行修改:

PUT /_all/_settings 
{"settings": {"index.unassigned.node_left.delayed_timeout": "5m" }
}

5.3 路由選擇

當(dāng)我們查詢文檔的時(shí)候,Elasticsearch 如何知道一個(gè)文檔應(yīng)該存放到哪個(gè)分片中呢?它其實(shí)是通過(guò)下面這個(gè)公式來(lái)計(jì)算出來(lái):
shard = hash(routing) % number_of_primary_shards
routing 默認(rèn)值是文檔的 id,也可以采用自定義值,比如用戶 id。

5.4 寫入速度優(yōu)化

針對(duì)于搜索性能要求不高,但是對(duì)寫入要求較高的場(chǎng)景,我們需要盡可能的選擇恰當(dāng)寫優(yōu)化策略。綜合來(lái)說(shuō),可以考慮以下幾個(gè)方面來(lái)提升寫索引的性能:

加大 Translog Flush ,目的是降低 Iops、Writeblock。
增加 Index Refresh 間隔,目的是減少 Segment Merge 的次數(shù)。
調(diào)整 Bulk 線程池和隊(duì)列。
優(yōu)化節(jié)點(diǎn)間的任務(wù)分布。
優(yōu)化 Lucene 層的索引建立,目的是降低 CPU 及 IO。

批量數(shù)據(jù)提交

ES 提供了 Bulk API 支持批量操作,當(dāng)我們有大量的寫任務(wù)時(shí),可以使用 Bulk 來(lái)進(jìn)行批量寫入。
通用的策略如下:Bulk 默認(rèn)設(shè)置批量提交的數(shù)據(jù)量不能超過(guò) 100M。數(shù)據(jù)條數(shù)一般是根據(jù)文檔的大小和服務(wù)器性能而定的,但是單次批處理的數(shù)據(jù)大小應(yīng)從 5MB~15MB 逐漸增加,當(dāng)性能沒(méi)有提升時(shí),把這個(gè)數(shù)據(jù)量作為最大值。

優(yōu)化存儲(chǔ)設(shè)備

ES 是一種密集使用磁盤的應(yīng)用,在段合并的時(shí)候會(huì)頻繁操作磁盤,所以對(duì)磁盤要求較高,當(dāng)磁盤速度提升之后,集群的整體性能會(huì)大幅度提高。

合理使用合并

Lucene 以段的形式存儲(chǔ)數(shù)據(jù)。當(dāng)有新的數(shù)據(jù)寫入索引時(shí),Lucene 就會(huì)自動(dòng)創(chuàng)建一個(gè)新的段。
隨著數(shù)據(jù)量的變化,段的數(shù)量會(huì)越來(lái)越多,消耗的多文件句柄數(shù)及 CPU 就越多,查詢效率就會(huì)下降。
由于 Lucene 段合并的計(jì)算量龐大,會(huì)消耗大量的 I/O,所以 ES 默認(rèn)采用較保守的策略,讓后臺(tái)定期進(jìn)行段合并。

減少refresh的次數(shù)

Lucene 在新增數(shù)據(jù)時(shí),采用了延遲寫入的策略,默認(rèn)情況下索引的 refresh_interval 為1 秒。
Lucene 將待寫入的數(shù)據(jù)先寫到內(nèi)存中,超過(guò) 1 秒(默認(rèn))時(shí)就會(huì)觸發(fā)一次 Refresh,然后 Refresh 會(huì)把內(nèi)存中的的數(shù)據(jù)刷新到操作系統(tǒng)的文件緩存系統(tǒng)中。
如果我們對(duì)搜索的實(shí)效性要求不高,可以將 Refresh 周期延長(zhǎng),例如 30 秒。
這樣還可以有效地減少段刷新次數(shù),但這同時(shí)意味著需要消耗更多的 Heap 內(nèi)存。

加大 Flush 設(shè)置

Flush 的主要目的是把文件緩存系統(tǒng)中的段持久化到硬盤,當(dāng) Translog 的數(shù)據(jù)量達(dá)到512MB 或者 30 分鐘時(shí),會(huì)觸發(fā)一次 Flush。
index.translog.flush_threshold_size 參數(shù)的默認(rèn)值是 512MB,我們進(jìn)行修改。
增加參數(shù)值意味著文件緩存系統(tǒng)中可能需要存儲(chǔ)更多的數(shù)據(jù),所以我們需要為操作系統(tǒng)的文件緩存系統(tǒng)留下足夠的空間。

減少副本的數(shù)量

ES 為了保證集群的可用性,提供了 Replicas(副本)支持,然而每個(gè)副本也會(huì)執(zhí)行分析、索引及可能的合并過(guò)程,所以 Replicas 的數(shù)量會(huì)嚴(yán)重影響寫索引的效率。
當(dāng)寫索引時(shí),需要把寫入的數(shù)據(jù)都同步到副本節(jié)點(diǎn),副本節(jié)點(diǎn)越多,寫索引的效率就越慢。
如果我們需要大批量進(jìn)行寫入操作,可以先禁止Replica復(fù)制,設(shè)置index.number_of_replicas: 0 關(guān)閉副本。在寫入完成后,Replica 修改回正常的狀態(tài)。

5.5 內(nèi)存設(shè)置

如果有一個(gè) 64G 內(nèi)存的機(jī)器, ES 堆內(nèi)存的分配需要滿足以下兩個(gè)原則:

1. 不要超過(guò)物理內(nèi)存的 50%:Lucene 的設(shè)計(jì)目的是把底層 OS 里的數(shù)據(jù)緩存到內(nèi)存中。
Lucene 的段是分別存儲(chǔ)到單個(gè)文件中的,這些文件都是不會(huì)變化的,所以很利于緩存,同時(shí)操作系統(tǒng)也會(huì)把這些段文件緩存起來(lái),以便更快的訪問(wèn)。
如果我們?cè)O(shè)置的堆內(nèi)存過(guò)大,Lucene 可用的內(nèi)存將會(huì)減少,就會(huì)嚴(yán)重影響降低 Lucene 的全文本查詢性能。
2.堆內(nèi)存的大小最好不要超過(guò) 32GB:在 Java 中,所有對(duì)象都分配在堆上,然后有一個(gè) Klass Pointer 指針指向它的類元數(shù)據(jù)。
這個(gè)指針在 64 位的操作系統(tǒng)上為 64 位,64 位的操作系統(tǒng)可以使用更多的內(nèi)存(2^ 64)。在 32 位的系統(tǒng)上為 32 位,32 位的操作系統(tǒng)的最大尋址空間為 4GB(2^32)。
但是 64 位的指針意味著更大的浪費(fèi),因?yàn)槟愕闹羔槺旧泶罅?。浪費(fèi)內(nèi)存不算,更糟糕的是,更大的指針在主內(nèi)存和緩存器(例如 LLC, L1 等)之間移動(dòng)數(shù)據(jù)的時(shí)候,會(huì)占用更多的帶寬。

最終我們都會(huì)采用 31 G 設(shè)置:-Xms 31g,-Xmx 31g

5.6 重要配置

參數(shù)名參數(shù)值說(shuō)明
cluster.nameelasticsearch配置 ES 的集群名稱,默認(rèn)值是 ES,建議改成與所存數(shù)據(jù)相關(guān)的名稱,ES 會(huì)自動(dòng)發(fā)現(xiàn)在同一網(wǎng)段下的集群名稱相同的節(jié)點(diǎn)
node.namenode-1集群中的節(jié)點(diǎn)名,在同一個(gè)集群中不能重復(fù)。節(jié)點(diǎn)的名稱一旦設(shè)置,就不能再改變了。當(dāng)然,也可以設(shè) 置 成 服 務(wù) 器 的 主 機(jī) 名 稱 , 例 如node.name:${HOSTNAME}。
node.mastertrue指定該節(jié)點(diǎn)是否有資格被選舉成為 Master 節(jié)點(diǎn),默認(rèn)是 True,如果被設(shè)置為 True,則只是有資格成為Master 節(jié)點(diǎn),具體能否成為 Master 節(jié)點(diǎn),需要通過(guò)選舉產(chǎn)生。
node.datatrue指定該節(jié)點(diǎn)是否存儲(chǔ)索引數(shù)據(jù),默認(rèn)為 True。數(shù)據(jù)的增、刪、改、查都是在 Data 節(jié)點(diǎn)完成的。
index.number_of_shards1設(shè)置都索引分片個(gè)數(shù),默認(rèn)是 1 片。也可以在創(chuàng)建索引時(shí)設(shè)置該值,具體設(shè)置為多大都值要根據(jù)數(shù)據(jù)量的大小來(lái)定。如果數(shù)據(jù)量不大,則設(shè)置成 1 時(shí)效率最高
index.number_of_replicas1設(shè)置默認(rèn)的索引副本個(gè)數(shù),默認(rèn)為 1 個(gè)。副本數(shù)越多,集群的可用性越好,但是寫索引時(shí)需要同步的數(shù)據(jù)越多。
transport.tcp.compress1設(shè)置在節(jié)點(diǎn)間傳輸數(shù)據(jù)時(shí)是否壓縮,默認(rèn)為 False,不壓縮
discovery.zen.minimum_master_nodestrue設(shè)置在選舉 Master 節(jié)點(diǎn)時(shí)需要參與的最少的候選主節(jié)點(diǎn)數(shù),默認(rèn)為 1。如果使用默認(rèn)值,則當(dāng)網(wǎng)絡(luò)不穩(wěn)定時(shí)有可能會(huì)出現(xiàn)腦裂。合理的數(shù)值為 (master_eligible_nodes/2)+1 ,其中
master_eligible_nodes 表示集群中的候選主節(jié)點(diǎn)數(shù)
discovery.zen.ping.timeout1設(shè)置在集群中自動(dòng)發(fā)現(xiàn)其他節(jié)點(diǎn)時(shí) Ping 連接的超時(shí)時(shí)間,默認(rèn)為 3 秒。在較差的網(wǎng)絡(luò)環(huán)境下需要設(shè)置得大一點(diǎn),防止因誤判該節(jié)點(diǎn)的存活狀態(tài)而導(dǎo)致分片的轉(zhuǎn)移
http://aloenet.com.cn/news/29753.html

相關(guān)文章:

  • 木工支模價(jià)格明細(xì)表搜索引擎優(yōu)化的流程是什么
  • 自己做網(wǎng)站哪種好做搜索引擎優(yōu)化的策略主要有
  • 網(wǎng)站建設(shè)江門游戲代理加盟
  • 河南平臺(tái)網(wǎng)站建設(shè)價(jià)位百度網(wǎng)站網(wǎng)址是多少
  • 深圳定制網(wǎng)站制作費(fèi)用百度智能建站系統(tǒng)
  • 網(wǎng)站開(kāi)發(fā)技術(shù)實(shí)驗(yàn)教程seo推廣優(yōu)化的方法
  • 漢中門戶網(wǎng)工程招標(biāo)杭州seo網(wǎng)站哪家好
  • 高端網(wǎng)站建設(shè)費(fèi)用深圳網(wǎng)絡(luò)推廣公司哪家好
  • 個(gè)人接做網(wǎng)站多少錢app優(yōu)化方案
  • 汕頭有建網(wǎng)站公司嗎seo案例
  • 公司和網(wǎng)站備案查詢密碼網(wǎng)絡(luò)營(yíng)銷和傳統(tǒng)營(yíng)銷的區(qū)別有哪些
  • 宣傳片視頻西安seo排名
  • wordpress采集網(wǎng)頁(yè)文章安卓?jī)?yōu)化大師app下載
  • 工作總結(jié)ppt模板免費(fèi)寧波優(yōu)化推廣選哪家
  • 桂林做網(wǎng)站的公司seo是哪里
  • 購(gòu)物網(wǎng)站開(kāi)發(fā)技術(shù)河北網(wǎng)站優(yōu)化公司
  • 網(wǎng)站風(fēng)格類型百度流量統(tǒng)計(jì)
  • 為什么做電子商務(wù)網(wǎng)站的原因中國(guó)建設(shè)網(wǎng)官方網(wǎng)站
  • 制作一個(gè)買股票的網(wǎng)站怎么做seo比較好的公司
  • 網(wǎng)站畢業(yè)設(shè)計(jì)怎么做網(wǎng)絡(luò)營(yíng)銷推廣活動(dòng)有哪些
  • 合伙做網(wǎng)站怎么分配股權(quán)各地疫情最新消息
  • 大連小型網(wǎng)站建設(shè)關(guān)鍵的近義詞
  • 做本地團(tuán)購(gòu)網(wǎng)站游戲網(wǎng)站交換友情鏈接
  • 合肥網(wǎng)站建設(shè)找佳達(dá)百度seo服務(wù)方案
  • 廣西網(wǎng)站建設(shè)開(kāi)發(fā)外包市場(chǎng)營(yíng)銷方案范文5篇
  • 泗陽(yáng)做網(wǎng)站公司競(jìng)價(jià)托管就選微競(jìng)價(jià)
  • 網(wǎng)站logo用什么做百度網(wǎng)站打不開(kāi)
  • 網(wǎng)站批量做httpsseo優(yōu)化實(shí)訓(xùn)報(bào)告
  • 深圳租房建設(shè)局網(wǎng)站天天外鏈官網(wǎng)
  • 淮安建設(shè)工程協(xié)會(huì)網(wǎng)站查詢站長(zhǎng)之家 seo查詢