ElasticSearch

ElasticSearch:

官方网站:https://www.elastic.co

认识和安装:

Elasticsearch 是由 elastic 公司开发的一套搜索引擎技术,它是 elastic 技术栈中的一部分;完整的技术栈包括:

  • Elasticsearch:用于数据存储、计算和搜索

  • Logstash/Beats:用于数据收集

  • Kibana:用于数据可视化

整套技术栈被称为 ELK,经常用来做日志收集、系统监控和状态分析等等:

整套技术栈的核心就是用来存储搜索计算的 Elasticsearch,因此接下来学习的核心也是Elasticsearch

要安装的内容:

  • elasticsearch:存储、搜索和运算

  • kibana:图形化展示

elasticsearch 作为核心组件,主要承担数据存储、检索与数据分析的核心能力;它对外统一提供 Restful 风格接口,所有数据读写、聚合分析等操作,均可通过 HTTP 请求实现;但接口请求方式、路由地址及请求体参数格式约束严格,日常开发与调试记忆成本较高,因此搭配 kibana 可视化工具使用,可直观便捷地调用接口、执行查询语句,大幅简化运维与开发调试工作

Kibana 是 elastic 公司提供的用于操作 Elasticsearch 的可视化控制台;它的功能非常强大,包括:

  • 对 Elasticsearch 数据的搜索、展示

  • 对 Elasticsearch 数据的统计、聚合,并形成图形化报表、图形

  • 对 Elasticsearch 的集群状态监控

  • 它还提供了一个开发控制台(DevTools),在其中对 Elasticsearch 的 Restful 的 API 接口提供了语法提示

安装 Elasticsearch:

通过下面的 Docker 命令即可安装单机版本的 elasticsearch:

bash 复制代码
docker network create es-net
bash 复制代码
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

或者直接导入镜像 tar 包:

bash 复制代码
docker load -i /root/es.tar

安装完成后,访问 9200 端口,即可看到响应的 Elasticsearch 服务的基本信息:

安装 Kibana:

通过下面的 Docker 命令,即可部署 Kibana:

bash 复制代码
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601  \
kibana:7.12.1

或者直接导入镜像 tar 包:

bash 复制代码
docker load -i /root/kibana.tar

安装完成后,访问 5601 端口,即可看到控制台页面:

选择 Explore on my own 之后,进入主页面:

然后选中 Dev tools,进入开发工具页面:

倒排索引:

Elasticsearch 之所以有如此高性能的搜索表现,正是得益于底层的倒排索引技术,而倒排索引的概念是基于 MySQL 这样的正向索引而言的

正向索引:

例如有一张名为 tb_goods 的表:

id title price
1 苹果手机 9999
2 华为手机 7999
3 华为苹果充电器 49
4 苹果手环 1299
... ... ...

其中的 id 字段已经创建了索引,由于索引底层采用了 B+ 树结构,因此根据 id 搜索的速度会非常快;但是其他字段例如 title,只在叶子节点上存在,因此要根据 title 搜索的时候只能遍历树中的每一个叶子节点,判断 title 数据是否符合要求

根据 id 精确匹配时,可以走索引,查询效率较高;而当搜索条件为模糊匹配时,由于索引无法生效,导致从索引查询退化为全表扫描,效率很差

因此,正向索引适合于根据索引字段的精确搜索,不适合基于部分词条的模糊匹配;而倒排索引恰好解决的就是根据部分词条模糊匹配的问题

倒排索引:

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

  • 文档(Document):用来搜索的数据,其中的每一条数据就是一个文档;例如一个网页、一个商品信息

  • 词条(Term):对文档数据或用户搜索数据,利用某种算法分词,得到的具备含义的词语就是词条;例如:只因你太美,就可以分为:只因、你、你太美、太美、美这样的几个词条

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

  • 将每一个文档的数据利用分词算法根据语义拆分,得到一个个词条

  • 创建表,每行数据包括词条、词条所在文档 id、位置等信息

  • 因为词条唯一性,可以给词条创建正向索引

此时形成的这张以词条为索引的表,就是倒排索引表:

词条(索引) 文档id
苹果 1,3,4
手机 1,2
华为 2,3
充电器 3
手环 4

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

流程描述:

(1)用户输入条件 "华为手机" 进行搜索。

(2)对用户输入条件分词,得到词条:华为、手机。

(3)拿着词条在倒排索引中查找(由于词条有索引,查询效率很高),即可得到包含词条的文档id:1、2、3

(4)拿着文档 id 到正向索引中查找具体文档即可(由于 id 也有索引,查询效率也很高)

正向和倒排:

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

  • 而倒排索引则相反,是先找到用户要搜索的词条,根据词条得到保护词条的文档的 id,然后根据 id 获取文档;是根据词条找文档的过程

正向索引

  • 优点:可以给多个字段创建索引;根据索引字段搜索、排序速度非常快

  • 缺点:根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描

倒排索引

  • 优点:根据词条搜索、模糊搜索时,速度非常快

  • 缺点:只能给词条创建索引,而不是字段;无法根据字段做排序

基础概念:

文档和字段:

Elasticsearch 是面向文档(Document)存储的,可以是数据库中的一条商品数据,一个订单信息;文档数据会被序列化为 json 格式后存储在 Elasticsearch 中:

bash 复制代码
{
    "id": 1,
    "title": "苹果手机",
    "price": 9999
}
{
    "id": 2,
    "title": "华为手机",
    "price": 7999
}
{
    "id": 3,
    "title": "华为苹果充电器",
    "price": 49
}
{
    "id": 4,
    "title": "苹果手环",
    "price": 1299
}

因此,原本数据库中的一行数据就是 Elasticsearch 中的一个 json 文档;而数据库中每行数据都包含很多列,这些列就转换为 JSON 文档中的字段(Field)

索引和映射:

随着业务发展,需要在 Elasticsearch 中存储的文档也会越来越多,比如有商品的文档、用户的文档、订单文档等等

所有文档都散乱存放显然非常混乱,也不方便管理;因此,要将类型相同的文档集中在一起管理,称为索引(Index);例如:

商品索引:

bash 复制代码
{
    "id": 1,
    "title": "苹果手机",
    "price": 9999
}

{
    "id": 2,
    "title": "华为手机",
    "price": 7999
}

{
    "id": 3,
    "title": "三星手机",
    "price": 3999
}

用户索引:

bash 复制代码
{
    "id": 101,
    "name": "张三",
    "age": 21
}

{
    "id": 102,
    "name": "李四",
    "age": 24
}

{
    "id": 103,
    "name": "王五",
    "age": 18
}

订单索引:

bash 复制代码
{
    "id": 10,
    "userId": 101,
    "goodsId": 1,
    "totalFee": 294
}

{
    "id": 11,
    "userId": 102,
    "goodsId": 2,
    "totalFee": 328
}

每个类型的文档,可以组织在一起,称为对应的索引;

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

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

对比:

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:擅长海量数据的搜索、分析、计算

IK 分词器:

Elasticsearch 的关键就是倒排索引,而倒排索引依赖于对文档内容的分词,而分词则需要高效、精准的分词算法,IK 分词器就是这样一个中文分词算法

安装:

bash 复制代码
docker exec -it es ./bin/elasticsearch-plugin install -b https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip

再重启容器:

bash 复制代码
docker restart es

使用:

IK 分词器包含两种模式:

  • ik_smart:智能语义切分

  • ik_max_word:最细粒度切分

在 Kibana 的 DevTools 上来测试分词器,先测试 Elasticsearch 官方提供的标准分词器:

bash 复制代码
POST /_analyze
{
  "analyzer": "standard",
  "text": "只因你太美"
}
bash 复制代码
{
  "tokens" : [
    {
      "token" : "只",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<IDEOGRAPHIC>",
      "position" : 0
    },
    {
      "token" : "因",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "<IDEOGRAPHIC>",
      "position" : 1
    },
    {
      "token" : "你",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "<IDEOGRAPHIC>",
      "position" : 2
    },
    {
      "token" : "太",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "<IDEOGRAPHIC>",
      "position" : 3
    },
    {
      "token" : "美",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "<IDEOGRAPHIC>",
      "position" : 4
    }
  ]
}

可以看到标准分词器只能 1 字 1 词条,无法正确对中文做分词

再测试 IK 分词器:

拓展词典:

IK 分词器提供了扩展词汇的功能

进入 ES 容器:

bash 复制代码
docker exec -it es bash

创建 IK 配置目录:

bash 复制代码
mkdir -p /usr/share/elasticsearch/config/analysis-ik

创建扩展词典:

bash 复制代码
printf "%s\n" "只因你太美" > /usr/share/elasticsearch/config/analysis-ik/ext.dic

创建/修改 IK 配置文件:

bash 复制代码
cat > /usr/share/elasticsearch/config/analysis-ik/IKAnalyzer.cfg.xml <<'EOF'
<?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"></entry>
</properties>
EOF

退出容器:

bash 复制代码
exit

重启 ES:

bash 复制代码
docker restart es

索引库操作:

Index 类似数据库表,Mapping 映射类似表的结构;要向 es 中存储数据,必须先创建 Index 和Mapping

Mapping 映射属性:

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

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

    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip 地址)

    • 数值:long、integer、short、byte、double、float

    • 布尔:boolean

    • 日期:date

    • 对象:object

  • index:是否创建索引,默认为 true

  • analyzer 使用哪种分词器

  • properties:该字段的子字段

例如:

bash 复制代码
{
    "age": 21,
    "weight": 52.1,
    "isMarried": false,
    "info": "黑子程序员",
    "email": "mz@zhu.cn",
    "score": [91.0, 27.6, 78.6],
    "name": {
        "firstName": "木",
        "lastName": "朱"
    }
}

对应的每个字段映射(Mapping):

|----------------|---------|-----------|--------|--------|--------|
| 字段名 | 字段类型 | 类型说明 | 是否参与搜索 | 是否参与分词 | 分词器 |
| age | integer | 整数 | 是 | 否 | ------ |
| weight | float | 浮点数 | 是 | 否 | ------ |
| isMarried | boolean | 布尔 | 是 | 否 | ------ |
| info | text | 字符串,但需要分词 | 是 | 是 | IK |
| email | keyword | 字符串,但是不分词 | 否 | 否 | ------ |
| score | float | 只看数组中元素类型 | 是 | 否 | ------ |
| name.firstName | keyword | 字符串,但是不分词 | 是 | 否 | ------ |
| name.lastName | keyword | 字符串,但是不分词 | 是 | 否 | ------ |

增删改查:

由于 Elasticsearch 采用的是 Restful 风格的 API,因此其请求方式和路径相对都比较规范,而且请求参数也都采用 JSON 风格

直接基于 Kibana 的 DevTools 来编写请求做测试

创建索引库和映射:

基本语法

  • 请求方式:PUT

  • 请求路径:/索引库名,可以自定义

  • 请求参数:mapping 映射

格式:

bash 复制代码
PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2":{
        "type": "keyword",
        "index": "false"
      },
      "字段名3":{
        "properties": {
          "子字段": {
            "type": "keyword"
          }
        }
      },
      // ...略
    }
  }
}

示例:

bash 复制代码
# PUT /heizi
{
  "mappings": {
    "properties": {
      "info":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email":{
        "type": "keyword",
        "index": "false"
      },
      "name":{
        "properties": {
          "firstName": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
查询索引库:

基本语法

  • 请求方式:GET

  • 请求路径:/索引库名

  • 请求参数:无

格式:

bash 复制代码
GET /索引库名

示例:

bash 复制代码
GET /heizi
修改索引库:

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

虽然无法修改 mapping 中已有的字段,但是却允许添加新的字段到 mapping 中,因为不会对倒排索引产生影响;因此修改索引库能做的就是向索引库中添加新字段,或者更新索引库的基础属性

语法说明

bash 复制代码
PUT /索引库名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

示例:

bash 复制代码
PUT /heizi/_mapping
{
  "properties": {
    "age":{
      "type": "integer"
    }
  }
}
删除索引库:

语法:

  • 请求方式:DELETE

  • 请求路径:/索引库名

  • 请求参数:无

格式:

bash 复制代码
DELETE /索引库名

示例:

bash 复制代码
DELETE /heizi

文档操作:

Elasticsearch 中的数据其实就是 json 风格的文档

新增文档:

语法:

bash 复制代码
POST /索引库名/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    "字段3": {
        "子属性1": "值3",
        "子属性2": "值4"
    },
}

示例:

bash 复制代码
POST /heizi/_doc/1
{
    "info": "黑子程序员",
    "email": "mz@zhu.cn",
    "name": {
        "firstName": "木",
        "lastName": "朱"
    }
}

查询文档:

语法:

bash 复制代码
GET /{索引库名称}/_doc/{id}

示例:

bash 复制代码
GET /heizi/_doc/1

删除文档:

语法:

bash 复制代码
DELETE /{索引库名}/_doc/id值

示例:

bash 复制代码
DELETE /heizi/_doc/1

修改文档:

修改有两种方式:

  • 全量修改:直接覆盖原来的文档

  • 局部修改:修改文档中的部分字段

全量修改:

全量修改是覆盖原来的文档,其本质是两步操作:

  • 根据指定的 id 删除文档

  • 新增一个相同 id 的文档

注意:如果根据 id 删除时,id 不存在,第二步的新增也会执行,也就从修改变成了新增操作了

语法:

bash 复制代码
PUT /{索引库名}/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    // ... 略
}

示例:

bash 复制代码
PUT /heizi/_doc/1
{
    "info": "黑子程序员",
    "email": "mz@zhu.cn",
    "name": {
        "firstName": "木",
        "lastName": "朱"
    }
}
局部修改:

局部修改是只修改指定 id 匹配的文档中的部分字段

语法:

bash 复制代码
POST /{索引库名}/_update/文档id
{
    "doc": {
         "字段名": "新的值",
    }
}

示例:

bash 复制代码
POST /heizi/_update/1
{
  "doc": {
    "email": "zhu@mz.cn"
  }
}

批处理:

批处理采用 POST 请求,基本语法如下:

bash 复制代码
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }

其中:

  • index 代表新增操作

    • _index:指定索引库名

    • _id 指定要操作的文档id

    • { "field1" : "value1" }:则是要新增的文档内容

  • delete 代表删除操作

    • _index:指定索引库名

    • _id 指定要操作的文档id

  • update 代表更新操作

    • _index:索引库名

    • _id 要操作的文档id

    • { "doc" : {"field2" : "value2"} }:要更新的文档字段

相关推荐
随身数智备忘录2 小时前
拆解合理化建议系统的三大流程,合理化建议如何解决建议征集难与落地慢
大数据·人工智能
逸Y 仙X2 小时前
文章三十四:ElasticSearch Script脚本实战
大数据·elasticsearch·搜索引擎·全文检索
倔强的石头1062 小时前
【Linux 指南】文件系统系列(三):Ext系统核心实现 —— 从块组到 inode 与数据块映射全解析
大数据·linux·运维
前端若水2 小时前
处理智能体的不确定性:重试、回退与人工介入
大数据·人工智能·windows·开源协议
小船跨境3 小时前
2026 NLP数据采集指南:代理IP如何帮助提升大规模采集效率
大数据·网络·人工智能
zhojiew3 小时前
在AWS中国区启动Trino使用Iceberg REST Endpoint查询S3 Tables的实践
大数据·trino·s3table
团象科技3 小时前
跨境业务落地频繁遇阻,Claude登AWS平台如何补齐出海短板
大数据·人工智能
青岛前景互联信息技术有限公司3 小时前
视频AI与智能预警:如何提前发现园区安全隐患?
大数据·人工智能·视频
Promise微笑3 小时前
SF6露点仪选型与避坑:SF6露点仪确保电力设备安全运行的关键
大数据·安全