ElasticSearch 8.x 教程

<span class="portrait"></span>Lockegogo / 2023-05-12 / 原文

ElasticSearch 8.x 教程

ElasticSearch 概述

  1. Lucene 是什么?

    1. Lucene 是一套信息检索工具包,jar 包,不包含搜索引擎系统
    2. 包含:索引结构,读写索引的工作,排序、搜索规则工具类
  2. ElasticSearch (es) 是什么?

  3. 一个开源的高扩展的分布式的全文搜索引擎

  4. 可以近乎实时存储检索数据

  5. 使用 java 开发并使用 Lucene 作为核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 REST-ful 的 API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单

  6. Solor 和 ElasticSearch 的区别

    1. Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能
    2. Solr 支持更多格式的数据,比 JSON、XML、CSV,而 Elasticsearch 仅支持 json 文件格式
    3. Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要 kibana 友好支撑
    4. Solr 查询快,但更新索引时慢(即插入删除慢),用于电商等查询多的应用
      1. ES 建立索引快(即查询慢),即实时性查询快,用于 facebook 新浪等搜索
      2. Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用
    5. Solr 比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而 Elasticsearch 相对开发维护者较少,更新太快,学习使用成本较高

ELK 安装

ELK 是 Elasticsearch、Logstash、Kibana 三大开源框架首字母大写简称。市面上也被成为 Elastic Stack。

  1. Elasticsearch 是一个基于 Lucene、分布式、通过 Restful 方式进行交互的近实时搜索平台框架。
  2. Logstash 是 ELK 的中央数据流引擎,用于从不同目标(文件 / 数据存储 / MQ)收集的不同格式数据,经过过滤后支持输出到不同的目的地(文件 / MQ / redis / elasticsearch / kafka 等)。
  3. Kibana 是一个针对 Elasticsearch 的开源分析及可视化平台,用来搜索、查看交互存储在 es 索引中的数据,提供实时分析的功能。它操作简单,基于浏览器的用户界面可以快速创建仪表板 dashboard,实时显示查询动态。

市面上很多开发只要一提到 ELK 能够一致说出它是一个日志分析架构技术栈总称,但实际上 ELK 不仅仅适用于日志分析,它还可以支持其他任何数据分析和收集的场景。

windows 下安装 Elasticsearch

  1. 最新版本下载地址:Download Elasticsearch | Elastic
  2. 熟悉目录:
bin:启动文件
config:配置文件
     log4j2:日志配置文件
     jvm.options:java虚拟机相关的配置
     elasticsearch.yml:elasticsearch的配置文件!默认9200端口!跨域!
lib:相关jar包
logs:日志
modules:功能模块
plugins:插件!
  1. 启动:进入解压后的目录,运行下面的命令:
./bin/elasticsearch

# 如果这时报错 "max virtual memory areas vm.max *map*count [65530] is too low"
sudo sysctl -w vm.max_map_count=262144
  1. 访问 localhost:9200
curl localhost:9200

默认情况下,Elastic 只允许本机访问,如果需要远程访问,可以修改 Elastic 安装目录的 config/elasticsearch.yml 文件,去掉 network.host 的注释,将它的值改成 0.0.0.0,然后重新启动 Elastic。

线上服务不要这样设置,要设成具体的 IP。

Docker-Compose 安装 ELK

  1. 创建 docker-compose.yaml 文件:注意 ES 和 Kibana 的版本要一致
version: "3.9"
services:
  elasticsearch:
    image: elasticsearch:8.7.1
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
      - xpack.security.enabled=false
    volumes:
      - es_data:/usr/share/elasticsearch/data
    ports:
      - target: 9200
        published: 9200
    networks:
      - elastic

  kibana:
    image: kibana:8.7.1
    ports:
      - target: 5601
        published: 5601
    depends_on:
      - elasticsearch
    networks:
      - elastic

volumes:
  es_data:
    driver: local

networks:
  elastic:
    name: elastic
    driver: bridge
  1. 在该文件所在目录,执行:docker-compose up

  2. 访问 http://localhost:9200/,返回:

6ab0dc860d1",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "YE5ZOkzbQoejhluBtOT_eQ",
  "version" : {
    "number" : "8.7.1",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "f229ed3f893a515d590d0f39b05f68913e2d9b53",
    "build_date" : "2023-04-27T04:33:42.127815583Z",
    "build_snapshot" : false,
    "lucene_version" : "9.5.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
  1. 访问 http://localhost:5601/app/home#/ 得到 Elastic 的可视化界面

  2. 如果容器意外关闭的话:

    1. 服务器版:可以使用 docker logs -f 容器id 查看日志

    2. 桌面版:点击 view details 进入 详情页

      image-20230510171535792

  3. 如果不再使用这些容器,可以删除:docker-compose down

  4. 重启 windows 前请务必先关掉容器

ES 的核心概念

集群,节点,索引,类型,文档,分片,映射是什么?

基本概念

Node 与 Cluster

Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。

单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。

Index

Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。

所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。它是单个数据库的同义词。每个 Index (即数据库)的名字必须是小写。

下面的命令可以查看当前节点的所有 Index:

curl -X GET 'http://localhost:9200/_cat/indices?v'

Document

Index 里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。

Document 使用 JSON 格式表示,下面是一个例子。

{
  "user": "张三",
  "title": "工程师",
  "desc": "数据库管理"
}

同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

关系行数据库和 Elasticsearch 对比

Elasticsearch 面向文档

Relational DB ElasticSearch
数据库 (database) 索引 (indices)
表 (tables) types(弃用)
行 (rows) documents
字段 (columns) fields

Elasticsearch (集群) 中可以包含多个索引 (数据库),每个索引中可以包含多个类型 (表),每个类型下又包含多个文档 (行),每个文档中又包含多个字段 (列)。

物理设计

ES 在后台把每个索引划分为多个分片,每个分片可以在集群中的不同服务器间迁移。一个人就是一个集群,默认的集群名 elasticsearch。

节点和分片如何工作

一个集群至少有一个节点,而一个节点就是一个 es 进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有 5 个分片(primary shard)构成,每一个主分片会有一个副本(replica shard)

img

上图是一个有 3 个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个 Lucene 索引,一个包含倒排索引的文件目录,倒排索引的结构使得 es 在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。

什么是倒排索引?

比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构:

img

如果我们需要搜索含有 python 标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章 ID 即可。

逻辑设计

一个索引类型中,包含多个文档,比如说文档 1、文档 2。当我们索引一篇文档时,可以通过这样的一个顺序找到它:索引 \(\to\) 类型 \(\to\) 文档 ID,通过这个组合我们就能索引到某个具体的文档。

注意:ID 不必是整数,实际上它是个字符串。

  1. 文档:es 是面向文档的,索引和搜索数据的最小单位是文档,文档有如下属性:
    1. 自我包含:一篇文档同时包含字段和对应的值,也就是同时包含 key:value
    2. 可以是层次型的:个文档中包含自文档,就是一个 json 对象
    3. 灵活的结构:文档不依赖于预先定义的模式。在关系数据库中,要提前定义字段才能用;在 es 中,字段是非常灵活的,可以动态添加一个新的字段
  2. 类型:类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如 name 映射为字符串类型。
  3. 索引:也就是数据库,索引是映射类型的容器,es 的索引是一个非常大的文档集合,索引存储了映射类型的字段和其他设置,然后将其存储在各个分片上

在 es 中安装插件

elasticsearch-analysis-ik 分词器为例:

安装

方法一:直接进入容器下载

# 1. 进入运行中的容器
docker exec -it 容器id bash
# 2. 安装插件
bin/elasticsearch-plugin install https://github.com/ElisaMin/elasticsearch-analysis-ik/releases/download/8.7.1-17/elasticsearch-analysis-ik-8.7.1.zip
# 3. 查看插件是否安装成功
bin/elasticsearch-plugin list
# 3. 退出容器
exit
# 4. 重启容器
docker restart 容器id

方法二:本地下载

# 1. 下载插件安装包,然后将其拷贝到容器中
docker cp D:\Software\Elasticsearch\Docker\plugins\elasticsearch-analysis-ik-8.7.1.zip 容器id:/tmp/
# 2. 进入运行中的容器
docker exec -it 容器id bash
# 3. 安装插件
bin/elasticsearch-plugin install file:///tmp/elasticsearch-analysis-ik-8.7.1.zip

使用

  1. 进入 [Console - Dev Tools - Elastic]
  2. 在控制台输入:其中 ik_smart 为最少切分;ik_max_word 为最细粒度划分
GET _analyze
{
  "analyzer": "ik_smart",
  "text": "北京邮电大学"
}

GET _analyze
{
  "analyzer": "ik_max_word",
  "text": "北京邮电大学"
}

restful 风格说明

restful 风格是一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

基本 rest 说明:

method url 地址 描述
PUT localhost:9200 / 索引名称 / 类型名称 / 文档 id 创建文档(指定文档 id)
POST localhost:9200 / 索引名称 / 类型名称 创建文档(随机文档 id)
POST localhost:9200 / 索引名称 / 类型名称 / 文档 id/_update 修改文档
DELETE localhost:9200 / 索引名称 / 类型名称 / 文档 id 删除文档
GET localhost:9200 / 索引名称 / 类型名称 / 文档 id 查询文档通过文档 id
POST localhost:9200 / 索引名称 / 类型名称 /_search 查询所有数据

关于索引的基本操作

  1. 创建一个索引
# ElasticSearch 的 7.x.x 版本
PUT /索引名/类型/文档id
{
    请求体
}

# ElasticSearch 的 8.x.x 版本,类型已经弃用,默认写_doc
PUT /索引名/_doc/文档id
{
    请求体
}

# 举个栗子
PUT /test1/_doc/1
{
    "name": "locke",
	"age": 3,
	"birthday": "1997-04-06"
}

Kibana 返回:
{
  "_index": "test1",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

官网类型文档地址

# 字符串类型
text、keyword
# 数值类型
long,integer,short,byte,double,float,half float,scaled float
# 日期类型
date
# te布尔值类型
boolean·
# 二进制类型
binary
  1. 创建具体的索引规则:
PUT test2/
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "long"
      },
      "birthday": {
        "type": "date"
      }
    }
  }
}

Kibana 返回:
{
  "acknowledged": true,
  "shards_acknowledged": true,
  "index": "test2"
}
  1. 获得索引规则
GET test2

如果自己的文档字段没有指定,那么 es 就会给我们默认配置字段类型!

  1. 扩展命令
GET _cat/health      # 查看健康值
GET _cat/indices?v   # 查看所有东西的版本信息
  1. 修改:POST
POST test1/_update/1
{
  "doc": {
    "name": "lockegogo"
  }
}
  1. 删除:DELETE,根据请求来判断是删除索引还是删除文档
DELETE test1

关于文档的基本操作

基本操作
# 增
PUT /test/_doc/1
{
  "name": "locke",
  "age": 26,
  "desc": "喜欢大熊猫花花",
  "tags": ["圆滚滚","可爱","三角形"]
}
PUT /test/_doc/2
{
  "name": "feifei",
  "age": 28,
  "desc": "喜欢大熊猫福宝",
  "tags": ["活泼","调皮","花园杀手"]
}

# 查
GET /test/_doc/1
GET /test/_doc/2

# 改
POST /test/_update/1
{
  "doc": {
  	"name": "locke"
  }
}
简单操作:条件查询

如果有多条记录,匹配度越高,分数越高:

# 支持模糊查询
GET /test/_search?q=name:locke
复杂操作
  1. 排序 & 分页
# "_source": 过滤字段查询
# "sort": 排序
# "from": 分页查询,从第几条数据开始查询
# "size": 返回多少数据

GET test/_search
{
 "query": {
   "match": {
     "name": "feifei"
   }
 },
 "_source": ["name", "desc"],
 "sort": [
   {
     "age": {
       "order": "desc"
     }
   }
 ],
 "from": 0,
 "size": 1
}

  1. 布尔值查询
    1. must = and:既符合 A 也符合 B
    2. should = or:符合 A 或符合 B
    3. must_not = not:不符合 A 或不符合 B
GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "feifei"
          }
        },
        {
          "match": {
            "age": "29"
          }
        }
      ]
    }
  }
}
  1. 过滤查询:filter
GET test/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "feifei"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "age": {
              "gte": 10,
              "lte": 30
            }
          }
        }
      ]
    }
  }
}
  1. 匹配多个条件:多个条件使用空格隔开,只要满足一个条件就可以被查出,可以通过分值进行基本判断
# 匹配多个条件
GET test/_search
{
  "query": {
    "match": {
      "tags": "活泼 花园杀手 三角形"
    }
  }
}
  1. 精确查询:term 查询是直接通过倒排索引指定的词条进行精确查找
    1. term:直接查询精确的,两种情况:text or keyword
    2. match:会使用分词器解析,先分析文档,然后在通过分析的文档进行查询
# 1. 创建数据
PUT testdb
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "desc": {
        "type": "keyword"
      }
    }
  }
}

PUT testdb/_doc/1
{
  "name": "java name",
  "desc": "java desc"
}

PUT testdb/_doc/2
{
  "name": "java name",
  "desc": "java desc2"
}

# 2. 分析数据,查看结果
# 2.1 没有被拆分
GET _analyze
{
  "analyzer": "keyword",
  "text": "java name"
}
# 2.2 被拆分了
GET _analyze
{
  "analyzer": "standard",
  "text": "java name"
}

# 3. 查看数据:keyword 类型的字段不会被分词器解析
# 3.1 两条记录都可以查到
GET testdb/_search
{
  "query": {
    "match": {
      "name": "java"
    }
  }
}
# 3.2 只能查到一条,因为 keyword 不可拆分
GET testdb/_search
{
  "query": {
    "match": {
      "desc": "java desc"
    }
  }
}

# term
GET testdb/_search
{
  "query": {
    "term": {
      "desc": "java desc"
    }
  }
}
  1. 高亮查询
# "pre_tags" + "post_tags": 自定义高亮条件

GET testdb/_search
{
  "query": {
    "match": {
      "desc": "java desc"
    }
  },
  "highlight": {
  	"pre_tags": "<p class='key' style='color:red'>",
  	"post_tags": "</p>",
    "fields": {
      "desc": {}
    }
  }
}

集成 SpringBoot

Installation

  1. 找文档
  2. 找到原生的依赖
<project>
  <dependencies>

    <dependency>
      <groupId>co.elastic.clients</groupId>
      <artifactId>elasticsearch-java</artifactId>
      <version>8.7.1</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.3</version>
    </dependency>

  </dependencies>
</project>

If you get ClassNotFoundException: jakarta.json.spi.JsonProvider:add

<project>
  <dependencies>
    ...
    <dependency>
      <groupId>jakarta.json</groupId>
      <artifactId>jakarta.json-api</artifactId>
      <version>2.0.1</version>
    </dependency>

  </dependencies>
</project>

Connecting

Java API 客户端围绕三个主要组件进行了结构化设计:

  • API 客户端类:它们提供了强类型的数据结构和用于 Elasticsearch API 的方法。由于 Elasticsearch API 较大,它被组织成命名空间,每个命名空间都有自己的客户端类。Elasticsearch 核心功能在 ElasticsearchClient 类中实现。

  • JSON 对象映射器:它将您的应用程序类映射到 JSON,并与 API 客户端无缝集成。

  • 传输层实现:这是处理所有 HTTP 请求的地方。

下面的代码片段创建并将这三个组件连接在一起:

// Create the low-level client
RestClient restClient = RestClient.builder(
    new HttpHost("localhost", 9200)).build();

// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
    restClient, new JacksonJsonpMapper());

// And create the API client
ElasticsearchClient client = new ElasticsearchClient(transport);

尝试连接:

SearchResponse<Product> search = client.search(s -> s
    .index("products")
    .query(q -> q
        .term(t -> t
            .field("name")
            .value(v -> v.stringValue("bicycle"))
        )),
    Product.class);

for (Hit<Product> hit: search.hits().hits()) {
    processProduct(hit.source());
}

实战:京东

参考资料

  1. 安装并运行 Elasticsearch | Elasticsearch: 权威指南 | Elastic

  2. 【狂神说 Java】ElasticSearch7.6.x 最新完整教程通俗易懂

  3. 【狂神说 Java】ElasticSearch7.6.x

  4. 全文搜索引擎 Elasticsearch 入门教程 - 阮一峰的网络日志 (ruanyifeng.com)