Elasticsearch 集群
分片规则

相同数字的分片是不能放在同一个服务器里的,这些规则都是ES自己处理。
搭建ES集群
这我准备了三台安装了ES的虚拟机:192.168.10.131、192.168.10.132、192.168.10.133
配置文件 elasticsearch.yml :
# 配置集群名称,保证每个节点的名称相同,如此就能都处于一个集群之内了
cluster.name: jinsh-es-cluster
# 每一个节点的名称,必须不一样es-node0、es-node1、es-node2
node.name: es-node0
# http端口(使用默认即可)
http.port: 9200
# 主节点,作用主要是用于来管理整个集群,负责创建或删除索引,管理其他非master节点(相当于企业老总)
node.master: true
# 数据节点,用于对文档数据的增删改查
node.data: true
# 集群列表
discovery.seed_hosts: ["192.168.10.131","192.168.10.132","192.168.10.133"]
# 启动的时候使用一个master节点
cluster.initial_master_nodes: ["es-node0"]
三台ES配置完成后全部启动。
通过 ES head 查看:

查看分片布局
新建一个test索引,有5个主分片,5个副本分片,下面看下分片的布局,可以看到同一个节点不会有两个相同的分片。

下面查看下假设一条节点宕机,我关闭了一个ES,可以看到es-node2节点宕机了,但01234五个节点都会还在

如果我们的主节点es-node0宕机了,那么剩下两个从节点中会有一个被选举为主节点,当es-node0恢复后,es-node0将变成从节点。
脑裂问题
在老版本的ES中存在一个脑裂问题,就是当主节点宕机恢复后,它自己将自己选举成了主节点,这时就会有两个主节点,相当于就是两个集群,其中一个不完整。
- 解决方案:半数以上的节点同意选举,节点才可成为新的master。
有一个配置参数:discovery.zen.minimum_master_nodes=(N/2)+1
N为集群的中master节点的数量,也就是那些 node.master=true 设置的那些服务器节点总数。
在最新版7.x中,minimum_master_node这个参数已经被移除了,这一块内容完全由es自身去管理,这样就避免了脑裂的问题,选举也会非常快。
ES集群文档的读写原理
写的原理

每当请求过来会选取一个节点充当协调节点,协调节点再选取一个主分片去存储,然后再同步给副本分片,最后备份分片再通知协调节点,响应给客户端。
读的原理

SpringBoot 整合 Elasticsearch
pom依赖引入
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
注意版本这块需要对应好,可以参考下面这两个个地址
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#preface.versions
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-elasticsearch
yml文件配置
spring:
elasticsearch:
rest:
uris: http://192.168.10.131:9200,http://192.168.10.132:9200,http://192.168.10.133:9200
2.2.x的包启动时可能会报异常 Netty issue fix

解决办法,创建下面这个类:
@Configuration
public class ESConfig {
/**
* 解决netty引起的issue
*/
@PostConstruct
void init() {
System.setProperty("es.set.netty.runtime.available.processors", "false");
}
}
Java代码示例
package com.itzixi.controller;
import com.itzixi.pojo.Stu;
import com.itzixi.utils.JsonUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.json.GsonJsonParser;
import org.springframework.boot.json.JsonParser;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@RestController
public class HelloController {
@Autowired
private ElasticsearchRestTemplate esTemplate;
@RequestMapping("hello")
public Object hello() {
return "OK";
}
/**
* 创建索引
*/
@RequestMapping("createIndex")
public Object createIndex() {
esTemplate.indexOps(Stu.class).create();
return "OK";
}
/**
* 删除索引
*/
@RequestMapping("deleteIndex")
public Object deleteIndex() {
esTemplate.indexOps(Stu.class).delete();
return "OK";
}
/**
* 判断索引是否存在
*/
@RequestMapping("existIndex")
public Object existIndex() {
return esTemplate.indexOps(Stu.class).exists();
}
/**
* 新增文档数据
*/
@RequestMapping("addDoc")
public Object addDoc() {
Stu stu0 = new Stu(10010l, "imooc", 18, 100.5f, true);
esTemplate.save(stu0);
Stu stu1 = new Stu(10011l, "风间影月", 20, 88.5f, true);
Stu stu2 = new Stu(10012l, "慕课网", 22, 108.5f, false);
ArrayList<Stu> stuList = new ArrayList<>();
stuList.add(stu1);
stuList.add(stu2);
esTemplate.save(stuList);
return "OK";
}
/**
* 根据文档id删除数据
*/
@RequestMapping("deleteDoc")
public Object deleteDoc(String docId) {
esTemplate.delete(docId, Stu.class);
return "OK";
}
/**
* 查询文档数据
*/
@RequestMapping("getDoc")
public Object getDoc(String docId) {
return esTemplate.get(docId, Stu.class);
}
/**
* 修改文档数据
*/
@RequestMapping("updateDoc")
public Object updateDoc() {
Map<String, Object> map = new HashMap<>();
map.put("name", "abc");
map.put("money", 55.66f);
Document doc = Document.from(map);
UpdateQuery updateQuery = UpdateQuery
.builder("10012")
.withDocument(doc)
.build();
IndexCoordinates indexCoordinates = IndexCoordinates.of("stu");
esTemplate.update(updateQuery, indexCoordinates);
return "OK";
}
/******************** 分词搜索 ********************/
/**
* 初始化数据
*/
@RequestMapping("init")
public Object init() {
esTemplate.indexOps(Stu.class).delete();
esTemplate.indexOps(Stu.class).create();
Stu stu0 = new Stu(10010l, "imooc", 18, 100.5f, true);
Stu stu1 = new Stu(10011l, "风间影月", 20, 88.5f, true);
Stu stu2 = new Stu(10012l, "慕课网", 22, 96.5f, false);
Stu stu3 = new Stu(10013l, "可爱的漂亮的小哥哥", 26, 108.5f, false);
Stu stu4 = new Stu(10014l, "美丽的祖国", 28, 108.6f, true);
Stu stu5 = new Stu(10015l, "美丽的漂亮的小姐姐", 16, 18.5f, false);
Stu stu6 = new Stu(10016l, "完美的慕课网", 29, 100.5f, true);
ArrayList<Stu> stuList = new ArrayList<>();
stuList.add(stu0);
stuList.add(stu1);
stuList.add(stu2);
stuList.add(stu3);
stuList.add(stu4);
stuList.add(stu5);
stuList.add(stu6);
esTemplate.save(stuList);
return "OK";
}
/**
* 搜索数据
*/
@RequestMapping("searchStu")
public Object searchStu(String name) {
Pageable pageable = PageRequest.of(0, 10);
SortBuilder sortBuilder = new FieldSortBuilder("money")
.order(SortOrder.DESC);
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("name", name))
.withPageable(pageable)
.withSort(sortBuilder)
.build();
SearchHits<Stu> searchHits = esTemplate.search(query, Stu.class);
return searchHits.getSearchHits();
}
/**
* 高亮搜索
*/
@RequestMapping("highlight")
public Object highlight(String name) {
String preTag = "<font color='red'>";
String postTag = "</font>";
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("name", name))
.withHighlightFields(new HighlightBuilder.Field("name")
.preTags(preTag)
.postTags(postTag))
.build();
SearchHits<Stu> searchHits = esTemplate.search(query, Stu.class);
List<SearchHit<Stu>> searchHitList = searchHits.getSearchHits();
List<Map<String, Object>> hlList = new ArrayList<>();
for (SearchHit h : searchHitList) {
List<String> highlightField = h.getHighlightField("name");
String nameValue = highlightField.get(0);
String originalJson = JsonUtils.objectToJson(h.getContent());
JsonParser jj = new GsonJsonParser();
Map<String, Object> myHighLight = jj.parseMap(originalJson);
// 用高亮的搜索结果覆盖原字段值
myHighLight.put("name", nameValue);
System.out.println(myHighLight);
hlList.add(myHighLight);
}
return hlList;
}
}
POJO
package com.imooc.es.pojo;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Document(indexName = "stu", type = "_doc", shards = 3, replicas = 0)
public class Stu {
@Id
private Long stuId;
@Field(store = true)
private String name;
@Field(store = true)
private Integer age;
@Field(store = true)
private Float money;
@Field(store = true, type = FieldType.Keyword)
private String sign;
@Field(store = true)
private String description;
public Long getStuId() {
return stuId;
}
public void setStuId(Long stuId) {
this.stuId = stuId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Float getMoney() {
return money;
}
public void setMoney(Float money) {
this.money = money;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Stu{" +
"stuId=" + stuId +
", name='" + name + '\'' +
", age=" + age +
", money=" + money +
", sign='" + sign + '\'' +
", description='" + description + '\'' +
'}';
}
}

浙公网安备 33010602011771号