微服务实用篇5-分布式搜索elasticsearch篇1
Posted nuist__NJUPT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务实用篇5-分布式搜索elasticsearch篇1相关的知识,希望对你有一定的参考价值。
今天的主要学习任务是分布式搜索,首先了解elasticsearch,然后学习索引库的操作、文档的操作、RestAPI等。elasticsearch是非常强大的开源搜索引擎,可以帮助我们从海量数据中快速定位到我们需要的内容。这一篇主要学习ES的基本使用,包括安装ES,安装kibana,安装分词器等,另外也学习了在java客户端实现索引库的增删改查和文档的增删改查。
目录
一、初识elasticsearch
1.1、了解ES
elasticsearch是非常强大的开源搜索引擎,可以帮助我们从海量数据中快速定位到我们需要的内容。elastic stack(ELK) 主要包含如下三部分,其中es是用于搜索和存储等,是该技术栈的核心,另外还包括数据抓取和数据可视化的技术栈。
下面对elasticsearch做个小总结吧,es是开源的分布式搜索引擎,用来搜索、日志统计、分析等,ELK是以ES为核心的技术栈,包括数据可视化的Kibana和数据抓取的Logstash和Beats。另外Lucene是开源搜索引擎类库,提供搜索引擎核心的API。es底层是基于Lucene实现的。
1.2、 倒排索引
我们先看一下正向索引,比如mysql就是典型的采用正向索引,例如根据内容进行搜索,比对成功,则存储,比对失败,则丢弃。
正向索引的搜索是一条一条的搜索,而倒排搜索是将数据划分成词条和文档 ,每次搜索前先分词,然后根据词条进行搜索,找到文档id,最后再根据文档id进行搜索,将搜索到的结果按照顺序存入结果集。在复杂的场景适合用倒排索引进行搜索,效率更高。
1.3、ES与MySQL概念对比
我们看一下MySQL和es的概念对比,表对应索引,行对应文档,列对应字段,schema对应映射Mapping,SQL对应DSL。
对于数据安全要求较高的写相关操作,一般使用MySQL数据库,对于性能要求较高的读相关操作,一般使用Elasticsearch完成。MySQL和ES之间也可以实现数据同步。
1.4、安装ES
下面看一下部署单点ES,我们需要先创建网络,因为我们还需要部署kibana容器,因此需要让es和kibana容器互联。这里先创建一个网络:当然需要先启动docker,再创建网络:
docker network create es-net
接下来就是加载es镜像了,一般来说,可以直接从服务器拉取,但是由于es镜像比较大,我们先下载到本地,然后通过xftp上传到虚拟机,然后使用load命令加载压缩包为镜像即可。
docker load -i es.tar
把kibana也上传到虚拟机,然后用load命令加载镜像,如下:
docker load -i kibana.tar
接下来就可以运行docker命令,部署单点es,如下:
命令解释:
● -e "cluster.name=es-docker-cluster" :设置集群名称
● -e "http.host=0.0.0.0" :监听的地址,可以外网访问
● -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" :内存大小
● -e "discovery.type=single-node" :非集群模式
● -v es-data:/usr/share/elasticsearch/data :挂载逻辑卷,绑定es的数据目录
● -v es-logs:/usr/share/elasticsearch/logs :挂载逻辑卷,绑定es的日志目录
● -v es-plugins:/usr/share/elasticsearch/plugins :挂载逻辑卷,绑定es的插件目录
● --privileged :授予逻辑卷访问权
● --network es-net :加入一个名为es-net的网络中
● -p 9200:9200 :端口映射配置
docker run -d \\
--name es \\
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \\
-e "discovery.type=single-node" \\
-v es-data:/usr/share/elasticsearch/data \\
-v es-plugins:/usr/share/elasticsearch/plugins \\
--privileged \\
--network es-net \\
-p 9200:9200 \\
-p 9300:9300 \\
elasticsearch:7.12.1
在浏览器输入ip地址+端口号检验es是否安装成功,如下所示出现如下json字符串则安装成功:
1.5、安装kibana
kibana可以给我们提供一个elasticsearch的可视化界面,便于我们学习,我们首先运行docker,部署kibana,具体如下所示:
docker run -d \\
--name kibana \\
-e ELASTICSEARCH_HOSTS=http://es:9200 \\
--network=es-net \\
-p 5601:5601 \\
kibana:7.12.1
安装完成后可以在浏览器使用ip地址+端口号的方式打开可视化界面,如下:
1.6、安装IK分词器
有常见的两种方式安装IK分词器,分别在线安装和离线安装,在线安装的docker命令如下,不过该方法比较慢。
# 进入容器内部
docker exec -it elasticsearch /bin/bash
# 在线下载并安装
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip
#退出
exit
#重启容器
docker restart elasticsearch
另外一种安装IK分词器的方式是离线安装,具体如下,安装插件需要知道elasticsearch的plugins目录位置,而我们用了数据卷挂载,因此需要查看elasticsearch的数据卷目录,通过下面命令查看:
docker volume inspect es-plugins
plugins目录被挂载到了: /var/lib/docker/volumes/es-plugins/_data 这个目录中,我们将解压的ik分词器上传到这个目录中,然后重启容器即可。
docker restart es
最后测试IK分词的两种模式:在es的可视化界面使用Dev Tools进行测试即可,如下:
● ik_smart :最少切分
● ik_max_word :最细切分
1.7、IK分词器的扩展与停用
ik分词器的扩展是很有必要的,因为词库是有限的,对于新的词汇,我们需要对分词器进行拓展,对于没有必要的分词,或者敏感词汇,也可以设置不进行分词,即停用,防止浪费内存。
1)首先打开ik分词器的config目录:
2)在IKAnalyzer.cfg.xml配置文件扩展词典和停用词典,如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict"></entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<entry key="ext_dict">ext.dic</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<entry key="ext_stopwords">stopword.dic</entry>
</properties>
3)在config文件夹内创建扩展和停用的文件,并设置相应的词语。
4)重启elasticsearch
docker restart es
docker restart kibana
5)测试
二、索引库操作
2.1、mapping映射属性
mapping常见的属性包括数据类型、是否索引、分词器、子字段等,常见的数据类型包括:文本或键值对字符串,数字,布尔类型,日期和对象类型等。
2.2、创建、查询、删除、修改索引库
Elasticsearch通过Restful请求操作索引库和文档,请求内容通过DSL语句来表示,创建索引库和mapping的DSL如下:
下面在es的可视化界面的使用Dev Tools创建索引库,如下:
#创建索引库
PUT /wang
"mappings":
"properties":
"info":
"type": "text",
"analyzer": "ik_smart"
,
"email":
"type": "keyword"
, "index": false
,
"name":
"type": "object",
"properties":
"firstname":
"type": "keyword"
,
"lastname":
"type": "keyword"
如下所示,表明创建索引成功。
查询索引库用GET,删除索引库用DELETE,修改索引库用PUT,只能添加新字段,不能修改之前的字段。
#查询索引库
GET /wang
#删除索引
DELETE /wang
#修改索引,添加新字段
PUT /wang/_mapping
"properties":
"age":
"type":"long"
2.3、文档的添加、查询、删除、修改
创建文档使用POST,查询文档用GET、删除文档用DELETE、修改文档主要有两种,一种是全量修改,另一种是增量修改。
#查询文档
GET /wang/_doc/1
#删除文档
DELETE /wang/_doc/1
#全量修改,修改文档
PUT /wang/_doc/1
"info":"Java学习之路,慢慢来",
"email":"huangzhong@qq.com",
"name":
"firstname":"忠",
"lastname":"黄"
#局部修改文档
POST /wang/_update/1
"doc":
"email":"zhangfei@126.com"
三、RestClient操作
3.1、RestClient操作索引库
我们要通过Java的客户端操作实现索引库的增删改查相关操作,首先需要导入demo,然后定于mapping映射,通过RestClient完成索引库的相关操作。
初始化Java客户端,需要先引入依赖,并使用相应的IP地址何端口号进行初始化。
创建库索引,如下主要分为三步,创建request对象,准备请求发送,最后是发送请求。
import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static cn.itcast.hotel.constants.HotelConstants.MAPPING;
public class HotelIndexTest
private RestHighLevelClient client ;
@Test
void testInits()
System.out.println(client);
@Test
void TestInit() throws IOException
//1.创建request对象
CreateIndexRequest createIndexRequest = new CreateIndexRequest("hotel") ;
//2.准备请求的参数
createIndexRequest.source(MAPPING, XContentType.JSON) ;
//3.发送请求
client.indices().create(createIndexRequest, RequestOptions.DEFAULT) ;
@BeforeEach
void setUp()
this.client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.132.128:9200"))) ;
@AfterEach
void tearDown() throws IOException
this.client.close() ;
因为是通过DSL语句创建索引库,所以这里定义了一个MAPPING常量,通过静态导包的方式加入,如下:
public class HotelConstants
public static final String MAPPING = "\\n" +
"\\"mappings\\": \\n" +
"\\n" +
"\\"properties\\": \\n" +
"\\n" +
"\\"id\\":\\n" +
"\\n" +
"\\"type\\": \\"keyword\\"\\n" +
",\\n" +
"\\"name\\":\\n" +
"\\n" +
"\\"type\\": \\"text\\",\\n" +
"\\"analyzer\\": \\"ik_max_word\\",\\n" +
"\\"copy_to\\": \\"all\\"\\n" +
",\\n" +
"\\"address\\":\\n" +
"\\n" +
"\\"type\\": \\"keyword\\",\\n" +
"\\"index\\": false\\n" +
",\\n" +
"\\"price\\":\\n" +
"\\n" +
"\\"type\\": \\"integer\\"\\n" +
",\\n" +
"\\"score\\":\\n" +
"\\n" +
"\\"type\\": \\"integer\\"\\n" +
",\\n" +
"\\"brand\\":\\n" +
"\\n" +
"\\"type\\":\\"keyword\\",\\n" +
"\\"copy_to\\": \\"all\\"\\n" +
",\\n" +
"\\"city\\":\\n" +
"\\n" +
"\\"type\\": \\"keyword\\"\\n" +
",\\n" +
"\\"starName\\":\\n" +
"\\n" +
"\\"type\\": \\"keyword\\"\\n" +
",\\n" +
"\\"business\\":\\n" +
"\\n" +
"\\"type\\": \\"keyword\\",\\n" +
"\\"copy_to\\": \\"all\\"\\n" +
",\\n" +
"\\"location\\":\\n" +
"\\n" +
"\\"type\\": \\"geo_point\\"\\n" +
",\\n" +
"\\"pic\\":\\n" +
"\\n" +
"\\"type\\": \\"keyword\\",\\n" +
"\\"index\\": false\\n" +
",\\n" +
"\\"all\\":\\n" +
"\\n" +
"\\"type\\": \\"text\\",\\n" +
"\\"analyzer\\": \\"ik_max_word\\"\\n" +
"\\n" +
"\\n" +
"\\n" +
"" ;
删除索引库何判断索引库是否存在的代码如下:
//判断索引库是否存在
@Test
void TestExists() throws IOException
//1.创建request对象
GetIndexRequest getIndexRequest = new GetIndexRequest("hotel") ;
//2.发送请求
boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT) ;
//3.输出
System.out.println(exists ? "索引库存在" : "索引库不存在");
//删除索引库
@Test
void TestDelete() throws IOException
//1.创建request对象
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("hotel") ;
//2.发送请求
client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT) ;
3.2、RestClient操作文档
我们来看一下使用RestClient对文档进行增删改查的相关操作,具体如下:
首先需要初始化Java客户端,然后新增酒店数据到索引库,然后从索引库查询、删除、修改数据。
插入文档的代码如下,首先需要从数据库查询文档,这里面之前报了一个空指针异常,在测试类上加了@SpringBootTest注解后解决了,首先从数据库中查询,然后转换为文档类型,最后根据请求和Json对象发送新增文档请求即可。
@Test //文档新增
void addDocument() throws IOException
//根据id查询酒店数据
Hotel hotel = iHotelService.getById(38665L) ;
//转换为文档类型
HotelDoc hotelDoc = new HotelDoc(hotel) ;
//1.创建request对象
IndexRequest indexRequest = new IndexRequest("hotel").id(hotel.getId().toString()) ;
//2.准备Json对象
indexRequest.source(JSON.toJSONString(hotelDoc), XContentType.JSON) ;
//3.发送请求
client.index(indexRequest, RequestOptions.DEFAULT) ;
可在es的可视化界面中使用指令查询索引库中添加的文档,如下:
根据id查询酒店数据,具体如下:
@Test //文档查询
void getDocumentById() throws IOException
//1.创建request对象
GetRequest getRequest = new GetRequest("hotel", "38665") ;
//2.发送请求,得到响应
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT) ;
//3.解析响应的结果
String json = getResponse.getSourceAsString() ;
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class) ;
System.out.println(hotelDoc);
根据id进行文档的修改,也就是更新,就是全量更新和局部更新。
@Test //文档更新
void updateDocumentById() throws IOException
//1.创建request对象
UpdateRequest updateRequest = new UpdateRequest("hotel", "38665") ;
//2.准备请求参数
updateRequest.doc("price","999","starName","3钻") ;
//3.发送请求
client.update(updateRequest,RequestOptions.DEFAULT) ;
接下来,我们进行删除文档,如下:
@Test //文档删除
void deleteDocumentById() throws IOException
//1.创建request对象
DeleteRequest deleteRequest = new DeleteRequest("hotel", "38665") ;
//2.发送请求
client.delete(deleteRequest,RequestOptions.DEFAULT) ;
最后对文档操作做个总结:首先需要初始化客户端,然后创建请求对象,准备参数,发送请求,最后解析请求得到的结果即可。
最后我们在看一个批量导入数据到ES中,就是将文档数据批量导入到索引库中,具体如下:
下面是文档批量导入的代码,如下:
@Test //文档批量处理
void bulkDocumentById() throws IOException
//批量出巡酒店数据
List<Hotel> list = iHotelService.list() ;
//1.创建request对象
BulkRequest bulkRequest = new BulkRequest() ;
//转换为文档类型
for(Hotel hotel : list)
HotelDoc hotelDoc = new HotelDoc(hotel) ;
//创建文档的request对象
bulkRequest.add(new IndexRequest("hotel")
.id(hotel.getId().toString())
.source(JSON.toJSONString(hotelDoc),XContentType.JSON)) ;
//2.发送请求
client.bulk(bulkRequest,RequestOptions.DEFAULT) ;
以上是关于微服务实用篇5-分布式搜索elasticsearch篇1的主要内容,如果未能解决你的问题,请参考以下文章
实用篇SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud分布式