【微服务学习笔记】九、ElasticSearch搜索引擎

发布于:2022-10-27 ⋅ 阅读:(360) ⋅ 点赞:(0)

ElasticSearch搜索引擎

一、ElasticSearch介绍

ES是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容,可以用来实现搜索、日志统计、分析、系统监控等功能。

elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等领域。

elasticsearch是elastic stack的核心,复杂存储、搜索、分析数据。

img

image-20220804150019316

image-20220804150219187

小结:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SUTAvUsc-1666856773309)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220804150426844.png)]

二、倒排索引

首先,倒排索引的概念是基于 MySQL 这样的正向索引而言的。

那么我们先讲何为正向索引。例如给下表(tb_goods)中的 id 创建索引

img

如果是根据 id 查询,那么直接走索引,查询速度非常快。

但如果是基于 title 做模糊查询,只能是逐行扫描数据,流程如下:

  1. 用户搜索数据,条件是 title 符合 "%手机%"
  2. 逐行获取数据,比如 id 为 1 的数据
  3. 判断数据中的 title 是否符合用户搜索条件
  4. 如果符合则放入结果集,不符合则丢弃。然后回到步骤1

逐行扫描,也就是全表扫描,随着数据量增加,其查询效率也会越来越低。当数据量达到数百万时,就是。。。

而倒排索引中有两个非常重要的概念:

  • 文档(Document):用来搜索的数据,其中的每一条数据就是一个文档。例如一个网页、一个商品信息
  • 词条(Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条。例如:我是中国人,就可以分为:我、是、中国人、中国、国人这样的几个词条

创建倒排索引是对正向索引的一种特殊处理,流程如下:

  • 将每一个文档的数据利用算法分词,得到一个个词条
  • 创建表,每行数据包括词条、词条所在文档 id、位置等信息
  • 因为词条唯一性,可以给词条创建索引,例如 hash 表结构索引

如图:

img

倒排索引的搜索流程如下(以搜索"华为手机"为例)

  1. 用户输入条件"华为手机"进行搜索
  2. 对用户输入内容分词,得到词条:华为手机
  3. 拿着词条在倒排索引中查找,可以得到包含词条的文档 id 有 1、2、3
  4. 拿着文档 id 到正向索引中查找具体文档

虽然要先查询倒排索引,再查询正向索引,但是词条和文档id 都建立了索引,查询速度非常快!无需全表扫描。

为什么一个叫做正向索引,一个叫做倒排索引呢?

正向索引是最传统的,根据 id 索引的方式。但根据词条查询时,必须先逐条获取每个文档,然后判断文档中是否包含所需要的词条,是根据文档找词条的过程

倒排索引则相反,是先找到用户要搜索的词条,根据得到的文档 id 获取该文档。是根据词条找文档的过程

小结:

image-20220804152541507

三、ES与MySQL概念对比

1、文档和字段

elasticsearch是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式存储在elasticsearch中。

img

而 JSON 文档中往往包含很多的字段(Field),类似于数据库中的列。

2、索引和映射

索引(Index),就是相同类型的文档的集合。

例如:

  • 所有用户文档,就可以组织在一起,称为用户的索引;
  • 所有商品的文档,可以组织在一起,称为商品的索引;
  • 所有订单的文档,可以组织在一起,称为订单的索引;

img

因此,我们可以把索引当做是数据库中的表。

数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有映射(mapping),是索引中文档的字段约束信息,类似表的结构约束。

mysql 与 elasticsearch

MySQL Elasticsearch 说明
Table Index 索引(index),就是文档的集合,类似数据库的表(table)
Row Document 文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式
Column Field 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column)
Schema Mapping Mapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)
SQL DSL DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD
  • Mysql:擅长事务类型操作,可以确保数据的安全和一致性
  • Elasticsearch:擅长海量数据的搜索、分析、计算

因此在企业中,往往是两者结合使用:

  • 对安全性要求较高的写操作,使用 MySQL 实现
  • 对查询性能要求较高的搜索需求,使用 ELasticsearch 实现
  • 两者再基于某种方式,实现数据的同步,保证一致性

img

三、安装ES 、安装kibana 、安装IK分词器

1、安装ES:

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

docker network create es-net 

安装

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:端口映射配置

访问地址:http://192.168.211.128 即可看到 elasticsearch 的响应结果

img

参考视频p81-p83链接:05-初识ES-安装es_哔哩哔哩_bilibili

2、安装kibana

安装kibana

kibana 可以给我们提供一个 elasticsearch 的可视化界面,便于我们学习命令。

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:端口映射配置

访问地址:http://192.168.211.128,即可看到结果

img

控制面板:http://192.168.211.128/app/dev_tools#/console

3、安装IK分词器

由于国内访问 GitHub 较慢,我们选择离线模式安装。

安装插件需要知道 elasticsearch 的 plugins 目录位置,而我们用了数据卷挂载,因此需要查看 elasticsearch 的数据卷目录,通过下面命令查看

docker volume inspect es-plugins

显示结果:

[
    {
        "CreatedAt": "2022-05-06T10:06:34+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/es-plugins/_data",
        "Name": "es-plugins",
        "Options": null,
        "Scope": "local"
    }
]

说明 plugins 目录被挂载到了 /var/lib/docker/volumes/es-plugins/_data 这个目录中

img

重启容器

# 4、重启容器
docker restart es

# 查看es日志
docker logs -f es

IK分词器包含两种模式:

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

我们在上面的 Kibana 控制台测试

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "钟老师你好菜啊"
}

扩展词词典

在上面的IK分词器我们可以随着热点词来扩展,可以自己添加,比如 ”钟老师应该是一个热点词“,另外你也可以配置一些停用掉的敏感词,让其不进行分词。

打开IK分词器 config 目录是 IKAnalyzer.cfg.xml,添加一个文件名,我们以 ext.dic 文件名为例。

img

我们去创建 ext.dic ,在其中添加热点词就好了,一个词一行。

img

重启 elasticsearch

docker restart es

重新测试

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "钟老师你好菜啊"
}

img

IK分词器简介:

image-20220805085701641

四、操作索引库

image-20220805091124134

索引库就类似数据库表,mapping 映射就类似表的结构

我们要向 es 中存储数据,必须先创建“库”和“表”

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

  • type:字段数据类型,常见的简单类型有:
    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
    • 数值:long、integer、short、byte、double、float、
    • 布尔:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引,默认为 true
  • analyzer:使用哪种分词器
  • properties:该字段的子字段

我们以需要存储下面的 JSON 为例来讲解

{
    "age": 21,
    "weight": 52.1,
    "isMarried": false,
    "info": "钟老师真菜",
    "email": "jialna@qq.com",
    "score": [99.1, 99.5, 98.9],
    "name": {
        "firstName": "湖",
        "lastName": "心"
    }
}

首先对应的每个字段映射(mapping)情况如下:

  • age:类型为 integer;参与搜索,index 为 true;无需分词器
  • weight:类型为 float;参与搜索,index 为 true;无需分词器
  • isMarried:类型为boolean;参与搜索,index 为 true;无需分词器
  • info:类型为字符串,需要分词,因此是 text;参与搜索,index为true;分词器可以用 ik_smart
  • email:类型为字符串,但是不需要分词,因此是 keyword;不参与搜索,index 为 false;无需分词器
  • score:虽然是数组,但是我们只看元素的类型,类型为 float;参与搜索,index 为 true;无需分词器
  • name:类型为 object,需要定义多个子属性
    • name.firstName:类型为字符串,不需要分词,keyword;参与搜索,index 为 true;无需分词器
    • name.lastName:类型为字符串,不需要分词,keyword;参与搜索,index 为 true;无需分词器

1、创建索引库和映射

上面我们了解了 Mapping 属性映射,接下来我们就去看看如何创建索引库及映射。

PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2":{
        "type": "keyword",
        "index": "false"
      },
      "字段名3":{
        "properties": {
          "子字段": {
            "type": "keyword"
          }
        }
      }
      // ...略
    }
  }
}
PUT /xn2001
{
  "mappings": {
    "properties": {
      "info":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email":{
        "type": "keyword",
        "index": "false"
      },
      "name":{
        "properties": {
          "firstName": {
            "type": "keyword"
          },
          "lastName": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

img

我们用真实的数据库表来创建一个索引库

img

  • 字段名、字段数据类型,可以参考数据表结构的名称和类型
  • 是否参与搜索要分析业务来判断,例如图片地址,就无需参与搜索
  • 是否分词呢要看内容,内容如果是一个整体就无需分词
  • 分词器,我们可以统一使用 ik_max_word
PUT /hotel
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword",
        "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword",
        "copy_to": "all"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword"
      },
      "location":{
        "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

特殊字段说明:

  • location:地理坐标,里面包含精度、纬度
  • all:一个组合字段,其目的是将多字段的值利用 copy_to 合并,提供给用户搜索,这样一来就只需要搜索一个字段就可以得到结果,性能更好。

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

  • geo_point:由纬度(latitude)和经度(longitude)确定的一个点。例如:“32.8752345, 120.2981576”
  • geo_shape:有多个 geo_point 组成的复杂几何图形。例如一条直线,“LINESTRING (-77.03653 38.897676, -77.009051 38.889939)”

2、修改索引库

倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改 mapping

虽然无法修改 mapping 中已有的字段,但是却允许添加新的字段到 mapping 中,不会对倒排索引产生影响。

PUT /索引库名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

3、删除索引库

DELETE /索引库名

4、查询索引库

GET /数据库名

ongitude)确定的一个点。例如:“32.8752345, 120.2981576”

  • geo_shape:有多个 geo_point 组成的复杂几何图形。例如一条直线,“LINESTRING (-77.03653 38.897676, -77.009051 38.889939)”

2、修改索引库

倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改 mapping

虽然无法修改 mapping 中已有的字段,但是却允许添加新的字段到 mapping 中,不会对倒排索引产生影响。

PUT /索引库名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

3、删除索引库

DELETE /索引库名

4、查询索引库

GET /数据库名

五、文档操作

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到