一、关系型数据库和ES的对比

ElasticSearch是一个搜索服务器,就是用来做大量查询。

  • 关系型数据库做查询的弊端

1.我们在查询数据量比较的多的数据时,如果我们使用模糊查询,这样将会使索引失效,这个时候就会进行全表扫描,查询效率非常低
2.我们在使用关系型数据库的时候,没办法对查询条件进行分词查询
总结:就是关系型数据库做查询的弊端就是性能低,功能弱

  • ES存储数据的特点

ES将数据存储在index中(索引库,类似于关系型数据库的db),而所以ES中的数据以文档(document,类似于关系型数据库的表一条记录)的形式存储到索引库中,文档的数据格式就是JSON的数据格式
ES存储的过程.jpg
ES来解决关系型数据库查询效率低功能弱的问题
ES查询的过程.jpg
ES查询效率高原因:首先会对文档进行分词,形成倒排索引,这些分词ES会对他们进行排序形成树形结构,这样查询分词的时候就会很快,不需要向关系型数据库那样进行全表扫描
ES查询功能强的原因:对于查询条件,ES首先会先进行分词,然后根据这些词条进行匹配查询

二、倒排索引

  • 正向索引

在人的存储思维中,我们存储数据信息时采用的是正向索引,即用数据的唯一标识来对应文本内容,我们大脑在查找数据时,现根据数据的唯一标识然后去找对应的文本内容,反过来通过文本内容信息来找唯一标识就很难

  • 倒排索引

首先对文本信息按照一定的规则拆分成不同的词条,将这些词条作为查找的条件,通过这些分词就行查到对象的数据,但是这样的缺点就是同一个分词可能会对应多个数据,导致数据量很大所以查找起来还是慢,这个时候我们可以进行优化,将数据位置不存储具体数据还是数据的唯一标识,即数据的索引,通过分词来对应索引,通过索引来找对应的数据,从而提高查询效率
倒排索引的解释:将各个文档的内容进行分词,形成词条。然后记录词条和数据唯一标识的(一般是ID)的对应关系,形成的产物

三、ES的介绍

  1. ES是一个基于Lucene的搜索服务器,Lucene是一套搜索的API(用于搜索的Jar包),ES是对Lucene的一个实现,ES内部提供了分词的操作,另一个对Lucene比较好的实现是solr,ES是后出来的搜索产品,性能方面比solr更好,在做实时搜索更好(即一边存储,一边搜索)
  2. ES是一个分布式(分布式说明ES是一个天然支持分布式的,搭建集群很方便),高扩展,高实时的搜索与数据分析引擎
  3. 它是基于restful Web 的接口(即通过发送RestFul风格的http请求来实现搜索)、
  4. ES是Java语言开发的(ES安装包内置了JDK,所以即使没有配置JDK也可以使用),它是一种开源的流行的企业级搜索引擎
  5. 官网是:https://www.elastic.co/

重点:ES 的应用场景
1.海量数据的搜索
2.日志数据分析
3.实时数据的分析
注意:有了ES我们依然需要关系型数据库,首先关系型数据库是有事务的,但是ES是没有事务的,所以我们删了数据是没有办法恢复的
MYSQL是有物理外键的,ES没有物理外键,所以对数据一致性要求比较高的话慎用ES
总结:两种类型的数据库只是分工不同,MYSQL数据库负责存储数据,而ES负责查询数据

四、安装ES

  • 9200:使用http请求,所以我们的rest方式的client要使用这个端口进行访问
  • 9300:使用tcp请求,是系统预留给es内部组件之间的通信方式
    1. #第一步:到官网下载Linux版本的压缩包,上传到Linux服务器上
    2. #第二步:将压缩包进行解压,由于我们下载的是tar包,所以使用tar命令
    3. tar -zxvf 压缩包名 -C 需要解压到那个目录
    4. #第三步:修改配置,在ES的安装目录下的config目录下有一个elasticseach.yml配置文件
    5. cluster.name: application(配置ES集群的名称,默认是elasticsearch)
    6. node.name: node-1(集群节点名,ES默认会随机给一个名字,我们可以修改成有意义的名字)
    7. network.host: 0.0.0.0 :这里配置是可以被外网访问(这个配置是将两个配置合二为一:network.host将设置network.bind_hostnetwork.publish_host为相同的值)
    8. http.port: 9200(默认端口号)
    9. cluster.initial_master_nodes: ["node-1"](初始化新的集群时,通过此配置来选取master)
    注意:启动ES不能通过root用户否则会报错,所以我们要创建非root用户 ```shell useradd test # 新增itheima用户 passwd zhubowen # 为itheima用户设置密码 //更改文件夹的所有者, chown -R test:test /opt/elasticsearch-7.4.0 #文件夹所有者

    新创建的test用户最大可创建文件数太小,最大虚拟内存太小,切换到root用户,编辑下列配置文件, 添加类似如下内容

    切换到root用户

    su root

    1. ===最大可创建文件数太小=======

    vim /etc/security/limits.conf

    在文件末尾中增加下面内容

    test soft nofile 65536 testhard nofile 65536

    =====

    vim /etc/security/limits.d/20-nproc.conf

    在文件末尾中增加下面内容

    test soft nofile 65536 test hard nofile 65536
  • hard nproc 4096

    注:* 代表Linux所有用户名称

2. ===最大虚拟内存太小=======

vim /etc/sysctl.conf

在文件中增加下面内容

vm.max_map_count=655360

重新加载,输入下面命令:

sysctl -p

运行ES出现的错误即解决:
```java
错误:
Caused by: org.elasticsearch.ElasticsearchException: Failure running machine learning native code. This could be due to running on an unsupported OS or distribution, missing OS libraries, or a problem with the temp directory. To bypass this problem by running Elasticsearch without machine learning functionality set [xpack.ml.enabled: false].

这个错误是因为在Centos中运行了Macos下的es安装包,换成linux的包,正常启动
解决:进入config目录下在elasticsearch.yml添加一条配置:xpack.ml.enabled: false

启动成功:可以访问IP:9200

五、ES的核心概念

  • index(索引)

ES存储数据的地方

  • mapping(映射)

用来定义每个字段的数据类型,字段所使用的分词器。相当于关系型数据库的表结构

  • document(文档)

ES中的最小数据单元,常以JSON格式显示,一个document相当于关系型数据库的一行数据

  • 倒排索引

一个倒排索引由文档中所有不重复,对于其中的每一个词,对应它包含的文档ID列表

六、操作ES

1.通过脚本操作ES,由于ES是一个服务器,支持RestFul风格请求,我们可以通过发送Http请求操作ES

RESTFul风格介绍.jpg
6.1:索引的操作(创建数据库)

  • 创建索引

使用put请求:ip:端口/索引库名 :表示创建索引库

  • 删除索引

使用delete请求:ip:端口/索引库名

  • 查询索引

使用get请求:ip:端口/索引库名 :查询创建的索引库信息,如果想查询多个索引库,后面的索引库名用逗号拼接即可
使用ip:端口/_all表示查询所有索引库,下划线开头的表示ES提供好的命令

//查询索引库返回的结果
{
    "test": {//索引库名
        "aliases": {},//索引库别名
        "mappings": {},//映射
        "settings": {
            "index": {
                "routing": {
                    "allocation": {
                        "include": {
                            "_tier_preference": "data_content"
                        }
                    }
                },
                "number_of_shards": "1",
                "provided_name": "test",
                "creation_date": "1622313127279",
                "number_of_replicas": "1",
                "uuid": "u8sR0H1nTTODlm392sVWXg",
                "version": {
                    "created": "7130099"
                }
            }
        }
    }
}
  • 关闭索引:索引没有删除,只是不能用

使用POST请求:ip:端口/索引库名/_close 来关闭索引库

  • 打开索引

使用POST请求:ip:端口/索引库名/_open 来打开索引库
6.2:操作映射(创建表)
ES中的数据类型

  • 简单数据类型
    • 字符串
      • text:这种数据类型,会分词,但是不支持聚合(这个聚合就相当于关系型数据库的聚合函数如:sum等)
      • keyword:不支持分词,将全部内容作为词条,支持聚合
    • 数值
      • byte:带符号8位整数,范围是[-128,127]
      • short:带符号16位整数,范围是[-32768,32767]
      • integer:带符号32位整数,范围是[-231232,231231]
      • long:带符号64位整数,范围是[-263263,263262]
      • half_float:单精度16位IEEE754浮点数,
      • float:单精度32位IEEE754浮点数,
      • double:双精度64位IEEE754浮点数,
      • scaled_float:有a支持的有限浮点数long,由固定double比例因子缩放
    • 布尔
      • boolean
    • 二进制
      • binary
    • 范围存储:integer_range,long_range,float_range,double_range,date_range
      • 我们可以设置一个字段为一个范围,在这个范围内可以存储
    • 日志
      • date
  • 复杂数据类型
    • 数组
      • []
    • 对象
      • {}

ES中复杂的数据类型.jpg
添加映射

要指定索引来添加映射:
发送put请求:http://IP:端口/索引(给那个索引添加映射)/_mapping(ES提供的添加映射的指令)
请求体内携带JSON格式参数
{
    "properties":{//来指定属性
        "name":{"type":"keyword"},//创建name属性,数据类型为keyword
        "age":{"type":"integer"}//创建age属性,数据类型为integer
    }
}
也可以在创建索引库的时候添加映射信息
发送put请求:http://IP:端口/索引(给那个索引添加映射)
请求体内携带JSON格式参数
{
    "mappings":{
      "properties":{//来指定属性
          "name":{"type":"keyword"},//创建name属性,数据类型为keyword
          "age":{"type":"integer"}//创建age属性,数据类型为integer
      }
  }
}

查询映射
使用get请求:http://IP:端口/索引/_mapping
添加字段

添加映射和创建映射一样
发送put请求:http://IP:端口/索引(给那个索引添加映射)/_mapping(ES提供的添加映射的指令)
请求体内携带JSON格式参数
{
    "properties":{//来指定属性
        "address":{
                "type":"keyword",
            "analyzer":"ik_max_word"//指定分词器
        }
    }
}

6.3操作文档(增删改查表中的数据)

  • 添加文档

    添加文档有两种方式:
    方式一:指定ID,使用put方式http://IP:端口/索引库/_doc/id
    {
      "name":"张三",
      "age":20,
      "address":"合肥"
    }
    方式二:不指定Id,只能用post请求,http://IP:端口/索引库/_doc
    {
      "name":"李四",
      "age":20,
      "address":"合肥"
    }
    //这里的Id是ES中提供的_id属性,如果我们提供就是我们提供的,如果我们不提供就是ES随机生成的一个字符串
    //对于文档中我们也可以添加自己的Id属性列,对应数据库中表的主键
    
  • 查询文档

    查询指定ID,使用get方式http://IP:端口/索引库/_doc/id
    查询所有文档:使用get方式http://IP:端口/索引库/_search
    
  • 删除文档

    根据Id删除文档:使用DELETE请求方式http://IP:端口/索引库/_doc/id
    
  • 修改文档

    修改和添加文档一样:ID存在则修改ID不存在则添加
    方式一:指定ID,使用put方式http://IP:端口/索引库/_doc/id
    

注意:ES中支持的请求类型是put、get、delete,ES中提供的指令都是下划线开头的例如_doc,_search等,而且每次操作我们基本都要携带索引库的名字,所以我们可以大致来记住操作ES索引库,映射和文档的请求格式都是ip:端口/索引库/加指令(_mapping,_doc,_search)

七、分词器

分词器的介绍.jpg
IK分词器
分词器的介绍.jpg
IK分词器的官网是:https://github.com/medcl/elasticsearch-analysis-ik/releases,根据ES的版本来下载IK分词器的版本,这个版本一定要对应ES的版本

jar包的移动
将下载好的ES进行解压,然后使用maven命令进行打包,打包好了,进入target目录
package执行完毕后会在当前目录下生成target/releases目录,将其中的elasticsearch-analysis-ik-7.4.0.zip。拷贝到elasticsearch目录下的新建的目录plugins/analysis-ik,并解压
拷贝词典
将elasticsearch-analysis-ik-7.4.0目录下的config目录中的所有文件 拷贝到elasticsearch的config目录
cp -R /opt/ik/elasticsearch-analysis-ik-7.13.0/config/*  /opt/elasticsearch/elasticsearch-7.13.0/config/
*表示该目录下的所有文件

八、IK分词器的使用

1.IK分词器有两种分词模式:ik_max_word(细粒度,细粒度分词更细,比如可以手机可以分为手和手机)和ik_smart(粗粒度)模式,粗粒度和细粒度的区别是细粒度会在分过的词上在进行分词,比如华为手机会分成华为,手机,手机也会进行分词,但是粗粒度在分过词后不会再分过的词上继续分词

//分词器测试:
发送get请求http://IP:端口/_analyze
请求体是
{
    "analyzer":"ik_max_word",//选择分词器,这里选择IK的细粒度分词器
  "text":"分词内容"
}

九、查询ES文档的语法

脚本查询文档

  • 词条查询:term(词条查询不会对查询条件进行分词,只有当词条和查询字符串完全匹配才能查出来)

    查询使用的是get请求:
    http://ip:端口/索引库/_search
    //携带的请求参数是
    {
      "query":{//这个属性表示查询
        "term":{//这里表示使用词条查询
          "address":{//查询的字段
            "value":"查询条件"//字段对应的值
        }
      }
    }
    }
    
  • 全文查询:match(全文查询会对查询条件进行分词,然后进行查询,最后求并集)

    查询使用的是get请求:
    http://ip:端口/索引库/_search
    //携带的请求参数是
    {
      "query":{//这个属性表示查询
        "match":{//这里表示使用全文查询
          "address":"查询的字段属性值"//这里指定查询那个字段
      }
    }
    }
    

十、通过Java代码的方式操作ES

这里总结两种方式操作ES:
方式一:通过ES提供的API
1.使用SpringBoot整合ES(由于SPring没有提供单独的启动坐标,所以我们要引入,使用Spring的话也是这些坐标)

<!--导入这个坐标注意使用的ES的版本,我测试使用的ES 7.13.0 -->
<dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>7.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.13.0</version>
        </dependency>

2.Java代码来操作索引

package com.study.elasticsearchtest;

import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Map;

@SpringBootTest
public class ElasticsearchtestApplicationTests {
    @Autowired
    private RestHighLevelClient client;
    @Test
    public void test1() {
        //创建ES客户端对象
        RestHighLevelClient restHighLevelClient=new RestHighLevelClient(RestClient.builder(
                new HttpHost("182.92.71.236",9200,"http")
        ));//创建HttpHost()对象需要三个参数IP,端口,协议
    }

    /**
     * 添加索引
     */
    @Test
    public void addIndex() throws Exception{
    //创建索引对象
        IndicesClient indices = client.indices();
     //通过索引对象来操作索引,
        CreateIndexRequest createRequest=new CreateIndexRequest("test1");//索引名称
        //create这个方法有两个重载的方法,这个方法第一个参数所在的包不一样,有一个方法是过时的
        CreateIndexResponse createIndexResponse = indices.create(createRequest, RequestOptions.DEFAULT);//第二参数是请求选项,这里用默认选项
        //true表示创建成功,创建失败抛异常
        boolean acknowledged = createIndexResponse.isAcknowledged();
        System.out.println(acknowledged);
    }

    /**
     * 添加索引同时指定mapping,(即初始化索引的相关参数)
     */
    @Test
    public void addIndexAndMapping() throws Exception{
        //创建索引对象
        IndicesClient indices = client.indices();
        /*通过索引对象来操作索引,注意createRequest是用来初始化索引的相关参数,采用链式编程
        * CreateIndexRequest的对象,可以用来给索引添加映射,起别名等,
        * 进行一些初始化索引的操作
        * */
        CreateIndexRequest createRequest=new CreateIndexRequest("test2");//索引名称
        //添加mapping,这里的mapping我们用JSON格式创建和使用请求携带请求创建格式一样,
        String mapping="{\n" +
                "    \"properties\":{\n" +
                "        \"name\":{\"type\":\"text\"},\n" +
                "        \"age\":{\"type\":\"integer\"}\n" +
                "    }\n" +
                "}";
        createRequest.mapping(mapping, XContentType.JSON);
        //create这个方法有两个重载的方法,这个方法第一个参数所在的包不一样,有一个方法是过时的
        CreateIndexResponse createIndexResponse = indices.create(createRequest, RequestOptions.DEFAULT);//第二参数是请求选项,这里用默认选项
        //true表示创建成功,创建失败抛异常
        boolean acknowledged = createIndexResponse.isAcknowledged();
        System.out.println(acknowledged);
    }

    /**
     * 查询索引(查询索引的相关配置):步骤和创建索引类似
     */
    @Test
    public void queryIndex()throws Exception{
        //创建索引对象
        IndicesClient indices = client.indices();
        GetIndexRequest getIndexRequest = new GetIndexRequest("test2");
        GetIndexResponse response = indices.get(getIndexRequest, RequestOptions.DEFAULT);
        //这里的GetIndexResponse,不仅可以获取索引信息,还可以获取索引的其他信息
        Map<String, MappingMetadata> mappings = response.getMappings();
        for (String s : mappings.keySet()) {
            System.out.println(mappings.get(s).getSourceAsMap());
        }
    }

    /**
     * 删除索引
     */
    @Test
    public void deleteIndex()throws Exception{
        //创建索引对象
        IndicesClient indices = client.indices();
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("test1");
        AcknowledgedResponse response = indices.delete(deleteIndexRequest, RequestOptions.DEFAULT);
        //返回true说明删除成功
        boolean acknowledged = response.isAcknowledged();
        System.out.println(acknowledged);
    }

    /**
     * 判断索引是否存在
     */
    @Test
    public void existIndex()throws Exception{
        //创建索引对象
        IndicesClient indices = client.indices();
        GetIndexRequest getIndexRequest = new GetIndexRequest("test","test1");
        boolean exists = indices.exists(getIndexRequest, RequestOptions.DEFAULT);
        System.out.println(exists);
    }
}

3.Java 代码来操作文档

package com.study.elasticsearchtest;

import com.alibaba.fastjson.JSON;
import com.study.elasticsearchtest.domain.Person;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.HashMap;
import java.util.Map;

//ES对文档的基本操作:增删改查
@SpringBootTest
public class ElasticsearchDocApplicationTest {
    @Autowired
    private RestHighLevelClient client;

    /**
     * 添加或者修改(不存在添加,存在就是修改)文档(使用Map添加)
     */
    @Test
    public void addDocByMap() throws Exception{
        HashMap map = new HashMap<>();
        //注意这里设置的值要和ES的类型进行对应
        map.put("age",12);
        map.put("name","中国");
        IndexRequest request=new IndexRequest("test2").id("3").source(map);
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        System.out.println(response.getId());
    }
    /**
     * 添加或修改(不存在添加,存在就是修改)文档(使用JSON串)
     */
    @Test
    public void addDocByJSON() throws Exception{
        //一般我们都是使用对象,将对象转换成JSON放入ES
        Person person = new Person();
        person.setAge(12);
        person.setName("ceshi");
        String personString = JSON.toJSONString(person);
        IndexRequest request=new IndexRequest("test2").id("4").source(personString, XContentType.JSON);
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        System.out.println(response.getId());
    }

    /**
     * 根据Id查询
     */
    @Test
    public void getDocById()throws Exception{
        GetRequest request=new GetRequest("test2","3");
        //或者可以调用方法指定ID,request.id();
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        String sourceAsString = response.getSourceAsString();
//        Map<String, Object> sourceAsMap = response.getSourceAsMap();
        System.out.println(sourceAsString);
    }

    /**
     * 根据Id删除
     */
    @Test
    public void deleteDoc()throws Exception{
        DeleteRequest request=new DeleteRequest("test2").id("3");
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        System.out.println(response.getId());
    }
}

4.批量操作ES
4.1脚本操作ES
ES脚本批量操作.jpg
4.2Java代码批量操作ES

package com.study.elasticsearchtest;

import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class ElasticsearchBulkApplicationTests {
    @Autowired
    private RestHighLevelClient client;
    //使用Java代码批量操作ES
    @Test
    public void testBulk() throws Exception{
        //创建批量操作对象
        BulkRequest bulkRequest=new BulkRequest();
        //进行批量操作
        //删除操作
        DeleteRequest deleteRequest=new DeleteRequest().index("test2").id("3");
        bulkRequest.add(deleteRequest);
        //添加操作
        IndexRequest indexRequest=new IndexRequest();
        Map map = new HashMap();
        map.put("age",12);
        map.put("name","test11212");
        indexRequest.index("test2").id("7").source(map);
        bulkRequest.add(indexRequest);
        //修改操作
        Map map2 = new HashMap();
        map2.put("age",12);
        map2.put("name","test11212");
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index("test2").id("7").doc(map2);
        bulkRequest.add();
        //执行批量操作,返回结果封装在Response对象中
        BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(response.status());
    }
}

注意:在我们使用java代码操作ES导入数据的时候,特别注意插入的字符串的格式(即JSON格式是否正确),否者可能会导致不报错但是ES里没有数据,追查原因可以在返回的Response对象中打断点,尝试在对象中查看异常信息

十一、ES中的常用查询

1.查询所文档

ES查询所有文档.jpg
查询结果中的max-score是得分,ES会更具这个得分进行排序
2.term查询(不会对查询条件进行分词,它会将查询条件和词条进行全匹配)
term查询.jpg
对于term查询,一般我们进行term查询的时候,最好映射列也是不分词的列,这样效率更高
3.match查询(它会对查询条件进行分词)

match查询.jpg
4.模糊查询(它会对查询条件分词,然后进行模糊查询,可以通过通配符来进行通配查询)
模糊查询2.jpg
5.正则查询
正则查询.jpg
6.前缀查询:(即以什么开头的,这个查询对映射为keyword类型的支持的比较好)
前缀查询.jpg
7.范围查询
范围查询.jpg
8.对查询结果记性排序(任何查询结果都可以进行排序)
排序查询.jpg
9.多字段匹配查询(和match查询很类似,但是同时匹配多个字段分词,这里的查询条件也可以写多个,可以取交集或者并集,例如华为or手机,表示字段中有华为或者手机,如果是华为and手机表示字段中既有华为又有手机才可以出现)

多字段匹配查询.jpg
simpleQueryString查询.jpg
10.布尔查询:它是同时进行多个查询,将查询结果连接
布尔查询.jpg
布尔查询脚本.jpg
2.常用查询之Java代码操作**

@SpringBootTest
class ElasticsearchDemo2ApplicationTests {

    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private GoodsMapper goodsMapper;
    /**
     * 批量导入
     */
    @Test
    public void importData() throws IOException {
        //1.查询所有数据,mysql
        List<Goods> goodsList = goodsMapper.findAll();

        //System.out.println(goodsList.size());
        //2.bulk导入
        BulkRequest bulkRequest = new BulkRequest();

        //2.1 循环goodsList,创建IndexRequest添加数据
        for (Goods goods : goodsList) {
            //2.2 设置spec规格信息 Map的数据   specStr:{}
            //goods.setSpec(JSON.parseObject(goods.getSpecStr(),Map.class));

            String specStr = goods.getSpecStr();
            //将json格式字符串转为Map集合
            Map map = JSON.parseObject(specStr, Map.class);
            //设置spec map
            goods.setSpec(map);
            //将goods对象转换为json字符串
            String data = JSON.toJSONString(goods);//map --> {}
            IndexRequest indexRequest = new IndexRequest("goods");
            indexRequest.id(goods.getId()+"").source(data, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(response.status());
    }

    /**
     * 查询所有
     *  1. matchAll
     *  2. 将查询结果封装为Goods对象,装载到List中
     *  3. 分页。默认显示10条
     */
    @Test
    public void testMatchAll() throws IOException {
        //2. 构建查询请求对象,指定查询的索引名称
        SearchRequest searchRequest = new SearchRequest("goods");
        //4. 创建查询条件构建器SearchSourceBuilder
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //6. 查询条件
        QueryBuilder query = QueryBuilders.matchAllQuery();//查询所有文档
        //5. 指定查询条件
        sourceBuilder.query(query);

        //3. 添加查询条件构建器 SearchSourceBuilder
        searchRequest.source(sourceBuilder);

        // 8 . 添加分页信息
        sourceBuilder.from(0);
        sourceBuilder.size(100);

        //1. 查询,获取查询结果
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //7. 获取命中对象 SearchHits
        SearchHits searchHits = searchResponse.getHits();
        //7.1 获取总记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);


        List<Goods> goodsList = new ArrayList<>();
        //7.2 获取Hits数据  数组
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //获取json字符串格式的数据
            String sourceAsString = hit.getSourceAsString();
            //转为java对象
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);

        }


        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }


    /**
     * termQuery:词条查询
     */
    @Test
    public void testTermQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

        QueryBuilder query = QueryBuilders.termQuery("title","华为");//term词条查询
        sourceBulider.query(query);

        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }





    /**
     * matchQuery:词条分词查询
     */
    @Test
    public void testMatchQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

        MatchQueryBuilder query = QueryBuilders.matchQuery("title", "华为手机");
        query.operator(Operator.AND);//求并集
        sourceBulider.query(query);

        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }





    /**
     * 模糊查询:WildcardQuery
     */
    @Test
    public void testWildcardQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

        WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华*");

        sourceBulider.query(query);

        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }




    /**
     * 模糊查询:regexpQuery
     */
    @Test
    public void testRegexpQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

        RegexpQueryBuilder query = QueryBuilders.regexpQuery("title", "\\w+(.)*");

        sourceBulider.query(query);

        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }



    /**
     * 模糊查询:perfixQuery
     */
    @Test
    public void testPrefixQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

        PrefixQueryBuilder query = QueryBuilders.prefixQuery("brandName", "三");

        sourceBulider.query(query);
        searchRequest.source(sourceBulider);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }





    /**
     * 1. 范围查询:rangeQuery
     * 2. 排序
     */
    @Test
    public void testRangeQuery() throws IOException {

        SearchRequest searchRequest = new SearchRequest("goods");
        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();
        //范围查询
        RangeQueryBuilder query = QueryBuilders.rangeQuery("price");

        //指定下限
        query.gte(2000);
        //指定上限
        query.lte(3000);

        sourceBulider.query(query);

        //排序
        sourceBulider.sort("price", SortOrder.DESC);


        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }

    /**
     * queryString
     */
    @Test
    public void testQueryStringQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();


        //queryString
        QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手机").field("title").field("categoryName").field("brandName").defaultOperator(Operator.AND);


        sourceBulider.query(query);
        searchRequest.source(sourceBulider);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }
    /**
     * 布尔查询:boolQuery
     * 1. 查询品牌名称为:华为
     * 2. 查询标题包含:手机
     * 3. 查询价格在:2000-3000
     */
    @Test
    public void testBoolQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();


        //1.构建boolQuery
        BoolQueryBuilder query = QueryBuilders.boolQuery();

        //2.构建各个查询条件
        //2.1 查询品牌名称为:华为
        QueryBuilder termQuery = QueryBuilders.termQuery("brandName","华为");
        query.must(termQuery);

        //2.2. 查询标题包含:手机
        QueryBuilder matchQuery = QueryBuilders.matchQuery("title","手机");
        query.filter(matchQuery);

        //2.3 查询价格在:2000-3000
        QueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
        ((RangeQueryBuilder) rangeQuery).gte(2000);
        ((RangeQueryBuilder) rangeQuery).lte(3000);
        query.filter(rangeQuery);

        //3.使用boolQuery连接

        sourceBulider.query(query);



        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }
    }




    /**
     * 聚合查询:桶聚合,分组查询
     * 1. 查询title包含手机的数据
     * 2. 查询品牌列表
     */
    @Test
    public void testAggQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

        // 1. 查询title包含手机的数据
        MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");

        sourceBulider.query(query);

        // 2. 查询品牌列表
        /*
        参数:
            1. 自定义的名称,将来用于获取数据
            2. 分组的字段
         */
        AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
        sourceBulider.aggregation(agg);



        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }

        // 获取聚合结果
        Aggregations aggregations = searchResponse.getAggregations();

        Map<String, Aggregation> aggregationMap = aggregations.asMap();

        //System.out.println(aggregationMap);
        Terms goods_brands = (Terms) aggregationMap.get("goods_brands");

        List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();

        List brands = new ArrayList();
        for (Terms.Bucket bucket : buckets) {
            Object key = bucket.getKey();
            brands.add(key);
        }

        for (Object brand : brands) {
            System.out.println(brand);
        }

    }




    /**
     *
     * 高亮查询:
     *  1. 设置高亮
     *      * 高亮字段
     *      * 前缀
     *      * 后缀
     *  2. 将高亮了的字段数据,替换原有数据
     */
    @Test
    public void testHighLightQuery() throws IOException {


        SearchRequest searchRequest = new SearchRequest("goods");

        SearchSourceBuilder sourceBulider = new SearchSourceBuilder();

        // 1. 查询title包含手机的数据
        MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");

        sourceBulider.query(query);

        //设置高亮
        HighlightBuilder highlighter = new HighlightBuilder();
        //设置三要素
        highlighter.field("title");
        highlighter.preTags("<font color='red'>");
        highlighter.postTags("</font>");


        sourceBulider.highlighter(highlighter);


        // 2. 查询品牌列表
        /*
        参数:
            1. 自定义的名称,将来用于获取数据
            2. 分组的字段
         */
        AggregationBuilder agg = AggregationBuilders.terms("goods_brands").field("brandName").size(100);
        sourceBulider.aggregation(agg);



        searchRequest.source(sourceBulider);


        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);


        SearchHits searchHits = searchResponse.getHits();
        //获取记录数
        long value = searchHits.getTotalHits().value;
        System.out.println("总记录数:"+value);

        List<Goods> goodsList = new ArrayList<>();
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();

            //转为java
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);

            // 获取高亮结果,替换goods中的title
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField HighlightField = highlightFields.get("title");
            Text[] fragments = HighlightField.fragments();
            //替换
            goods.setTitle(fragments[0].toString());
            goodsList.add(goods);
        }

        for (Goods goods : goodsList) {
            System.out.println(goods);
        }

        // 获取聚合结果
        Aggregations aggregations = searchResponse.getAggregations();

        Map<String, Aggregation> aggregationMap = aggregations.asMap();

        //System.out.println(aggregationMap);
        Terms goods_brands = (Terms) aggregationMap.get("goods_brands");

        List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();

        List brands = new ArrayList();
        for (Terms.Bucket bucket : buckets) {
            Object key = bucket.getKey();
            brands.add(key);
        }

        for (Object brand : brands) {
            System.out.println(brand);
        }

    }
}

十二、ES集群

1.ES集群的介绍
集群分布式的介绍.jpg
ES是天然的分布式的,隐藏了内部集群的复杂度,也就是说,我们可以通过简单的配置就可以实现集群的搭建,同时会负载均衡一些处理内部都已经处理好了
2.集群的相关概念

es集群介绍.jpg

3.ES集群的搭建

Elasticsearch如果做集群的话Master节点至少三台服务器或者三个Master实例加入相同集群,三个Master节点最多只能故障一台Master节点,如果故障两个Master节点,Elasticsearch将无法组成集群.会报错,Kibana也无法启动,因为Kibana无法获取集群中的节点信息。
由于,我们使用只有一台虚拟机,所以我们在虚拟机中安装三个ES实例,搭建伪集群,而ES启动比较耗内存,所以先设置虚拟机的内存3G和CPU个数4个

1.1.1 整体步骤

步骤如下:

  • 拷贝opt目录下的elasticsearch-7.4.0安装包3个,分别命名:
    elasticsearch-7.4.0-itcast1
    elasticsearch-7.4.0-itcast2
    elasticsearch-7.4.0-itcast3
  • 然后修改elasticsearch.yml文件件。
  • 然后启动启动itcast1、itcast2、itcast3三个节点。
  • 打开浏览器输⼊:http://192.168.149.135:9200/_cat/health?v ,如果返回的node.total是3,代表集 群搭建成功

在此,需要我们特别注意的是,像本文这样单服务器多节点( 3 个节点)的情况,仅供测试使用,集群环境如下:

cluster name node name IP Addr http端口 / 通信端口
itcast-es itcast1 192.168.149.135 9201 / 9700
itcast-es itcast2 192.168.149.135 9202 / 9800
itcast-es itcast3 192.168.149.135 9203 / 9900

1.1.2 拷贝副本

拷贝opt目录下的elasticsearch-7.4.0安装包3个,打开虚拟机到opt目录
执行 拷贝三份
cd /opt
cp -r elasticsearch-7.4.0 elasticsearch-7.4.0-itcast1
cp -r elasticsearch-7.4.0 elasticsearch-7.4.0-itcast2
cp -r elasticsearch-7.4.0 elasticsearch-7.4.0-itcast3

1.1. 3 修改elasticsearch.yml配置文件

1)、创建日志目录
cd /opt
mkdir logs
mkdir data
# 授权给itheima用户
chown -R itheima:itheima ./logs
chown -R itheima:itheima ./data
chown -R itheima:itheima ./elasticsearch-7.4.0-itcast1
chown -R itheima:itheima ./elasticsearch-7.4.0-itcast2
chown -R itheima:itheima ./elasticsearch-7.4.0-itcast3
打开elasticsearch.yml配置,分别配置下面三个节点的配置文件
vim /opt/elasticsearch-7.4.0-itcast1/config/elasticsearch.yml
vim /opt/elasticsearch-7.4.0-itcast2/config/elasticsearch.yml
vim /opt/elasticsearch-7.4.0-itcast3/config/elasticsearch.yml
2)、下面是elasticsearch-7.4.0-itcast1配置文件
cluster.name: itcast-es
node.name: itcast-1
node.master: true
node.data: true
node.max_local_storage_nodes: 3
network.host: 0.0.0.0
http.port: 9201
transport.tcp.port: 9700
discovery.seed_hosts: [“localhost:9700”,”localhost:9800”,”localhost:9900”]
cluster.initial_master_nodes: [“itcast-1”, “itcast-2”,”itcast-3”]
path.data: /opt/data
path.logs: /opt/logs
#集群名称
cluster.name: itcast-es
#节点名称
node.name: itcast-1
#是不是有资格主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#ip地址
network.host: 0.0.0.0
#端口
http.port: 9201
#内部节点之间沟通端口
transport.tcp.port: 9700
#es7.x 之后新增的配置,节点发现
discovery.seed_hosts: [“localhost:9700”,”localhost:9800”,”localhost:9900”]
#es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举master
cluster.initial_master_nodes: [“itcast-1”, “itcast-2”,”itcast-3”]
#数据和存储路径
path.data: /opt/data
path.logs: /opt/logs
3)、下面是elasticsearch-7.4.0-itcast2配置文件
cluster.name: itcast-es
node.name: itcast-2
node.master: true
node.data: true
node.max_local_storage_nodes: 3
network.host: 0.0.0.0
http.port: 9202
transport.tcp.port: 9800
discovery.seed_hosts: [“localhost:9700”,”localhost:9800”,”localhost:9900”]
cluster.initial_master_nodes: [“itcast-1”, “itcast-2”,”itcast-3”]
path.data: /opt/data
path.logs: /opt/logs
#集群名称
cluster.name: itcast-es
#节点名称
node.name: itcast-2
#是不是有资格主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#ip地址
network.host: 0.0.0.0
#端口
http.port: 9202
#内部节点之间沟通端口
transport.tcp.port: 9800
#es7.x 之后新增的配置,节点发现
discovery.seed_hosts: [“localhost:9700”,”localhost:9800”,”localhost:9900”]
#es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举master
cluster.initial_master_nodes: [“itcast-1”, “itcast-2”,”itcast-3”]
#数据和存储路径
path.data: /opt/data
path.logs: /opt/logs
4)、下面是elasticsearch-7.4.0-itcast3 配置文件
cluster.name: itcast-es
node.name: itcast-3
node.master: true
node.data: true
node.max_local_storage_nodes: 3
network.host: 0.0.0.0
http.port: 9203
transport.tcp.port: 9900
discovery.seed_hosts: [“localhost:9700”,”localhost:9800”,”localhost:9900”]
cluster.initial_master_nodes: [“itcast-1”, “itcast-2”,”itcast-3”]
path.data: /opt/data
path.logs: /opt/logs
#集群名称
cluster.name: itcast-es
#节点名称
node.name: itcast-3
#是不是有资格主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#ip地址
network.host: 0.0.0.0
#端口
http.port: 9203
#内部节点之间沟通端口
transport.tcp.port: 9900
#es7.x 之后新增的配置,节点发现
discovery.seed_hosts: [“localhost:9700”,”localhost:9800”,”localhost:9900”]
#es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举master
cluster.initial_master_nodes: [“itcast-1”, “itcast-2”,”itcast-3”]
#数据和存储路径
path.data: /opt/data
path.logs: /opt/logs

1.1.4 执行授权

在root用户下执行
chown -R itheima:itheima /opt/elasticsearch-7.4.0-itcast1
chown -R itheima:itheima /opt/elasticsearch-7.4.0-itcast2
chown -R itheima:itheima /opt/elasticsearch-7.4.0-itcast3
如果有的日志文件授权失败,可使用(也是在root下执行)
cd /opt/elasticsearch-7.4.0-itcast1/logs
chown -R itheima:itheima ./
cd /opt/elasticsearch-7.4.0-itcast2/logs
chown -R itheima:itheima ./

cd /opt/elasticsearch-7.4.0-itcast3/logs
chown -R itheima:itheima ./*

1.1.5 启动三个节点

启动之前,设置ES的JVM占用内存参数,防止内存不足错误

vim /opt/elasticsearch-7.4.0-itcast1/bin/elasticsearch

可以发现,ES启动时加载/config/jvm.options文件
vim /opt/elasticsearch-7.4.0-itcast1/config/jvm.options

默认情况下,ES启动JVM最小内存1G,最大内存1G
-xms:最小内存
-xmx:最大内存
修改为256m

启动成功访问节点一:

可以从日志中看到:master not discovered yet。还没有发现主节点
访问集群状态信息 http://192.168.149.135:9201/_cat/health?v 不成功

启动成功访问节点二:

可以从日志中看到:master not discovered yet。还没有发现主节点master node changed.已经选举出主节点itcast-2
访问集群状态信息 http://192.168.149.135:9201/_cat/health?v 成功

健康状况结果解释:
cluster 集群名称
status 集群状态
green代表健康;
yellow代表分配了所有主分片,但至少缺少一个副本,此时集群数据仍旧完整;
red 代表部分主分片不可用,可能已经丢失数据。
node.total代表在线的节点总数量
node.data代表在线的数据节点的数量
shards 存活的分片数量
pri 存活的主分片数量 正常情况下 shards的数量是pri的两倍。
relo迁移中的分片数量,正常情况为 0
init 初始化中的分片数量 正常情况为 0
unassign未分配的分片 正常情况为 0
pending_tasks准备中的任务,任务指迁移分片等 正常情况为 0
max_task_wait_time任务最长等待时间
active_shards_percent正常分片百分比 正常情况为 100%
启动成功访问节点三
访问集群状态信息 http://192.168.149.135:9201/_cat/health?v 成功

可以看到节点已经变为3个,至此,ES集群已经搭建成功~

十三 使用Kibana配置和管理集群

1.2.1 集群配置

因为之前我们在单机演示的时候也使用到了Kibana,我们先复制出来一个Kibana,然后修改它的集群配置
cd /opt/
cp -r kibana-7.4.0-linux-x86_64 kibana-7.4.0-linux-x86_64-cluster
# 由于 kibana 中文件众多,此处会等待大约1分钟的时间
修改Kibana的集群配置
vim kibana-7.4.0-linux-x86_64-cluster/config/kibana.yml
加入下面的配置
elasticsearch.hosts: [“http://localhost:9201","http://localhost:9202","http://localhost:9203“]
启动Kibana
sh kibana —allow-root

1.2.2 管理集群

1、打开Kibana,点开 Stack Monitoring 集群监控

2、点击【Nodes】查看节点详细信息

在上图可以看到,第一个红框处显示【Green】,绿色,表示集群处理健康状态
第二个红框是我们集群的三个节点,注意,itcast-3旁边是星星,表示是主节点