概念
ElasticSearch ,简称为es,是一个开源的、高拓展的分布式全文检索引擎,它可以近乎实时的存储、检索数据。
本身拓展性很好,可以拓展到上百台服务器,处理PB级别的数据。ElasticSearch使用java开发并且使用Lucene作为其核心来实现所有索引和搜索的功能,但是他的目的是通过简单的RestFul API来隐藏Lucene的复杂性,从而让全文检索变得简单
与Solr的比较
ElasticSearch优势:
- es基本上是开箱即用、非常简单,Solr安装略麻烦;
- es自身带有分布式协调管理功能,而Solr利用Zookeeper进行分布式管理;
Solr优势:
- Solr支持更多格式的数据,例如:json,xml,csv,而ElasticSearch仅仅支持json文件格式;
- Solr官方提供的功能更多,而ElasticSearch本身更注重核心功能,高级功能多由第三方插件提供,例如图形化界面需要kibana友好支持;
- Solr比较成熟,有一个更大、更成熟的用户、开发和贡献者社区,而ElasticSearch相对开发维护者较少,更新太快,学习使用成本较高。
各自优势:
- Solr查询快但更新索引时慢,ElasticSearch建立索引快但查询慢
安装及启动
ElasticSearch要求安装环境必须是要jdk1.8以上才行。
ElasticSearch: https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D
kibana: https://mirrors.huaweicloud.com/kibana/?C=N&O=D
ElasticSearch安装
解压安装包完成解压
ElasticSearch目录环境说明
- bin:可执行文件
- config:配置文件
- elasticsearch.yml:项目配置文件
- jvm.options:jvm相关的配置文件
- log4j2.properties:日志相关配置文件
- jdk:相关的jdk环境
- lib:项目所使用的相关jar包
- logs:日志信息
- modules:模块信息
- plugins:插件信息
启动并访问
双击elasticsearch.bat文件启动
localhost:9200访问
Kibana
Kibana是一个针对ElasticSearch的开源分析及可视化平台,用于搜索、查看交互存储在ElasticSearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理解,它操作简单,基于浏览器的用户界面可以快速创建仪表板实时显示ElasticSearch查询动态。设置Kibana非常简单,无需编码或者额外的基础架构,几分钟就可以完成安装并启动索引监测。
注意事项:Kibana的版本必须要与安装的es版本一致。
解压安装包完成解压
启动Kibana并访问
localhost:5601访问
汉化
Kibana可以支持汉化,但要修改配置文件
重启之后:
ES核心概念
ElasticSearch是一个面向文档的数据库,其中的所有数据都是json
各种专用名词和关系型数据库的对比:ElasticSearch中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档,每个文档中又包含多个字段。
物理设计
ElasticSearch在后台把每个索引划分为多个分片,每份分片可以在集群的不同服务器间迁移。ElasticSearch一启动就是一个集群,哪怕只有一个,默认的集群名称为:elasticsearch。
逻辑设计
在ES中,一个索引中包含多个文档,当我们索引一篇文档时,可以通过这样的顺序找到它:索引>类型>文档ID。通过这个组合我们就能索引到某个具体的文档。
文档
文档的概念换算到关系型数据库中就类似于一条数据。
ES是面向文档的,也就意味着索引和搜索数据的最小单位是文档,在ElasticSearch中,文档有几个重要属性:
- 自我包含:一片文档同时包含字段和对应的值,也就是同时包含key-value;
- 可以是层次型的:一个文档包含另一个文档;
- 灵活的结构:文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用。在ElasticSearch中,对于字段是非常灵活的,有时候我们可以忽略该字段,或者动态的添加一个新的字段。
类型
类型是文档的逻辑容器,就像关系型数据库中表格是行的容器一样,类型对于字段的定义称之为映射,比如说
name
映射为字符串类型。
我们说文档是无模式的,我们不需要对我们新增的每一个字段的类型进行映射,在没有进行映射的时候Elasticsearch会对数据的类型进行猜测,但是也有可能会猜不对,所以最安全的方式提前定义好所需要的映射,这一部分就跟关系型数据库差不多了。
索引
索引就类似于关系型数据库中的数据库。
索引是映射类型的容器,ElasticSearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置,然后他们被保存到了各个分片上。
节点和分片如何工作
一个集群至少有一个节点,就是最基本的elasticsearch进程,每个节点可以有多个索引,如果你创建索引,则,默认会创建5个分片(又称主分片),每一个分片都会有一个副本(又称复制分片)。
例如这是一个有三个节点的集群,可以看到主分片对应的复制分片都不会在同一个节点内,这样就有利于如果某个节点挂掉了,数据也不至于丢失,实际上:一个分片是一个lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得ElasticSearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。
倒排索引
Lucene采用倒排索引作为底层,这种设计适用于快速的全文搜索。
倒排索引的做法是对索引中的每个单词都进行重构为一个索引列表, 这样就可以清楚的反应每个单词在文档中的位置,当我们想要查找某个数据的时候,根据倒排索引生成的索引列表就可以最大限度的避免不符合数据的重复查询,只会在包含该数据的文档中进行查询。
例如:下面数据中可以得知:python这个词条,在1,2,3号文档中都有出现,linux这个词条在3,4号文档中出现,当我们想要查询linux这个词条的时候,根据倒排索引生成的索引列表,就不会再去查询1,2这两个文档,最大限度的避免了无用数据的查询。
ik分词器插件
分词器概念
分词的意思就是把一段文字分成一个个的关键字,我们在搜索的时候会把自己的信息进行分词,会把数据库中的数据进行分词,然后进行一个匹配操作。ik分词器提供了两个分词算法:ik_smart和ik_max_word,其中ik_smart为最少切分,ik_max_word为最细粒度切分。
例如,我搜索”奥特曼打小怪兽“,在搜索结果中你可能看到只包含”奥特曼“的信息,也有可能看到只包含"小怪兽"的信息,这就是程序对我们的搜索信息进行了分词
安装分词器
https://github.com/medcl/elasticsearch-analysis-ik/releases(往下滑,找到自己的要的版本进行下载)
解压安装包完成解压
解压到plugins下,新建ik文件夹
重启ElasticSearch
重启可以在日志中观察到ik分词器插件被加载。
使用Kibana发送请求展示ik分词器效果
分词器会根据语句中的词语进行分割,但是他们怎么认使什么字连起来是一个词呢?这是因为在分词器的内部有一个自己的字典,可以识别常用的正常词语
GET _analyze //请求分词器
{
"analyzer": "ik_smart", //选择分词算法
"text": "奥特曼打小怪兽"
}
自定义ik分词器词典信息
有一些词默认的分词器无法识别,需要我们自定义完成它
现在,我们想要把“狂神说”当做一个完整的词进行分词入库,怎么操作呢?
1、打开ik插件,打开config文件夹,找到IKAnalyzer.cfg.xml文件
2、编写扩展字典,并纳入配置中
- 在配置同级,新建kuang.dic
- 写入“狂神说”,保存
- 引入配置
3、重启ES,查看效果
重启时,我们发现,配置中已引入kuang.dic字典文件
再次在Kibana测试,得到我们想要的结果
ElasticSearch基本操作
操作ElasticSearch的命令都是通过RestFul风格的请求命令去完成的
大致如下图:
创建一个索引
语法:
PUT /索引名/类型名/文档id
{
请求体
}
ElasticSearch相关的数据类型
- 字符串类型:text、keyword
- 数值类型:long、integer、short、byte、doule、float、half float、scaled float
- 日期类型:date
- 布尔值类型:boolean
- 二进制类型:binary
- 等等…
查看索引信息
通过GET
命令就可以查看到索引的信息,后面的目标准确到索引就查看索引信息,准确到文档就查看文档信息。
语法:
GET 索引名 or 类型名 or 文档id
修改文档信息
put
还是使用put,put既可以添加,也可以修改!(使用PUT进行修改有一个弊端,就是他会将所有的数据都进行覆盖,如果你修改的字段有缺漏,则缺漏的部分会被覆盖为空,造成数据的丢失)
语法:
PUT /索引名/类型名/文档id
{
请求体
}
现在修改之前的index1索引里面的数据,修改过后result会变成updated!
post
删除索引
DELETE操作和GET操作一样,后面的目标精确到文档就删除文档信息,精确到索引就删除索引信息。
语法:
DELETE 索引名 or 类型名 or 文档id
Springboot集成ES
新建boot项目
勾选相应的ES配置
也可以自己在maven配
修改pom的ES版本
此时的ES版本和我们自己的不一样,要在pom中进行配置
将ES对象注入到Spring容器之中
package com.example.demo.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author qijian
* @description ES配置类
* @date 2021/8/5 - 20:47
*/
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client=new RestHighLevelClient(
RestClient.builder(
//ES集群的相关信息,如果有多个就配置多个
new HttpHost("localhost",9200,"http")
)
);
return client;
}
}
至此,所以搭建已完成,接下来是如何运用ES
关于java操作ES索引的API
创建索引
1、写一个测试类
package com.example.demo.test;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
/**
* @author qijian
* @description EStest
* @date 2021/8/5 - 20:50
*/
@SpringBootTest
public class test {
@Autowired
@Qualifier("restHighLevelClient")
RestHighLevelClient restHighLevelClient;
//测试索引的创建
@Test
void createIndex() throws IOException {
//创建索引请求
CreateIndexRequest java_index = new CreateIndexRequest("java_index");
//客户端执行请求创建索引
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(java_index, RequestOptions.DEFAULT);
}
}
2、运行,查看Kibana
删除索引
1、测试删除
//测试删除索引
@Test
void deleteIndex() throws IOException {
DeleteIndexRequest java_index = new DeleteIndexRequest("java_index");
AcknowledgedResponse delete = restHighLevelClient.indices().delete(java_index, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged()); //获取删除成功与否的提示信息
}
2、运行,查看Kibana
关于java操作ES文档的API
添加文档
1、创建实体类
package com.example.demo.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
/**
* @author qijian
* @description 用户实体类
* @date 2021/8/5 - 21:00
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
private String name;
private int age;
}
2、测试添加文档
ES只支持json格式的数据流通,所以在将对象放入请求的过程中需要将对象序列化为josn字符串
//测试添加文档
@Test
void addDocument() throws IOException {
//创建对象
User user=new User();
user.setName("张三");
user.setAge(18);
//创建请求
IndexRequest java_index = new IndexRequest("java_index");
//填充规则
java_index.id("1"); //文档编号
//将对象放入请求中
java_index.source(JSONUtil.toJsonStr(user), XContentType.JSON);
//客户端发送请求,接收响应结果
IndexResponse index = restHighLevelClient.index(java_index, RequestOptions.DEFAULT);
//打印响应结果
System.out.println(index.toString()); //查看返回的具体json信息
System.out.println(index.status()); //查看操作的状态
}
输出:
获取文档信息
//测试获取文档信息
@Test
void getDocument() throws IOException {
GetRequest java_index = new GetRequest("java_index", "1");
GetResponse getResponse = restHighLevelClient.get(java_index, RequestOptions.DEFAULT);
System.out.println(getResponse.getSourceAsString()); //打印文档的内容
System.out.println(getResponse); //getResponse对象就包含ES的所有查询信息
}
修改文档信息
//测试修改文档信息
@Test
void updateDocument() throws IOException {
UpdateRequest java_index = new UpdateRequest("java_index", "1");
User user=new User();
user.setName("法外狂徒张三");
user.setAge(20);
java_index.doc(JSONUtil.toJsonStr(user),XContentType.JSON);
UpdateResponse update = restHighLevelClient.update(java_index, RequestOptions.DEFAULT);
System.out.println(update.status()); //查看更新状态
}
删除文档信息
//删除文档信息
@Test
void deleteDocument() throws IOException {
DeleteRequest java_index = new DeleteRequest("java_index", "1");
DeleteResponse delete = restHighLevelClient.delete(java_index,RequestOptions.DEFAULT);
System.out.println(delete.status()); //查看删除状态
}
批量操作
ES同时也支持批量增删改的操作
//测试批量添加文档下信息
@Test
void bulkDocument() throws IOException {
//创建批量操作对象
BulkRequest bulkRequest = new BulkRequest();
ArrayList<User> list = new ArrayList<>();
list.add(new User("张三1",12));
list.add(new User("张三2",12));
list.add(new User("张三3",12));
list.add(new User("张三4",12));
list.add(new User("张三5",12));
for (int i=0;i<list.size();i++){
bulkRequest.add(
new IndexRequest("java_index").id(""+(i+1))
.source(JSONUtil.toJsonStr(list.get(i)),XContentType.JSON));
}
//发送请求
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures()); //查看状态,是否失败,返回false代表成功
}