【ElasticSearch - 自学笔记】

ElasticSearch

1、分布式、Restful风格免费开源的搜索引擎

elastic:可伸缩,灵活;search:查询的意思

2、数据分为三大类:结构化数据、非结构化数据、半结构化数据

2.1、结构化数据

对于结构化数据,我们会用特定的结构来组织和管理数据,一般表现为二维表结构。

例如:mysql、oracle,并可以通过sql语句进行查询,为了提高效率,可以采用一些索引的方式优化查询。

优点:方便查询;

缺点:扩展结构比较困难

2.2、非结构化数据

无法用二维表结构来表现数据的数据,维度广,数据量大,一般会将此类数据以key-value结构保存到NoSQL数据库中,比如Redis、MongonDB;

例如:服务器日志,通信记录、工作文档、报表、视频、图片

缺点:存储和查询的数据量非常大,需要专业的人员和统计模型来进行处理

2.3、半结构化数据

将内容和结构混合在一起,例如xml、html类似的文档就是半结构化数据;一般也是保存到Redis、之类的非关系型数据库

3、The Elastic Stack,包括ElasticeSearch(用于存储搜集数据)、Kibana(展示数据)、Beats和Logstash(采集和传输数据),也被称为ELK;

能够安全可靠的获取任何来源、任何格式的数据,然后实时的对数据进行搜索、分析和可视化。

ES是一个开源的、高扩展的、分布式的全文搜索引擎,是整个ELK技术栈的核心。可以近乎实时的存储、检索数据。扩展性也比较好,可拓展到上百台服务器,处理PB级别的数据。

全文搜索,我们可以理解成全站搜索。例如在博客网站,用户可以通过热门词汇、关键字进行搜索,查询所有网站中匹配的文章,并且以列表的形式展现结果。但是在传统数据库利用sql进行搜索时,效率低下,即使有sql优化,但性能依旧很差,所以在生产环境中效果是比较差的,这就要求我们使用全文检索的搜索引擎。

3.1、ES、Solr/SolrCloud区别

特征Solr/SolrCloudElasticSearch
社区和开发者Apache软件基金会和社区支持单一商业实体及其员工
节点发现Apache Zookeeper 在大量项目中成熟且经过实战测试Zen内置于ElsshSearch本身,需要专用的节点才能进行分裂脑保护
碎片放置本质上是静态,需要手工工作来迁移分片,从Solr7开始,AutoScaling API允许一些动态操作动态,可以根据集群状态按需移动分片
高速缓存全局,每个端段更改无效每段,更适合动态更改数据
分析引擎性能非常适合精确计算的静态字段结果的准确性取决于数据放置
全文搜索功能基于Lucene的语言分析,多建议:拼写检查,丰富的高亮显示支持基于Lucene的语言分析,单一建议API是实现,高亮显示重新计算
DevOps支持尚未完成非常好的API
非平面数据处理嵌套文档和父-子支持嵌套和对象类型类型的自然支持允许几乎无限的嵌套和父-子支持
查询DSLJSON(有限)、XML(有限)、或者URL参数JSON
索引/收集领导控制领导者安置控制和领导着重新平衡甚至可以节点上的负载不可能
机器学习内置-在流聚合之上,专注于逻辑回归和学习排名贡献模块商业功能,专注于异常和异常值以及时间序列数据

3.2、为什么选择ES?

3.2.1、易于使用,一个下载和一个命令就可以启动一切
3.2.2、除了搜索文本之外还可以用来处理分析查询
3.2.3、可以满足分布式索引、良好可伸缩性以及性能分布式环境的要求
3.2.4、在开源日志管理用例中占据主导地位
3.2.5、暴露了更多的关键指标

4、ES入门

4.1、下载软件

ES官网地址:https://www.elastic.co/cn/

ES下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch

4.2、9300端口为ES集群间组件的通信端口,9200端口为浏览器访问的http协议RESTful端口;

启动可以直接打开bin/elasticsearch的终端窗口

当ES软件启动之后,可以通过访问http://localhost:9200,如果出现以下信息,则代表启动成功

{
  "name" : "MacBook-Air.local",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "5DTqNfehQk2kXns6pQpFzw",
  "version" : {
    "number" : "7.8.0",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65",
    "build_date" : "2020-06-14T19:35:50.234439Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

4.3、问题解决

4.3.1、ES是使用java开发的,且7.8版本的ES需要使用JDK1.8以上,默认安装包带有jdk环境,如果系统配置JAVA_HOME,那么使用系统默认的JDK,如果没有使用自带的JDK,一般建议使用系统配置的JDK
在这里插入图片描述

4.3.2、双击启动,窗口闪退,通过路径访问追踪错误,如果是“空间不足”,请修改config/jvm.options配置文件
#设置JVM初始内存为1G。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存
#Xmx represents the initial size of total heap space
#设置JVM最大内存为1G

-Xms1g
-Xmx1g
4.3.3、ES软件的发送和返回结构都是JSON格式的
4.3.4、如果直接通过浏览器向ES发送请求,那么需要在发送的请求中包含HTTP标准的方法,而HTTP的大部分特性且仅支持GET和POST方法

5、数据格式

5.1、ES是面向文档型数据库,一条数据就是一个文档。

ES -> MySQL
index(索引) -> Database(数据库)
Type(类型) -> Table(表)
Documents(文档) -> Row(行)
Fields(字段) -> Column(列)

5.2、正排索引和倒排索引

正排索引
id			content
-------------------
1001		my name is zhang san
1002		my name is li si


倒排索引
keyword			id
---------------------
name				10011002
zhang				1001

//Type的概念在被弱化,ES6.X中,一个index下只能包含一个type
//ES7.X中,Type的概念已经被删除了

6、ES基础操作

6.1、创建索引

想要对ES中的数据进行操作,首先要有索引(类比关系型数据库就等同于创建数据库)

APIfox中,向ES服务器发送PUT请求:
//http://127.0.0.1:9200 :代表ES服务器地址
//shopping :代表索引
http://127.0.0.1:9200/shopping


//响应结果
{
    "acknowledged": true,
    "shards_acknowledged": true,
    "index": "shopping"
}

//再次发送PUT请求,显示该索引已经存在
{
    "error": {
        "root_cause": [
            {
                "type": "resource_already_exists_exception",
                "reason": "index [shopping/bNlj8fcKTbyLOdOCowOSvw] already exists",
                "index_uuid": "bNlj8fcKTbyLOdOCowOSvw",
                "index": "shopping"
            }
        ],
        "type": "resource_already_exists_exception",
        "reason": "index [shopping/bNlj8fcKTbyLOdOCowOSvw] already exists",
        "index_uuid": "bNlj8fcKTbyLOdOCowOSvw",
        "index": "shopping"
    },
    "status": 400
}

6.2、获取索引信息

APIfox中,向ES服务器发送GET请求:
//http://127.0.0.1:9200 :代表ES服务器地址
//shopping :代表索引,获取指定索引信息
http://127.0.0.1:9200/shopping

//响应结果
{
    "shopping": {
        "aliases": {},
        "mappings": {},
        "settings": {
            "index": {
                "creation_date": "1699861966435",
                "number_of_shards": "1",
                "number_of_replicas": "1",
                "uuid": "bNlj8fcKTbyLOdOCowOSvw",
                "version": {
                    "created": "7080099"
                },
                "provided_name": "shopping"
            }
        }
    }
}


//获取ES中所有的索引信息,仍然使用GET请求
http://127.0.0.1:9200/_cat/indices?v

//响应结果
health status index    	uuid                   pri rep 
yellow open 	shopping	bNlj8fcKTbyLOdOCowOSvw 1 	 1
-------------------------------------------------------
docs.count docs.deleted store.size pri.store.size
0          0       			208b       208b

6.3、删除索引

APIfox中,向ES服务器发送DELETE请求:
//http://127.0.0.1:9200 :代表ES服务器地址
//shopping :代表索引,删除指定索引信息
http://127.0.0.1:9200/shopping

//响应结果
{
    "acknowledged": true
}

7、文档操作

7.1、创建文档

这里的文档类似于数据库中的表数据,添加的数据格式为JSON格式

APIfox中,向ES服务器发送POST请求
//_doc : 表示ES索引中添加文档
http://127.0.0.1:9200/shopping/_doc

请求体内容
{
	"title":"小米手机",
  "category":"小米",
  "images":"http://www.gulixueyuan.com/xm.jpg",
  "price":3999.00
}


//如果没有请求体,则会报错
{
    "error": {
        "root_cause": [
            {
                "type": "parse_exception",
                "reason": "request body is required"
            }
        ],
        "type": "parse_exception",
        "reason": "request body is required"
    },
    "status": 400
}

//正确发送后的响应结果
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "3GLHx4sBqo4WFUWVgkxS",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

7.1.1、创建文档不能使用PUT请求,只能使用POST请求,响应结果中有一个"_id": “3GLHx4sBqo4WFUWVgkxS”,这就是数据的唯一性标识,类似我们的主键,同样的请求,多次执行之后,响应结果是不同的,这里的请求不是幂等性的,但是PUT请求必须是幂等性的,所以在此只能使用POST请求。

7.1.2、ES生成的唯一性标识不太好记,所以我们如果想自定义唯一标识,需要修改一下请求路径,在_doc后面添加id信息

APIfox中,向ES服务器发送POST请求
//_doc : 表示ES索引中添加文档
http://127.0.0.1:9200/shopping/_doc/1001

请求体内容
{
	"title":"小米手机",
  "category":"小米",
  "images":"http://www.gulixueyuan.com/xm.jpg",
  "price":3999.00
}

//响应结果
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 1,
    "_primary_term": 1
}

7.1.3、加上自定义id之后,每次POST请求的返回结果都是相同的,所以这个时候再去使用PUT请求,就可以发送了

APIfox中,向ES服务器发送POST请求
//_doc : 表示ES索引中添加文档
http://127.0.0.1:9200/shopping/_create/1001

7.2、查询文档

APIfox中,向ES服务器发送GET请求
//_doc : 表示查询ES指定索引中的文档
http://127.0.0.1:9200/shopping/_doc/1001

//响应结果,found为true,表示找到该文档
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 1,
    "_seq_no": 1,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "title": "小米手机",
        "category": "小米",
        "images": "http://www.gulixueyuan.com/xm.jpg",
        "price": 3999.00
    }
}

//如果查询一个不存在的id,响应结果会说found未找到
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1002",
    "found": false
}

//查询全部的文档信息,发送GET请求
http://127.0.0.1:9200/shopping/_search

7.3、修改:全量修改&局部修改

7.3.1、全量修改:不管发送多少次请求,ES文档数据都会被完全覆盖,所以可以使用PUT请求

APIfox中,向ES服务器发送GET请求
//_doc : 表示ES指定索引中的文档
http://127.0.0.1:9200/shopping/_doc/1001

//请求体
{
	"title":"华为手机",
    "category":"华为",
    "images":"http://www.gulixueyuan.com/xm.jpg",
    "price":3999.00
}

//响应结果,result为updated
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 2,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 7,
    "_primary_term": 1
}

7.3.2、局部修改

因为是局部修改,所以每次的返回结果都不是相同的,所以不能使用PUT方式,只能使用POST方式

APIfox中,向ES服务器发送GET请求
//_doc : 表示新增,如果使用_doc相当于做全量修改,_update表示局部修改
http://127.0.0.1:9200/shopping/_update/1001

//请求体
{
    "doc":{
        "title":"三星手机"
    }
}

//响应体
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1002",
    "_version": 2,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 10,
    "_primary_term": 1
}

//再次请求
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1002",
    "_version": 2,
    "_seq_no": 10,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "title": "三星手机",
        "category": "小米",
        "images": "http://www.gulixueyuan.com/xm.jpg",
        "price": 3999.0
    }
}

7.4、删除文档

//在APIfox中,向ES服务器发送GET请求
http://127.0.0.1:9200/shopping/_doc/1001

//响应结果
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 5,
    "result": "deleted",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 11,
    "_primary_term": 1
}

//再次对同一id发送删除请求,结果会显示not_found
{
    "_index": "shopping",
    "_type": "_doc",
    "_id": "1001",
    "_version": 1,
    "result": "not_found",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "_seq_no": 12,
    "_primary_term": 1
}

8、多条件查询

8.1、请求体添加条件查询参数

//在APIfox发送GET请求
http://127.0.0.1:9200/shopping/_search?q=category:小米

但是这样发送查询请求,容易造成乱码情况,所以一般把条件参数写在请求体中,推荐将条件查询数据写入到请求体中

//在APIfox中发送GET请求
http://127.0.0.1:9200/shopping/_search

//请求体
{
  	//代表条件查询
    "query":{
      	//match:匹配
        "match":{
          	//具体条件
            "title":"三星"
        }
    }
}

//全量查询的请求体
{
  "query":{
    "match_all":{
      
    }
  }
}

//分页查询的请求体
{
  "query":{
    "match_all":{
      
    }
  },
  "from":0,
  "size":2
}
//想查询第几页,就把from的值改一下:(页码-1)*每夜数据条数

//对source数据进行过滤
{
  "query":{
    "match_all":{
      
    }
  },
  "from":0,
  "size":2,
  "_source":["title"]
}

//对查询结果进行排序
{
  "query":{
    "match_all":{
      
    }
  },
  "from":0,
  "size":2,
  "_source":["title"],
  "sort":{
    "price":{
      "order":"asc/desc"
    }
  }
}

8.2、多个条件组合在一起

//多条件同时成立,查询 价格为5999的小米手机
{
    "query" : {
        "bool" : {
            "must" : [
                {
                    "match" : {
                        "category" : "小米"
                    }
                },{
                    "match" :{
                        "price" : 5999
                    }
                }
            ]
        }
    }
}
//must意味着必须满足,类似于and


//满足多个条件中的其中一个 
{
    "query" : {
        "bool" : {
            "should" : [
                {
                    "match" : {
                        "category" : "小米"
                    }
                },{
                    "match" :{
                        "category" : "华为"
                    }
                }
            ]
        }
    }
}


//范围查询,价格大于5000
{
    "query" : {
        "bool" : {
            "should" : [
                {
                    "match" : {
                        "category" : "小米"
                    }
                },{
                    "match" :{
                        "category" : "华为"
                    }
                }
            ],
            "filter" : {
                "range" : {
                    "price" : {
                        "gt" : 5000
                    }
                }
            }
        }
    }
}

8.2.1、全查询(全文检索) & 精准匹配 & 高亮显示

在ES保存文档数据的时候,ES会将文字进行分词拆解处理,并将拆解之后的数据保存到倒排索引中,这样的话,即使使用文字的一部分,也可以查询到数据,这种检索方式我们称为全文检索。

ES也会对查询内容进行分词处理,在倒排索引中进行匹配

//使用category的一部分进行查询,也可以查询到数据
{
    "query" : {
        "match" : {
            "category" : "为"
        }
    }
}


//精准匹配
{
    "query" : {
        "match_phrase" : {
            "category" : "小华"
        }
    }
}

//对查询结果进行高亮显示
{
    "query" : {
        "match":{
            "category" : "小米"
        }
    },
    "highlight" : {
        "fields" : {
            "category" : {}
        }
    }
}

8.2.2、对查询结果进行分组或统计分析

//对价格字段进行分组
{
    "aggs" : { //聚合操作
        "price_group" : { //分组名称,随意起名,无特殊含义
            "terms" : {  //分组
                "field" : "price"   //分组字段
            }
        }
    },
    "size" : 0  //表示不需要获取原始数据
}

//求平均值操作
{
    "aggs" : { //聚合操作
        "price_avg" : { //分组名称,随意起名,无特殊含义
            "avg" : {  //分组
                "field" : "price"   //分组字段
            }
        }
    },
    "size" : 0  //表示不需要获取原始数据
}

9、映射关系

ES的查询中有的查询可以分词查询,有的却不可以分词查询,必须要全部匹配;

分词还是不分词该如何界定呢?

在mysql中,一张表的字段、类型、长度信息都属于表的结构信息,在ES中也有类似的概念,我们称为映射

//1、首先创建一个索引
PUT http://localhost:9200/user

//response
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "user"
}

//2、创建user的结构信息
PUT http://localhost:9200/user/_mapping

//body
{
    "properties" : {
        "name" : {
            "type" : "text", //text类型可以进行分词
            "index" : true    //表示这个字段可以进行索引查询
        },
        "sex" : {
            "type" : "keyword", //keyword类型不可以进行分词,只能完整匹配
            "index" : true    //表示这个字段可以进行索引查询
        },
        "tel" : {
            "type" : "keyword", //keyword类型不可以进行分词,只能完整匹配
            "index" : false    //表示这个字段不可以进行索引查询
        }
    }
}

//response
{
  "acknowledged": true
}

//3、再进行查询创建的user结构信息
GET http://localhost:9200/user/_mapping

//response
{
    "user": {
        "mappings": {
            "properties": {
                "name": {
                    "type": "text"
                },
                "sex": {
                    "type": "keyword"
                },
                "tel": {
                    "type": "keyword",
                    "index": false
                }
            }
        }
    }
}

//4、进行查询操作
GET http://127.0.0.1:9200/user/_search
//4.1、查询name
//body
{
    "query":{
        "match":{
            "name":"张三"
        }
    }
}
//发现无论是“张“,”三“,还是”张三“,都可以查到数据,因为type是text,可以进行分词处理

//4.2、查询sex性别
//body
{
    "query":{
        "match":{
            "sex":"男的"
        }
    }
}
//发现只能完全匹配的时候才可以查询到数据,因为type为keyword,不能进行分词处理

//4.3、查询电话号
//body
{
    "query":{
        "match":{
            "tel":"1111"
        }
    }
}

//response
{
    "error": {
        "root_cause": [
            {
                "type": "query_shard_exception",
                "reason": "failed to create query: Cannot search on field [tel] since it is not indexed.",
                "index_uuid": "aCS_ON0TR1GxNZNVlwX4pA",
                "index": "user"
            }
        ],
        "type": "search_phase_execution_exception",
        "reason": "all shards failed",
        "phase": "query",
        "grouped": true,
        "failed_shards": [
            {
                "shard": 0,
                "index": "user",
                "node": "zhbdoDsoRBKoxxqJbwT-Cg",
                "reason": {
                    "type": "query_shard_exception",
                    "reason": "failed to create query: Cannot search on field [tel] since it is not indexed.",
                    "index_uuid": "aCS_ON0TR1GxNZNVlwX4pA",
                    "index": "user",
                    "caused_by": {
                        "type": "illegal_argument_exception",
                        "reason": "Cannot search on field [tel] since it is not indexed."
                    }
                }
            }
        ]
    },
    "status": 400
}

//发现不能使用tel进行查询,因为index=false,是没有建立索引查询的,所以不能使用tel进行查询

10、Java API操作

Elasticsearch软件是由Java语言开发的,所以也可以通过Java API的方式对ES服务进行访问

10.1、创建新的maven工程

//使用IDEA工具创建新的maven工程,到导入一下依赖
<!--添加依赖关系-->
    <dependencies>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>7.8.0</version>
        </dependency>
        <!--es的客户端-->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.8.0</version>
        </dependency>
        <!--es依赖的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单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

10.2、客户端对象

创建一个package:com.td.es.test,并创建一个class:ESTest_client,代码中创建Elasticsearch客户端对象,因为早起版本的客户端对象已经不推荐使用,且在未来版本中会删除,所以我们采用高级REST客户端对象:RestHighLevelClient

public class ESTest_client {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient EsClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //关闭客户端
        EsClient.close();
    }
}

10.3、创建索引

public class ESTest_index_create {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //创建索引
        CreateIndexRequest request = new CreateIndexRequest("newuser");
        CreateIndexResponse response = esClient.indices().create(request , RequestOptions.DEFAULT);
        boolean acknowledged = response.isAcknowledged();
        System.out.println("索引创建 :"+ acknowledged);

        //关闭客户端
        esClient.close();
    }
}

//需要注意的是,索引名称必须是小写,否则会报错
"error":{"root_cause":[{"type":"invalid_index_name_exception","reason":"Invalid index name [newUser], must be lowercase","index_uuid":"_na_","index":"newUser"}],"type":"invalid_index_name_exception","reason":"Invalid index name [newUser], must be lowercase","index_uuid":"_na_","index":"newUser"},"status":400}

10.4、查询索引

public class ESTest_index_search {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //查询索引
        GetIndexRequest request = new GetIndexRequest("newuser");
        GetIndexResponse getIndexResponse = esClient.indices().get(request, RequestOptions.DEFAULT);

        System.out.println(getIndexResponse.getAliases());
        System.out.println(getIndexResponse.getMappings());
        System.out.println(getIndexResponse.getSettings());

        //关闭客户端
        esClient.close();
    }
}

10.5、删除索引

public class ESTest_index_delete {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //删除索引
        DeleteIndexRequest request = new DeleteIndexRequest("newuser");
        AcknowledgedResponse response = esClient.indices().delete(request, RequestOptions.DEFAULT);

        System.out.println(response.isAcknowledged());

        //关闭客户端
        esClient.close();
    }
}

10.6、创建文档对象

public class NewUser implements Serializable {
    private String name;
    private String sex;
    private String tel;
		
  //get set 方法
}

10.7、添加文档对象

public class ESTest_doc_create {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //创建文档
        IndexRequest request = new IndexRequest();
        //添加编号
        request.index("newuser").id("1001");
        //创建user对象
        NewUser user = new NewUser();
        user.setName("zhangsan");
        user.setSex("男");
        user.setTel("1111");
        //向ES插入数据,数据格式必须是JSON格式
        ObjectMapper mapper = new ObjectMapper();
        String userJson = mapper.writeValueAsString(user);
        request.source(userJson , XContentType.JSON);
        //获取响应结果
        IndexResponse response = esClient.index(request, RequestOptions.DEFAULT);

        System.out.println(response.getResult());

        //关闭客户端
        esClient.close();
    }
}

10.8、修改doc

public class ESTest_doc_update {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //创建文档
        UpdateRequest request = new UpdateRequest();
        //获取想要修改的doc
        request.index("newuser").id("1002");
        //局部修改的内容
        request.doc(XContentType.JSON,"sex","女");
        //获取响应结果
        UpdateResponse response = esClient.update(request, RequestOptions.DEFAULT);

        System.out.println(response.getResult());

        //关闭客户端
        esClient.close();
    }
}

10.9、查询doc

public class ESTest_doc_search {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //创建请求
        GetRequest request = new GetRequest();
        //获取想要search的doc
        request.index("newuser").id("1002");
        //获取响应结果
        GetResponse response = esClient.get(request, RequestOptions.DEFAULT);
        System.out.println(response.getSourceAsString());

        //关闭客户端
        esClient.close();
    }
}

10.10、删除doc

public class ESTest_doc_delete {
    public static void main(String[] args) throws Exception{

        //创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //创建请求
        DeleteRequest request = new DeleteRequest();
        //获取想要删除的doc
        request.index("newuser").id("1002");
        //获取响应结果
        DeleteResponse response = esClient.delete(request, RequestOptions.DEFAULT);

        System.out.println(response.getResult());

        //关闭客户端
        esClient.close();
    }
}

11、Java API高级用法

11.1、批量添加doc

public class ESTest_doc_batch_create {
    public static void main(String[] args) throws IOException {
        //1.创建ES连接
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //2.创建批量请求
        BulkRequest request = new BulkRequest();
        //批量请求中添加单个请求
        request.add(new IndexRequest().index("newuser").id("1001").source(XContentType.JSON , "name" , "张三"));
        request.add(new IndexRequest().index("newuser").id("1002").source(XContentType.JSON , "name" , "李四"));
        request.add(new IndexRequest().index("newuser").id("1003").source(XContentType.JSON , "name" , "王五"));

        //3.发起请求,获取响应
        BulkResponse response = esClient.bulk(request, RequestOptions.DEFAULT);

        //4.打印响应结果
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("一共添加了"+response.getItems().length+"条数据:");

        //关闭连接
        esClient.close();
    }
}

11.2、批量删除数据

public class ESTest_doc_batch_delete {
    public static void main(String[] args) throws IOException {
        //1.创建ES连接
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );

        //2.创建批量删除请求
        BulkRequest request = new BulkRequest();
        //批量请求中添加单个请求
        request.add(new DeleteRequest().index("newuser").id("1001"));
        request.add(new DeleteRequest().index("newuser").id("1002"));
        request.add(new DeleteRequest().index("newuser").id("1003"));

        //3.发起请求,获取响应
        BulkResponse response = esClient.bulk(request, RequestOptions.DEFAULT);

        //4.打印响应结果
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("一共删除了"+response.getItems().length+"条数据:");

        //关闭连接
        esClient.close();
    }
}

11.3、全量查询数据

public class ESTest_doc_matchAll {
    public static void main(String[] args) throws IOException {
        //1.创建ES连接
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );
        //2.创建请求
        SearchRequest request = new SearchRequest();
        request.indices("newuser");
        //3.添加查询条件
        request.source(new SearchSourceBuilder().query(QueryBuilders.matchAllQuery()));
        //4.获取响应
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);

        SearchHits hits = response.getHits();
        System.out.println("命中的数据条数"+hits.getTotalHits());
        System.out.println("花费的时间:"+response.getTook());
        //打印查询出来的数据
        for (SearchHit hit:hits) {
            System.out.println(hit.getSourceAsString());
        }

        //关闭连接
        esClient.close();
    }
}

11.4、条件查询

public class ESTest_doc_termQuery {
    public static void main(String[] args) throws IOException {
        //1.创建ES连接
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost",9200,"http"))
        );
        //2.创建请求
        SearchRequest request = new SearchRequest();
        request.indices("newuser");
        //3.添加查询条件
        request.source(new SearchSourceBuilder().query(QueryBuilders.termQuery("sex","女")));
        //4.获取响应
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
        //打印查询出来的数据
        SearchHits hits = response.getHits();
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("命中的条数:"+hits.getTotalHits());
        for(SearchHit hit : hits){
            System.out.println(hit);
        }
        //关闭连接
        esClient.close();
    }
}

11.5、分页查询+结果排序+结果过滤

public class ESTest_doc_pageQuery {
    public static void main(String[] args) throws IOException {
        //1.创建ES连接
        RestHighLevelClient esClient = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost",9200,"Http")));
        //2.创建请求
        SearchRequest request = new SearchRequest();
        request.indices("newuser");
        //3.添加查询条件
        SearchSourceBuilder condition = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
        //添加分页信息
        condition.from(0);
        condition.size(2);
        //添加排序规则
        condition.sort("tel",SortOrder.DESC);
        //添加过滤信息
        String[] incloud = {};
        String[] excloud = {"name"};
        condition.fetchSource(incloud , excloud);
        request.source(condition);
        //4.获取响应
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
        //打印查询出来的数据
        SearchHits hits = response.getHits();
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("命中的条数:"+hits.getTotalHits());
        for(SearchHit hit : hits){
            System.out.println(hit);
        }
        //关闭连接
        esClient.close();
    }
}

11.6、组合查询

public class ESTest_doc_combination_query {
    public static void main(String[] args) throws IOException {
        //创建ES连接
        RestHighLevelClient esClient = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost",9200,"http")));

        //创建请求
        SearchRequest request = new SearchRequest();
        request.indices("newuser");
        //添加条件
        SearchSourceBuilder condition = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        //must 必须满足
        // boolQueryBuilder.must(QueryBuilders.matchQuery("sex","男"));
        // boolQueryBuilder.must(QueryBuilders.matchQuery("tel","1111"));
        //mustNot 必须不满足
        // boolQueryBuilder.mustNot(QueryBuilders.matchQuery("tel","1111"));
        //should 相当于or,可以是...也可以是...
        boolQueryBuilder.should(QueryBuilders.matchQuery("tel","1111"));
        boolQueryBuilder.should(QueryBuilders.matchQuery("tel","2222"));
        condition.query(boolQueryBuilder);

        //发送请求
        request.source(condition);
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);

        //打印查询出来的数据
        SearchHits hits = response.getHits();
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("命中的条数:"+hits.getTotalHits());
        for(SearchHit hit : hits){
            System.out.println(hit);
        }
        //关闭连接
        esClient.close();

    }
}

11.7、范围查询

public class ESTest_doc_range_query {
    public static void main(String[] args) throws IOException {
        //创建连接
        RestHighLevelClient esClient = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));

        //创建请求
        SearchRequest request = new SearchRequest();
        request.indices("newuser");

        //创建条件
        SearchSourceBuilder condition = new SearchSourceBuilder();
        RangeQueryBuilder rangedQuery = QueryBuilders.rangeQuery("age");

        //添加条件 大于等于23,小于40
        rangedQuery.gte(22);
        rangedQuery.lt(40);
        condition.query(rangedQuery);

        request.source(condition);
        //发送请求,获取响应
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
        //打印查询出来的数据
        SearchHits hits = response.getHits();
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("命中的条数:"+hits.getTotalHits());
        for(SearchHit hit : hits){
            System.out.println(hit);
        }
        //关闭连接
        esClient.close();
    }
}

11.8、模糊查询

public class ESTest_doc_fuzzy_query {
    public static void main(String[] args) throws IOException {
        //创建连接
        RestHighLevelClient esClient = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost" , 9200 , "http")));

        //创建请求
        SearchRequest request = new SearchRequest();
        request.indices("newuser");

        //添加条件
        SearchSourceBuilder condition = new SearchSourceBuilder();
        //模糊查询 查询zhangsan,偏差可以有两个字符
        condition.query(QueryBuilders.fuzzyQuery("name","zhangsan").fuzziness(Fuzziness.TWO));

        request.source(condition);
        //发起请求,获取响应
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);

        //打印查询出来的数据
        SearchHits hits = response.getHits();
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("命中的条数:"+hits.getTotalHits());
        for(SearchHit hit : hits){
            System.out.println(hit);
        }
        //关闭连接
        esClient.close();

    }
}

11.9、分组查询

public class ESTest_doc_agg_query {
    public static void main(String[] args) throws IOException {
        //创建连接
        RestHighLevelClient esClient = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost" ,9200, "http")));

        //创建请求
        SearchRequest request = new SearchRequest();
        request.indices("newuser");

        //添加条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //求年龄最大值
        // AggregationBuilder aggregationBuilder = AggregationBuilders.max("maxAge").field("age");
        //根据年龄进行分组
        AggregationBuilder aggregationBuilder = AggregationBuilders.terms("ageGroup").field("age");
        builder.aggregation(aggregationBuilder);

        request.source(builder);
        //发送请求,获取响应
        SearchResponse response = esClient.search(request, RequestOptions.DEFAULT);
        //打印查询出来的数据
        SearchHits hits = response.getHits();
        System.out.println("花费的时间:"+response.getTook());
        System.out.println("命中的条数:"+hits.getTotalHits());
        for(SearchHit hit : hits){
            System.out.println(hit);
        }
        //关闭连接
        esClient.close();
    }
}

12、Elasticsearch环境

12.1、相关概念

12.1.1、单机 & 集群

单台Elasticsearch服务器提供服务,往往都有最大的负载能力,超过这个阈值,服务器的性能就会大大降低甚至不可用,所以生产环境中,一般都是运行在指定服务器集群中。

除了负载能力,单点服务器还存在其他问题:

  • 单台机器存储容量有限
  • 但服务器容易出现单点故障,无法实现高可用
  • 单服务器的并发处理能力有限

集群是把多个节点整体作为一个服务

配置服务器集群时,集群中节点数量没有限制,大于等于2个节点就可以看作是集群了。一般出于高性能及高可用方面来考虑集群中节点数量都是3个以上。

12.1.2、集群 Cluster

一个集群就是由一个或者多个服务器节点组织在一起,共同持有整个的数据,并一起提供索引和搜索功能。一个Elasticsearch集群有一个唯一的名字表示,这个名字默认就是"elasticsearch"。这个名字是比较重要的,因为一个节点只能通过指定某个集群的名字来加入这个集群。

12.1.3、节点Node

集群中包含很多服务器,一个节点就是其中的一个服务器。作为集群的一部分,它存储数据,参与集群的索引和搜索功能。

一个节点也是有一个名字来标识,默认情况下,这个名字是一个随机的漫威漫画角色的名字,这个名字会在启动的时候赋予节点。这个名字对于管理工作来说挺重要的,因为在这个管理过程中,你会去确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。

一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做"elasticsearch"的集群中,这意味着,如果你在你的网络中启动了若干个节点,并假定他们能够相互发现彼此,它们将会自动地形成并加入到一个叫做"elasticsearch"的集群中。

12.2、Windows集群

12.2.1、部署集群
  1. 创建elasticsearch-cluster文件夹,在内部复制三个elasticsearch服务(但是因为es之前使用过,所以在复制之前,需要把data目录和logs目录清空) 在这里插入图片描述

  2. 修改集群文件目录中每个节点的config/elasticsearch.yml配置文件

    • Node-1001节点

      #节点1的配置信息
      #集群名称,节点之间要保持一致
      cluster.name: my-application
      #节点名称,集群内要唯一
      node.name: node-1
      node.master: true     //可以是主节点
      node.data: true     //可以是数据节点
      #IP地址
      network.host: localhost
      #http端口
      http.port: 9201
      #tcp端口,通信端口
      transport.tcp.port: 9301
      #discovery.seed_hosts: ["localhost:9301","localhost:9302","localhost:9303"]
      #discovery.zen.fd.ping_timeout: 1m
      #discovery.zen.fd.ping_retries: 5
      #集群内的可以被选为主节点的列表
      #cluster.initial_master_nodes: ["node-1001","node-1002","node-1003"]
      #跨域配置
      #action.destructive_requires_name: true
      http.cors.enabled: true
      http.cors.allow-origin: "*"
      
    • 访问集群状态

      • http://localhost:9201/_cluster/health
    • 节点为1001的时候,启动报错java.net.BindException

      • 原因是在linux系统中只有root用户才可以访问1024以下的端口号,其余用户不可访问
    • node-1002节点

    #节点1的配置信息
    #集群名称,节点之间要保持一致
    cluster.name: my-application
    #节点名称,集群内要唯一
    node.name: node-2
    node.master: true     //可以是主节点
    node.data: true     //可以是数据节点
    #IP地址
    network.host: localhost
    #http端口
    http.port: 9202
    #tcp端口,通信端口
    transport.tcp.port: 9302
    #ES中特殊的查找模块,用来查找master节点的模块
    discovery.seed_hosts: ["localhost:9301"]
    discovery.zen.fd.ping_timeout: 1m
    discovery.zen.fd.ping_retries: 5
    #集群内的可以被选为主节点的列表
    #cluster.initial_master_nodes: ["node-1001","node-1002","node-1003"]
    #跨域配置
    #action.destructive_requires_name: true
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
    • node-1003节点
    #节点1的配置信息
    #集群名称,节点之间要保持一致
    cluster.name: my-application
    #节点名称,集群内要唯一
    node.name: node-3
    node.master: true     //可以是主节点
    node.data: true     //可以是数据节点
    #IP地址
    network.host: localhost
    #http端口
    http.port: 9203
    #tcp端口,通信端口
    transport.tcp.port: 9303
    #ES中特殊的查找模块,用来查找master节点的模块
    discovery.seed_hosts: ["localhost:9301","localhost:9302"]
    discovery.zen.fd.ping_timeout: 1m
    discovery.zen.fd.ping_retries: 5
    #集群内的可以被选为主节点的列表
    #cluster.initial_master_nodes: ["node-1001","node-1002","node-1003"]
    #跨域配置
    #action.destructive_requires_name: true
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
  3. 启动之前先删除每个节点中的data和logs目录中所有内容(如果存在);分别双击执行bin/elasticsearch.bat,启动节点服务器,启动之后,会自动加入指定名称的集群

  4. 查询集群状态

GET		http://localhost:9201/_cluster/health
//response
{
    "cluster_name": "my-application",
    "status": "green",
    "timed_out": false,
    "number_of_nodes": 3,
    "number_of_data_nodes": 3,
    "active_primary_shards": 0,
    "active_shards": 0,
    "relocating_shards": 0,
    "initializing_shards": 0,
    "unassigned_shards": 0,
    "delayed_unassigned_shards": 0,
    "number_of_pending_tasks": 0,
    "number_of_in_flight_fetch": 0,
    "task_max_waiting_in_queue_millis": 0,
    "active_shards_percent_as_number": 100.0
}

12.3、Linux单机

12.3.1、软件下载

软件下载地址:https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-8-0
在这里插入图片描述

12.3.2、软件安装
1、解压软件
//1、先将下载的软件上传至服务器
//2、解压缩
tar -axvf elasticsearch-7.8.0-linux-x86_64.tar.gz -C /opt/module
//3、改名
mv elasticsearch-7.8.0 es
2、创建用户

因为安全问题,Elasticsearch不允许root用户直接运行,所以要创建新用户,在root用户中创建新用户

//新增es用户
useradd es
//为es用户设置密码
passwd es
//如果错了,可以删除再加
userdel -r es
//文件夹所有者
chown -R es:es /opt/module/es
3、修改配置文件

修改/opt/module/es/config/elasticsearch.yml文件

//加入如下配置
cluster.name: elasticsearch
node.name: nade-1
network.host: 0.0.0.0
http.port: 9200
cluster.initial_master_nodes: ["node-1"]

修改/etc/security/limits.conf

//在文件末尾中增加下面内容
//每个进程可以打开的文件数的限制
es soft nofile 65536
es hard nofile 65536

修改/etc/security/limits.d/20-nproc.conf

//在文件末尾中增加下面内容
//每个进程可以打开的文件数的限制
es soft nofile 65536
es hard nofile 65536
//操作系统级别对每个用户创建的进程数的限制
* hard nproc 4096
//* 代表Linux所有用户名称

修改/etc/sysctl.conf

//在文件中增加下面的内容
//一个进程可以拥有的VMA(虚拟内存区域)的数量,默认值为65536
vm.max_map_count=655360

重新加载

sysctl -p
12.3.3、启动软件

使用ES用户启动

cd /opt/module/es/
//启动
bin/elasticsearch
//后台启动
bin/elasticsearch -d
  
//直接启动则会报错
java.lang.RuntimeException:can not run elasticsearch as root
//不能使用root用户直接进行操作,需要切换到刚刚创建的es用户进行启动
su es
//此时还是会报错
Exception in thread "main" org.elasticsearch.bootstrap.BootstrapException: java.nio.file.AccessDeniedException: /opt/module/es/config/elasticsearch.keystore
Likely root cause : java.nio.file.AccessDeniedException: /opt/module/es/config/elasticsearch.keystore
//这个问题的主要原因是我们在启动的过程中,会动态生成一些文件目录,这些目录的内容权限和ES没有关系
//我们可以切换到root用户,再执行一遍chown -R es:es /opt/module/es
su root
chown -R es:es /opt/module/es
su es
//然后再进行启动

12.4、Linux 集群

下载、安装、创建用户和单节点的操作是一样的

3、修改配置文件

修改/opt/module/es/config/elasticsearch.yml文件,分发文件

//加入如下配置
//集群名称
cluster.name: cluster-es
//节点名称。每个节点的名称不能重复
node.name: node-1
//IP地址。每个节点的地址不能重复
network.host: linux1
//是不是有资格成为主节点
node.master: true
//是不是数据节点
node.data: true
http.port: 9200
//head 插件需要打开这两个配置
http.cors.allow-origin: "*"
http.cors.enabled: true
http.max_content_length: 200mb
//es7.x之后新增的配置,初始化一个新的集群时需要此配置来选举maxter
cluster.initial_master_nodes: ["mode-1"]
//es7.x 之后新增的配置,节点发现
discovery.seed_hosts: ["linux1:9300","linux2:9300","linux3:9300"]
gateway.recover_after_nodex: 2
network.tcp.keep_alive: true
network.tcp.no_delay: true
transport.tcp.compress: true
//集群内同时启动的数据任务个数,默认是2个
cluster.routing.allocation.cluster_concurrent_rebalance: 16
//添加或删除节点及负载均衡时并发恢复的线程个数,默认是4个
cluster.routing.allocation.node_concurrent_recoveries: 16
//初始化数据恢复时,并发挥恢复线程的个数,默认是4个
  cluster.routing.allocation.node_initial_primaries_recoveries: 16
  • 31
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值