Elasticsearch 集群

分片规则

image
相同数字的分片是不能放在同一个服务器里的,这些规则都是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 查看:
image

查看分片布局

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

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

如果我们的主节点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集群文档的读写原理

写的原理

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

读的原理

image

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

image
解决办法,创建下面这个类:

@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 + '\'' +
                '}';
    }
}
posted @ 2021-09-04 14:34  金盛年华  阅读(439)  评论(0)    收藏  举报