Elasticsearch-分布式搜索

Elasticsearch-分布式搜索

1.初识Elasticsearch

1.1了解ES

ElasticSearch是一款非常强大的开源搜索引擎 可以帮助我们从海量数据中快速找到需要的内容

ElasticSearch结合kibana Logstash Beats 也就是ElasticStack(ELK) 被广泛应用在微服务的日志数据分析 实时监控等领域

ElasticSearch是ElasticStack的核心 负责存储 搜索 分析数据

1.2正向索引和倒排索引

传统数据库(如MySQL)采用正向索引 如给下表(tb_goods)中的id创建索引:

ElasticSearch采用倒排索引:

  • 文档(document):每条数据就是一个文档
  • 词条(term)不重复:文档按照语义分成的成语

1.3ES的一些概念

1.3.1文档

ElasticSearch是面向文档存储的 可以是数据库中的一条商品数据 一个订单信息

文档数据会被序列化为json格式后存储在ElasticSearch中

1.3.2索引

索引(index):相同类型的文档的集合

映射(mapping):索引中文档的字段约束信息 类似表的结构约束

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4L4rjus-1663344368121)(/Users/bobochang/Library/Application Support/typora-user-images/截屏2022-05-19 21.33.19.png)]

1.3.3MySQL与ElasticSearch概念对比
1.3.4架构

Mysql:擅长事务类型操作 可以确保数据的安全和一致性

ElasticSearch:擅长海量数据的搜索 分析 计算

1.4安装ES Kibana

1.4.1部署单点
1.4.1.1创建网络

因为还需要部署kibana容器 因此需要让es和kibana容器互联 这里先创建一个网络:

docker network create es-net
1.4.1.2加载镜像
docker load -i es.tar
1.4.1.3运行

运行docker命令 部署单点es:

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

命令解释:

  • -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:端口映射配置
1.4.2部署kibana

kibana可以给我们提供一个ElasticSearch的可视化界面 便于学习

1.4.2.1部署

运行docker命令 部署kibana

docker run -d \
  --name kibana \
    -e ELASTICSEARCH_HOSTS=http://es:9200 \
  --network=es-net \
  	-p 5601:5601 \
kibana:7.12.1
  • --network es-net :加入一个名为es-net的网络中 与elasticsearch在同一个网络中
  • -e ELASTICSEARCH_HOSTS=http://es:9200":设置elasticsearch的地址 因为kibana已经与elasticsearch在一个网络 因此可以用容器名直接访问elasticsearch
  • -p 5601:5601:端口映射配置
1.4.2.2运行测试

打开本地浏览器输入 虚拟机地址+端口(5601) 访问kibana网页地址

1.5分词器

es在创建倒排索引时需要对文档分词 在搜索时 需要对用户输入内容分词

但默认的分词规则对中文处理并不友好 在kibana的DevTools中测试:

POST /_analyz
{
   
	"analyzer":"standard",
	"text":"黑马程序员学习java太棒了!"
}

语法说明:

  • POST:请求方式

  • /_analyze:请求路径 这里省略了http://10.211.55.9(虚拟机地址):9200 有kibana帮我们补充

  • 请求参数 json风格:

    • analyzer:分词器类型 这里是默认的standard(标准)分词器
    • text:要分词的内容
1.5.1IK分词器

处理中文分词 一般会使用IK分词器

离线安装ik插件

  1. 查看数据卷目录
docker volume inspect es-plugins
  1. 将ik文件夹上传到es容器的插件数据卷中

也就是上传到/var/lib/docker/volumes/es-plugins/_data

  1. 重启容器并查看日志
#重启容器
docker restart es
#查看es日志
docker logs -f es

ik分词器包含两种模式:

  • ik_smart:最少切分 粗粒度

  • ik_max_word:最细切分 细粒度

1.5.2ik分词-拓展词库

随着互联网的发展 “造词运动”也越发的频繁 出现了很多新的词语 在原有的词汇列表中并不存在

所以我们的词汇也需要不断的更新 IK分词器提供了扩展词汇的功能

  1. 在ik分词器的config目录中往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">ext.dic</entry>
</properties>
  1. 新建一个 ext.dic 可以参考config目录下复制一个配置文件进行修改

  2. 最后重启elasticsearch服务并查看elasticsearch日志

#重启容器
docker restart elasticsearch
#查看es日志
docker logs -f elasticsearch
1.5.3ik分词器-停用词库

在互联网项目中 在网络间传输的速度很快 所以很多语言是不允许在网络上传递的

IK分词器也提供了强大的停用词功能 让我们在索引时就直接忽略当前的停用词汇表中的内容

  1. 在ik分词器的config目录中往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">ext.dic</entry>
         <!--用户可以在这里配置自己的扩展停止词字典  *** 添加停用词词典-->
        <entry key="ext_stopwords">stopword.dic</entry>
</properties>
  1. 在 stopword.dic 添加停用词

  2. 重启elasticsearch和kibana

#重启容器
docker restart elasticsearch
docker restart kibana
#查看es日志
docker logs -f elasticsearch
1.5.4总结

分词器的作用

  • 创建倒排索引时对文档分词
  • 用户搜索时 对输入的内容分词

IK分词器的模式?

  • ik_smart:智能切分 粗粒度
  • ik_max_word:最细切分 细粒度

IK分词器拓展词条 停用词条的方式

  • 利用config目录的IkAnalyzer.cfg.xml文件添加拓展词典和停用词典
  • 在词典中添加拓展词条或者停用词条

2.索引库操作

2.1mapping映射属性

mapping是对索引库中文档的约束 常见的mapping属性包括:

  • type:字段数据类型 常见的简单类型有:

    • 字符串:text(可分词的文本) keyword(精确值 例如:品牌 国家 ip地址)
    • 数值:long integer short byte double float
    • 布尔:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引 默认为true

  • analyzer:使用哪种分词器

  • properties:该字段的子字段

2.2索引库的CRUD

2.2.1创建索引库

ES中通过Restful请求操作索引库 文档 请求内容用DSL语句来表示

创建索引库和mapping的DSL语法如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0wkrm5Cb-1663344368121)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/5560/%E6%88%AA%E5%B1%8F2022-05-23%2001.18.29.png#alt=%E6%88%AA%E5%B1%8F2022-05-23%2001.18.29)]

2.2.2查看 删除索引库

查看索引库语法:

GET /索引库名
#example
GET /heima

删除索引库语法:

DELETE /索引库名
#example
DELETE /heima
2.2.3修改索引库

索引库和mapping一旦创建无法修改 但是可以添加新的字段 语法如下:

PUT /索引库名/_mapping
{
   
  "properties": {
   
    "新字段名":{
   
      "type": "integer"
    }
  }
}
#example
PUT /heima/_mapping
{
   
  "properties": {
   
    "age":{
   
      "type": "integer"
    }
  }
}
2.2.4总结

索引库操作

  • 创建索引库:PUT /索引库名
  • 查询索引库:GET /索引库名
  • 删除索引库:DELETE /索引库名
  • 添加字段:PUT /索引库名/_mapping

3.文档操作

3.1新增文档

新增文档的DSL语法:

POST /索引库名/_doc/文档id
{
   
    "字段1": "值1",
    "字段2": "值2",
    "字段3": {
   
        "子属性1": "值3",
        "子属性2": "值4"
    },
    // ...
}
POST /heima/_doc/1
{
   
    "info": "黑马程序员Java讲师",
    "email": "zy@itcast.cn",
    "name": {
   
        "firstName": "云",
        "lastName": "赵"
    }
}

3.2查询 删除文档

查询文档语法:

#语法
GET /索引库名/_doc/文档id

#示例
GET /heima/_doc/1

删除文档语法:

#语法
DELETE /索引库名/_doc/文档id 
#示例
DELTE /heima/_doc/1

3.3修改文档

方式一:全量修改 会删除旧文档 添加新文档

PUT /索引库名/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    // ... 略
}
PUT /heima/_doc/1
{
    "info": "黑马程序员高级Java讲师",
    "email": "zy@itcast.cn",
    "name": {
        "firstName": "云",
        "lastName": "赵"
    }
}

方式二:增量修改 修改指定字段值

POST /索引库名/_update/文档id
{
    "doc": {
         "字段名": "新的值",
    }
}
POST /heima/_update/1
{
  "doc": {
    "email": "ZhaoYun@itcast.cn"
  }
}
总结

文档操作

  • 创建文档:POST /索引库名/_doc/文档id { json文档 }

  • 查询文档:GET /索引库名/_doc/文档id

  • 删除文档:DELETE /索引库名/_doc/文档id

  • 修改文档:

    • 全量修改:PUT /索引库名/_doc/文档id { json文档 }
    • 增量修改:POST /索引库名/_update/文档id { “doc”: {字段}}

3.4Dynamic Mapping

当我们向ES中插入文档时 如果文档中字段没有对应的mapping ES会帮助我们字段设置mapping 规则如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6dNv3ET-1663344368121)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/5560/%E6%88%AA%E5%B1%8F2022-05-23%2011.53.24.png#alt=%E6%88%AA%E5%B1%8F2022-05-23%2011.53.24)]

  • 插入文档时 es会检查文档中的字段是否有mapping 如果没有则按照默认mapping规则来创建索引
  • 如果默认mapping规则不符合你的需求 一定要自己设置字段mapping

4.RestAPI-RestClient操作索引库

4.1RestClient概念

ES官方提供了各种不同语言的客户端用来操作ES 这些客户端本质就是组装DSL语句 通过http请求发送给ES

4.2案例-利用JavaRestClient实现创建 删除索引库和判断索引库是否存在

根据资料提供的酒店数据创建索引库 索引名为hotel mapping属性根据数据库结构定义

基本步骤:

  1. 导入资料Demo

  2. 分析数据结构 定义mapping属性

mapping映射需要考虑的问题:字段名 数据类型 是否参与搜素 是否分词 分词器类型是什么

CREATE TABLE `tb_hotel` (
  `id` bigint(20) NOT NULL COMMENT '酒店id',
  `name` varchar(255) NOT NULL COMMENT '酒店名称;例:7天酒店',
  `address` varchar(255) NOT NULL COMMENT '酒店地址;例:航头路',
  `price` int(10) NOT NULL COMMENT '酒店价格;例:329',
  `score` int(2) NOT NULL COMMENT '酒店评分;例:45  就是4.5分',
  `brand` varchar(32) NOT NULL COMMENT '酒店品牌;例:如家',
  `city` varchar(32) NOT NULL COMMENT '所在城市;例:上海',
  `star_name` varchar(16) DEFAULT NULL COMMENT '酒店星级  从低到高分别是:1星到5星  1钻到5钻',
  `business` varchar(255) DEFAULT NULL COMMENT '商圈;例:虹桥',
  `latitude` varchar(32) NOT NULL COMMENT '纬度;例:31.2497',
  `longitude` varchar(32) NOT NULL COMMENT '经度;例:120.3925',
  `pic` varchar(255) DEFAULT NULL COMMENT '酒店图片;例:/img/1.jpg',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ES中支持两种地理坐标数据类型:

  • geo_point:由纬度(latitude)和经度(longitude)确定的一个点。

例如:“32.8752345, 120.2981576”

  • geo_shape:有多个geo_point组成的复杂几何图形

例如一条直线 “LINESTRING(-77.03653 38.897676, -77.009051 38.889939)”

  1. 初始化JavaRestClient

    1. 引入es的RestHighLevelClient依赖
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
  2. SpringBoot默认ES版本是7.6.2 故需要覆盖默认的ES版本
<properties>
  <java.version>1.8</java.version>
  <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
  3. 初始化RestHighLevelClient
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://10.211.55.9:9200")));
4.2.1利用JavaRestClient创建索引库
@Test
void testCreateHotelIndex() throws IOException {
   
  // 1.创建Request对象
  CreateIndexRequest request = new CreateIndexRequest("hotel");
  / 2.请求参数 MAPPING_TEMPLATE是静态常量字符串 内容是创建索引库的DSL语句
  request.source(MAPPING_TEMPLATE, XContentType.JSON);
  // 3.发起请求
  client.indices().create(request, RequestOptions.DEFAULT);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R8szUAu1-1663344368122)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/5560/%E6%88%AA%E5%B1%8F2022-05-23%2013.15.39.png#alt=%E6%88%AA%E5%B1%8F2022-05-23%2013.15.39)]

4.2.2利用JavaRestClient删除索引库
@Test
void testDeleteHotelInde() throws IOException {
   
    // 1.创建Request对象
    DeleteIndexRequest request = new DeleteIndexRequest("hotel");
    // 2.发送请求
    restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
}
4.2.3利用JavaRestClient判断索引库是否存在
@Test
void testExistsHotelIndex() throws IOException {
   
    // 1.创建Request对象
    GetIndexRequest request = new GetIndexRequest("hotel");
    // 2.发送请求
    boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
    // 3.输出
    System.out.println(exists ? "索引库已经存在!" : "索引库不存在!");
}

5.RestAPI-RestClient操作文档

5.1案例-利用JavaRestClient实现文档的CRUD

去数据库查询酒店数据 导入到hotel索引库 实现酒店数据的CRUD

基本步骤如下:

  1. 初始化JavaRestClient

新建一个测试类 实现文档相关操作 并且完成JavaRestClient的初始化

public class HotelDocumentTest {
   

    private RestHighLevelClient restHighLevelClient;

    @BeforeEach
    void setUp() {
   
        this.restHighLevelClient = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://10.211.55.9:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
   
        this.restHighLevelClient.close();
    }
    
}
  1. 利用JavaRestClient新增酒店数据

先查询酒店数据 然后给这条数据创建倒排索引 即可完成添加:

@Test
void testAddDocument() throws IOException {
   
  // 根据id查询数据库中的hotel数据
  Hotel hotel = hotelService.getById(60223L);
  // 将hotel格式转换为hotelDoc格式
  HotelDoc hotelDoc = new HotelDoc(hotel);
  // 1.准备Request对象
  IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
  // 2.准备JSON文档
  request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
  // 3.发送请求
  restHighLevelClient.index(request, RequestOptions.DEFAULT);
}
  1. 利用JavaRestClient根据id查询酒店数据

根据id查询到的文档数据是json 需要反序列化为java对象:

@Test
void testGetDocumentById() throws IOException {
   
    // 1.准备Request对象
    GetRequest request = new GetRequest("hotel", "60223");
    // 2.发送请求 得到响应
    GetResponse reponse = restHighLevelClient.get(request, RequestOptions.DEFAULT);
    // 3.解析响应结果并将JSON结果反序列化为对象格式
    String source = reponse.getSourceAsString();
    Hotel hotel = JSON.parseObject(source, Hotel.class);
    System.out.println(hotel);
}
  1. 利用JavaRestClient删除酒店数据

修改文档数据有两种方式:

方式一:全量更新 再次写入id一样的文档 就会删除旧文档 添加新文档

方式二:局部更新 只更新部分字段

 @Test
void testUpdateDocumentById() throws IOException {
   
    // 1.准备Request对象
    UpdateRequest request = new UpdateRequest("hotel", "60223");
    // 2.准备参数 每2个参数为一对key value
    request.doc(
            "price", "2888"
    );
    // 3.更新文档
    restHighLevelClient.update(request, RequestOptions.DEFAULT);
}
  1. 利用JavaRestClient修改酒店数据
@Test
void testDeleteDocumentById() throws IOException {
   
    // 1.准备Request对象
    DeleteRequest request = new DeleteRequest("hotel", "60223");
    // 2.发送请求
    restHighLevelClient.delete(request,RequestOptions.DEFAULT);
}
总结

文档操作的基本步骤:

  • 初始化RestHighLevelClient
  • 创建XxxRequest XXX是Index Get Update Delete
  • 准备参数(Index和Update时需要)
  • 发送请求 调用RestHighLevelClient#.xxx()方法 xxx是index get update delete
  • 解析结果(Get时需要)

5.2案例-利用JavaRestClient批量导入酒店数据到ES

需求:批量查询酒店数据 然后批量导入索引库中

思路:

  1. 利用MyBatis-Plus查询数据库中的酒店数据
  2. 将查询到的酒店数据(Hotel)转换为文档类型数据(HotelDoc)
  3. 利用JavaRestClient中的Bulk批处理 实现批量新增文档
@Test
void testBulkRequest() throws IOException {
   
    // 获取数据库中的所有酒店数据
    List<Hotel> hotelList = hotelService.list();
    // 1.准备Request对象
    BulkRequest request = new BulkRequest();
    // 2.准备参数 添加多个新增的Request
    // 循环遍历将hotel数据转换为hotelDoc数据
    for(Hotel hotel : hotelList) {
   
        HotelDoc hotelDoc = new 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啵啵肠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值