1. 概述
ElasticSearch是基于lucence的全文搜索引擎呻待,用java語言作為開發(fā)打月,可以直接集成到項目中,使用json/xml的方式通過http來索引數(shù)據(jù)蚕捉!
特點:
①:基于lucene
②:使用簡單
③:支持分布式
④:使用Restful API
⑤:支持多種客戶端
⑥:可以處理PB級的數(shù)據(jù)
⑦:實時文件存儲奏篙、速度快、性能高
⑧:支持集群
⑨:處理json格式數(shù)據(jù)
2. ES安裝
2.1:安裝服務(wù)
ES服務(wù)只依賴于JDK迫淹,推薦使用JDK1.7+
① 下載ES安裝包
官方下載地址:https://www.elastic.co/downloads/elasticsearch
② 運(yùn)行ES
bin/elasticsearch.bat
③ 驗證
2.2:ES交互方式
ES和所有客戶端的交互都是使用JSON格式的數(shù)據(jù)秘通!
其他所有程序語言都可以使用RESTful API,通過9200端口的與ES進(jìn)行通信敛熬,java通過9300端口交互肺稀!
2.3: 輔助管理工具Kibana5
① Kibana5.2.2下載地址:https://www.elastic.co/downloads/kibana
② 解壓并編輯config/kibana.yml,設(shè)置elasticsearch.url的值為已啟動的ES
③ 啟動Kibana5 : bin\kibana.bat
④ 默認(rèn)訪問地址:http://localhost:5601
Discover:可視化查詢分析器
Visualize:統(tǒng)計分析圖表
Dashboard:自定義主面板(添加圖表)
Timelion:Timelion是一個kibana時間序列展示組件(暫時不用)
Dev Tools :Console(同CURL/POSTER应民,操作ES代碼工具话原,代碼提示,很方便)
Management:管理索引庫(index)诲锹、已保存的搜索和可視化結(jié)果(save objects)繁仁、設(shè)置 kibana 服務(wù)器屬性。
3. Restful風(fēng)格
Restful是一種面向資源的架構(gòu)風(fēng)格归园,用名詞描述資源黄虱,用動詞描述操作!
例如:
獲取id為1的用戶:
GET /user/1
獲取全部用戶:
GET /users
刪除id為1的用戶:
DELETE /user/1
添加用戶:
PUT /user
修改用戶:
POST /user
使用Restful的優(yōu)點:
①:透明性蔓倍,暴露資源存在悬钳。
②:充分利用 HTTP 協(xié)議本身語義。
③:無狀態(tài)偶翅,這點非常重要。在調(diào)用一個接口(訪問碉渡、操作資源)的時候聚谁,可以不用考慮上下文,不用考慮當(dāng)前狀態(tài)滞诺,極大的降低了復(fù)雜度形导。
3. ES相關(guān)概念
(1)Near Realtime(NRT)近實時:ES從錄入數(shù)據(jù)到可以被搜索可以達(dá)到秒級別环疼,延遲很小朵耕!
(2)Index:索引庫炫隶,包含一堆有相似結(jié)構(gòu)的文檔數(shù)據(jù),就好比Mysql中的某一個數(shù)據(jù)庫阎曹!
(3)**Type:類型 **每個索引庫里都可以有一個或多個type伪阶,一個type下的document,都有相同的field处嫌,就好比Mysql中的某一張表栅贴!
(4)Document&field:文檔,es 中的最小數(shù)據(jù)單元熏迹,一個document就是一條數(shù)據(jù)檐薯,好比Mysql中的某一張表中的某一行,一個Document可以有多個filed注暗,好比Mysql中某一張表中的某一列坛缕!
(5)Cluster:集群,一個集群包含多個節(jié)點捆昏,可以通過配置指定哪些節(jié)點屬于哪一個集群赚楚!
(6)Node:節(jié)點,集群中的一個節(jié)點屡立,節(jié)點也有一個名稱(默認(rèn)是隨機(jī)分配的)直晨,節(jié)點名稱很重要(在執(zhí)行運(yùn)維管理操作的時候),默認(rèn)節(jié)點會去加入一個名稱為“elasticsearch”的集群膨俐,如果直接啟動一堆節(jié)點勇皇,那么它們會自動組成一個elasticsearch集群,當(dāng)然一個節(jié)點也可以組成一個elasticsearch集群
(7)shard(分片):單臺機(jī)器無法存儲大量數(shù)據(jù)焚刺,es可以將一個索引中的數(shù)據(jù)切分為多個shard敛摘,分布在多臺服務(wù)器上存儲。有了shard就可以橫向擴(kuò)展乳愉,存儲更多數(shù)據(jù)兄淫,讓搜索和分析等操作分布到多臺服務(wù)器上去執(zhí)行,提升吞吐量和性能蔓姚。每個shard都是一個lucene index捕虽。
(8)replica(復(fù)制品):任何一個服務(wù)器隨時可能故障或宕機(jī),此時shard可能就會丟失坡脐,因此可以為每個shard創(chuàng)建多個replica副本泄私。replica可以在shard故障時提供備用服務(wù),保證數(shù)據(jù)不丟失,多個replica還可以提升搜索操作的吞吐量和性能晌端。primary shard(建立索引時一次設(shè)置捅暴,不能修改,默認(rèn)5個)咧纠,replica shard(隨時修改數(shù)量蓬痒,默認(rèn)1個),默認(rèn)每個索引10個shard漆羔,5個primary shard梧奢,5個replica shard,最小的高可用配置钧椰,是2臺服務(wù)器粹断。
4. ES集群
為什么需要集群:
解決高并發(fā)
解決海量數(shù)據(jù)存儲
解決單點故障
ES節(jié)點類型
默認(rèn)情況下,elasticsearch集群中每個節(jié)點都可以是主節(jié)點嫡霞,即可存儲數(shù)據(jù)也可提供查詢服務(wù)瓶埋,身兼數(shù)職,如果不配置它的角色诊沪,在高數(shù)據(jù)和高并發(fā)的場景下养筒,如果該節(jié)點宕機(jī),則數(shù)據(jù)就會分散不完整端姚!
配置 | 值 | 解釋 |
---|---|---|
node.master | true | 是否是主節(jié)點 |
node.data | true | 是否存儲數(shù)據(jù) |
- 主節(jié)點
node.master=true,代表該節(jié)點有成為主資格晕粪,主節(jié)點的主要職責(zé)是和集群操作相關(guān)的內(nèi)容,如創(chuàng)建或刪除索引渐裸,跟蹤哪些節(jié)點是群集的一部分巫湘,并決定哪些分片分配給相關(guān)的節(jié)點。一般會把主節(jié)點和數(shù)據(jù)節(jié)點分開昏鹃,node.master=true , node.data=false
- 數(shù)據(jù)節(jié)點
node.data=true,數(shù)據(jù)節(jié)點主要是存儲索引數(shù)據(jù)的節(jié)點尚氛,主要對文檔進(jìn)行增刪改查操作,聚合操作等,數(shù)據(jù)節(jié)點對CPU,IO,內(nèi)存要求較高洞渤,優(yōu)化節(jié)點的時候需要做狀態(tài)監(jiān)控阅嘶,資源不夠時要做節(jié)點擴(kuò)充。配置:node.master=false,node.data=true
- 負(fù)載均衡節(jié)點
當(dāng)主節(jié)點和數(shù)據(jù)節(jié)點配置都設(shè)置為false的時候载迄,該節(jié)點只能處理路由請求讯柔,處理搜索,分發(fā)索引操作等护昧,從本質(zhì)上來說該客戶節(jié)點表現(xiàn)為智能負(fù)載平衡器魂迄。配置:mode.master=false,mode.data=false
最佳實踐
master節(jié)點:三臺及以上,負(fù)責(zé)維護(hù)集群的狀態(tài)
data節(jié)點:負(fù)責(zé)存儲數(shù)據(jù)
client節(jié)點(node.master: false node.data: false):負(fù)責(zé)處理用戶請求惋耙,轉(zhuǎn)發(fā)請求极祸,負(fù)載均衡慈格!
5. ES集群理解
shard&replica機(jī)制
- 一個index索引庫包含多個shard(分片)怠晴,一個shard是最小的存儲單元
- replica shard是primary shard的副本,負(fù)責(zé)容錯蒜田,讀寫分離!
- primary shard的數(shù)量在創(chuàng)建索引的時候就固定了冲粤,replica shard的數(shù)量可以隨時修改
- primary shard的默認(rèn)數(shù)量是5,replica默認(rèn)是1厢呵,默認(rèn)有10個shard,5個primary shard傀顾,5個replica shard
- primary shard不能和自己的replica shard放在同一個節(jié)點上襟铭,如果該節(jié)點宕機(jī),primary shard和replica shard數(shù)據(jù)都會丟失短曾,起不到容錯的作用
- 增減節(jié)點寒砖,shard會自動分配
- 一個Document只可能存在一個primary shard及其對應(yīng)的replica shard上,不可能存在多個primary shard上
圖解Shard分配
a.單node環(huán)境下創(chuàng)建index
? 單node環(huán)境下嫉拐,創(chuàng)建一個index哩都,有3個primary shard,3個replica shard
[圖片上傳失敗...(image-df49c1-1582342886239)]
- 這個時候婉徘,只會將3個primary shard分配到僅有的一個node上去漠嵌,另外3個replica shard是無法分配的
- 集群status是yellow
- 集群可以正常工作,但是一旦出現(xiàn)節(jié)點宕機(jī)盖呼,數(shù)據(jù)全部丟失儒鹿,而且集群不可用,無法承接任何請求
b.兩個node環(huán)境下創(chuàng)建index
? 2個node環(huán)境下塌计,創(chuàng)建一個index, 3個primary shard挺身,3個replica shard
[圖片上傳失敗...(image-d6cb6f-1582342886240)]
c.擴(kuò)容極限,提升容錯
? 如何讓性能達(dá)到更優(yōu)?
- 每個Node更少的Shard,每個Shard資源跟充沛,性能更高
- 擴(kuò)容極限:6個shard(3 primary锌仅,3 replica)章钾,最多擴(kuò)容到6臺機(jī)器,每個shard可以占用單臺服務(wù)器的所有資源热芹,性能最好
- 超出擴(kuò)容極限贱傀,動態(tài)修改replica數(shù)量,9個shard(3primary伊脓,6 replica)府寒,擴(kuò)容到9臺機(jī)器魁衙,比3臺機(jī)器時,擁有3倍的讀吞吐量
容錯機(jī)制-Master選舉
-
master node宕機(jī)株搔,自動進(jìn)行master選舉剖淀, - Red
當(dāng)某個PrimaryShard (主分片)宕機(jī),這個PrimaryShard的某個ReplicShard(備分片)會通過選舉成為PrimaryShard。
-
Replica容錯:將replica提升為新的primary shard纤房,- yellow
新的主分片選舉成功后,那么保證了主分片的完整性纵隔,但是少了一個備分片,所以狀態(tài)變成了黃色
重啟宕機(jī)節(jié)點:會生成新的ReplicShard,如果宕機(jī)前有數(shù)據(jù),會像恢復(fù)之前的數(shù)據(jù)炮姨,然后從PrimaryShard中拷貝新的數(shù)據(jù)舒岸,這樣做的好處是:1.恢復(fù)性能好 蛾派, 2.可以避免數(shù)據(jù)同步延遲造成的數(shù)據(jù)丟失問題(在宕機(jī)的一瞬間,有些數(shù)據(jù)還沒同步到ReplicShard,可能會導(dǎo)致數(shù)據(jù)丟失)
6. 集群搭建
環(huán)境準(zhǔn)備
真實環(huán)境
NodeName | Web端口,客戶端端口 |
---|---|
node-1 | 172.168.1.1:9200 172.168.1.1:9300 |
node-2 | 172.168.1.2:9200 172.168.1.2:9300 |
node-3 | 172.168.1.3:9200 172.168.1.3:9300 |
模擬環(huán)境
NodeName | Web端口糟需,客戶端端口 |
---|---|
node-1 | 127.0.0.1:9201 127.0.0.1:9301 |
node-2 | 127.0.0.1:9202 127.0.0.1:9302 |
node-3 | 127.0.0.1:9203 127.0.0.1:9303 |
注意:需要準(zhǔn)備三個ES(拷貝),然后刪除data目錄 身冀, 如果電腦內(nèi)存不夠钾埂,可以把jvm.properties中的內(nèi)存設(shè)置改小
配置說明
- cluster.name
集群名褥紫,自定義集群名,默認(rèn)為elasticsearch部念,建議修改儡炼,因為低版本多播模式下同一網(wǎng)段下相同集群名會自動加入同一集群乌询,如生產(chǎn)環(huán)境這樣易造成數(shù)據(jù)運(yùn)維紊亂妹田。
- node.name
節(jié)點名,同一集群下要求每個節(jié)點的節(jié)點名不一致驶拱,起到區(qū)分節(jié)點和辨認(rèn)節(jié)點作用
- node.master
是否為主節(jié)點屯烦,選項為true或false,當(dāng)為true時在集群啟動時該節(jié)點為主節(jié)點温眉,在宕機(jī)或任務(wù)掛掉之后會選舉新的主節(jié)點类溢,恢復(fù)后該節(jié)點依然為主節(jié)點
- node.data
是否處理數(shù)據(jù)闯冷,選項為true或false蛇耀。負(fù)責(zé)數(shù)據(jù)的相關(guān)操作
- path.data
默認(rèn)數(shù)據(jù)路徑纺涤,可用逗號分隔多個路徑
- path.logs
默認(rèn)日志路徑
- bootstrap.mlockall
內(nèi)存鎖,選項為true或false外永,用來確保用戶在es-jvm中設(shè)置的ES_HEAP_SIZE參數(shù)內(nèi)存可以使用一半以上而又不溢出
- network.host
對外暴露的host伯顶,0.0.0.0時暴露給外網(wǎng)
- http.port
對外訪問的端口號祭衩,默認(rèn)為9200汪厨,所以外界訪問該節(jié)點一般為http://ip:9200/
- transport.tcp.port
集群間通信的端口號劫乱,默認(rèn)為9300
- discovery.zen.ping.unicast.hosts
集群的ip集合衷戈,可指定端口,默認(rèn)為9300刁笙,如 ["192.168.1.101","192.168.1.102"]
- discovery.zen.minimum_master_nodes
最少的主節(jié)點個數(shù)疲吸,為了防止腦裂摘悴,最好設(shè)置為(總結(jié)點數(shù)/2 + 1)個
- discovery.zen.ping_timeout
主節(jié)點選舉超時時間設(shè)置
- gateway.recover_after_nodes
值為n蹂喻,網(wǎng)關(guān)控制在n個節(jié)點啟動之后才恢復(fù)整個集群
- node.max_local_storage_nodes
值為n捂寿,一個系統(tǒng)中最多啟用節(jié)點個數(shù)為n
- action.destructive_requires_name
選項為true或false蔓彩,刪除indices是否需要現(xiàn)實名字
修改ES配置
- Node1-配置
# 統(tǒng)一的集群名
cluster.name: my-ealsticsearch
# 當(dāng)前節(jié)點名
node.name: node-1
# 對外暴露端口使外網(wǎng)訪問
network.host: 127.0.0.1
# 對外暴露端口
http.port: 9201
#集群間通訊端口號
transport.tcp.port: 9301
#集群的ip集合踱侣,可指定端口探膊,默認(rèn)為9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
- Node2-配置
# 統(tǒng)一的集群名
cluster.name: my-ealsticsearch
# 當(dāng)前節(jié)點名
node.name: node-2
# 對外暴露端口使外網(wǎng)訪問
network.host: 127.0.0.1
# 對外暴露端口
http.port: 9202
#集群間通訊端口號
transport.tcp.port: 9302
#集群的ip集合逞壁,可指定端口腌闯,默認(rèn)為9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
- Node3-配置
# 統(tǒng)一的集群名
cluster.name: my-ealsticsearch
# 當(dāng)前節(jié)點名
node.name: node-3
# 對外暴露端口使外網(wǎng)訪問
network.host: 127.0.0.1
# 對外暴露端口
http.port: 9203
#集群間通訊端口號
transport.tcp.port: 9303
#集群的ip集合姿骏,可指定端口分瘦,默認(rèn)為9300
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
分別啟動三個ES節(jié)點 嘲玫, 訪問:http://127.0.0.1:9201/
連接集群
修改kibana配置
elasticsearch.url: "http://localhost:9201"
連接其中一個節(jié)點自然能連接上整個集群 去团, 然后啟動Kibana
集群查看命令
創(chuàng)建索引
PUT shopping
{
"settings":{
"number_of_shards":5,
"number_of_replicas":1
}
}
GET _cat/nodes?v :查看Node
GET _cat/indices?v : 查看索引庫
七.JavaApi操作ES
集成ES
導(dǎo)入依賴
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
連接ES
編寫工具
public class ESClientUtil {
public static TransportClient getClient(){
Settings settings = Settings.builder()
.put("cluster.name","my-ealsticsearch")
.put("client.transport.sniff", true).build();
TransportClient client = null;
try {
client = new PreBuiltTransportClient(settings)
.addTransportAddress(
new InetSocketTransportAddress(InetAddress.getByName("127.0.0.1"), 9303));
} catch (UnknownHostException e) {
e.printStackTrace();
}
return client;
}
}
文檔CRUD
添加文檔
@Test
public void testAdd() {
//獲取客戶端對象
TransportClient client = ESClientUtil.getClient();
//創(chuàng)建索引
IndexRequestBuilder indexRequestBuilder = client.prepareIndex("shopping", "user", "1");
Map<String,Object> data = new HashMap<>();
data.put("id",1);
data.put("username","zs");
data.put("age",11);
//獲取結(jié)果
IndexResponse indexResponse = indexRequestBuilder.setSource(data).get();
System.out.println(indexResponse);
client.close();
}
9.2.2.獲取文檔
GetResponse response = client.prepareGet("crm", "vip", "1").get();
9.2.3.更新文檔
@Test
public void testUpdate(){
//獲取客戶端對象
TransportClient client = ESClientUtil.getClient();
//修改索引
UpdateRequestBuilder updateRequestBuilder = client.prepareUpdate("shopping", "user", "1");
Map<String,Object> data = new HashMap<>();
data.put("id",1);
data.put("username","zs");
data.put("age",11);
//獲取結(jié)果設(shè)置修改內(nèi)容
UpdateResponse updateResponse = updateRequestBuilder.setDoc(data).get();
System.out.println(updateResponse);
client.close();
}
9.2.4.刪除文檔
@Test
public void testDelete(){
//獲取客戶端對象
TransportClient client = ESClientUtil.getClient();
DeleteRequestBuilder deleteRequestBuilder = client.prepareDelete("shopping", "user", "1");
DeleteResponse deleteResponse = deleteRequestBuilder.get();
System.out.println(deleteResponse);
client.close();
}
9.2.5.批量操作
@Test
public void testBuilkAdd(){
//獲取客戶端對象
TransportClient client = ESClientUtil.getClient();
BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
Map<String,Object> data1 = new HashMap<>();
data1.put("id",11);
data1.put("username","zs");
data1.put("age",11);
bulkRequestBuilder.add(client.prepareIndex("shopping", "user", "11").setSource(data1));
Map<String,Object> data2 = new HashMap<>();
data2.put("id",22);
data2.put("username","zs");
data2.put("age",11);
bulkRequestBuilder.add(client.prepareIndex("shopping", "user", "11").setSource(data2));
BulkResponse bulkItemResponses = bulkRequestBuilder.get();
Iterator<BulkItemResponse> iterator = bulkItemResponses.iterator();
while(iterator.hasNext()){
BulkItemResponse next = iterator.next();
System.out.println(next.getResponse());
}
client.close();
}
9.3.查詢
@Test
public void testSearch(){
//獲取客戶端對象
TransportClient client = ESClientUtil.getClient();
SearchRequestBuilder searchRequestBuilder = client.prepareSearch("shopping");
searchRequestBuilder.setTypes("user");
searchRequestBuilder.setFrom(0);
searchRequestBuilder.setSize(10);
searchRequestBuilder.addSort("age", SortOrder.ASC);
//查詢條件
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
List<QueryBuilder> must = boolQueryBuilder.must();
must.add(QueryBuilders.matchQuery("username" , "zs"));
List<QueryBuilder> filter = boolQueryBuilder.filter();
filter.add(QueryBuilders.rangeQuery("age").lte(20).gte(10));
filter.add(QueryBuilders.termQuery("id",11));
searchRequestBuilder.setQuery(boolQueryBuilder);
SearchResponse searchResponse = searchRequestBuilder.get();
SearchHits hits = searchResponse.getHits();
System.out.println("條數(shù):"+hits.getTotalHits());
for (SearchHit hit : hits.getHits()) {
System.out.println(hit.getSource());
}
client.close();
}