微服务实用篇5-分布式搜索elasticsearch篇1

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务实用篇5-分布式搜索elasticsearch篇1相关的知识,希望对你有一定的参考价值。

今天的主要学习任务是分布式搜索,首先了解elasticsearch,然后学习索引库的操作、文档的操作、RestAPI等。elasticsearch是非常强大的开源搜索引擎,可以帮助我们从海量数据中快速定位到我们需要的内容。这一篇主要学习ES的基本使用,包括安装ES,安装kibana,安装分词器等,另外也学习了在java客户端实现索引库的增删改查和文档的增删改查。

目录

一、初识elasticsearch

1.1、了解ES

1.2、 倒排索引

1.3、ES与MySQL概念对比

1.4、安装ES

1.5、安装kibana

1.6、安装IK分词器 

1.7、IK分词器的扩展与停用

二、索引库操作

2.1、mapping映射属性

2.2、创建、查询、删除、修改索引库

2.3、文档的添加、查询、删除、修改

三、RestClient操作

3.1、RestClient操作索引库

3.2、RestClient操作文档


一、初识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的主要内容,如果未能解决你的问题,请参考以下文章

微服务实用篇6-分布式搜索elasticsearch篇2

微服务实用篇6-分布式搜索elasticsearch篇2

微服务实用篇--学习笔记

SpringCloud之微服务实用篇1

SpringCloud之微服务实用篇1

实用篇SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud分布式