ElasticSearch快速上手:从配置、docker部署、Kibana到运维

在这里插入图片描述

ElasticSearch快速上手

1. 简介

ES是一个基于Lucene的开源的、分布式的、支持多用户的、由Java实现的一个业界非常流行的全文实时搜索引擎。它还拥有以下特征:

  • 基于RESTful web接口
  • 海量数据规模下的性能表现稳定,接近实时返回结果
  • 支持水平扩展
  • 支持多种编程语言进行客户端开发
  • 大量网站和企业使用
    • Wikipedia
    • Stack Overflow
    • GitHub

1.1 ES VS Solr

  • ES部署安装简单,自带分布式协调机制。Solr则需要安装Zookeeper
  • ES开箱即用,Solr上手成本略高
  • Solr支持多种数据格式的文件,如JSON、XML、CSV等。而ES仅支持JSON
  • Solr数据搜索速度快,但插入和删除慢;而ES都比较快
  • Solr提供的功能繁杂;ES则注重核心功能,高级功能由第三方插件提供,如Kibana提供GUI功能。

1.2 为什么学习ES

好处如下:

  • 全文搜索是企业平台常用的功能,ES可提供良好的体验
  • ES具备强大的数据分析功能
  • ES部署方便(单机或集群)
  • 国内很多互联网公司都在使用ES,学习ES有助于提升竞争力

1.3 ES的主要功能和应用场景

  1. 海量数据的分布式存储以及集群管理
  2. 近乎实时的数据搜索能力。支持对(非)结构化数据、地理位置等类型数据进行处理和分析
  3. 各种聚合分析功能,如统计、排序、过滤、分组等

应用场景:

  1. 网站搜索、代码搜索等
  2. 日志管理和分析,应用性能监控、web舆情分析等
  3. 对海量业务订单进行分析和处理,利用ES的聚合函数和分析功能统计处各种各样的数据报表

1.4 ELK架构

在这里插入图片描述

ES是与Logstash(数据采集和转换)和Kibana(数据分析和可视化)一起开发的,它们共同组成ELK架构。

最早Logstash是由一个小公司开发的,在2013年被Elastic公司收购。

一个常见的例子是使用Logstash采集Nginx日志,并将其发送到Elasticsearch,然后使用Kibana进行可视化展示。

  • [ES][ES]
  • [Logstash][Logstash]
  • [Kibana][Kibana]

1.5 Beats

在这里插入图片描述

[Beats][Beats]是Elastic公司使用Go语言开发的一组比Logstash更轻量的数据采集器,用于采集服务器、网络设备等数据源的数据,并将数据发送到Logstash或Elasticsearch。
它可以Sidecar模式与应用容器一同部署,也可以按节点维度部署。

Beats家族有多个成员,包括Auditbeat、Filebeat、Metricbeat、Packetbeat、Winlogbeat等,它们分别用于采集不同类型的数据。

Beats通常由两种部署模式:

  • 在中小规模架构中,通常在每个节点或K8s Pod中部署一个Beats组件,将采集数据直接发送到Elasticsearch
  • 在大规模架构中,可能需要先将Beats采集到的数据发送到Logstash,再由Logstash将数据发送到Elasticsearch
    • 这是因为Logstash将提供比Beats更丰富的过滤器和插件来处理数据,以及多数据源聚合、对ES节点的负载均衡和缓冲的功能
    • Logstash还可以持久化数据,这在ES集群不可用时非常有用,降低数据丢失的可能性

2. 安装ES

ES的安装方式有多种,可以参考官方文档:Installing Elasticsearch

这里由于是快速入门,因此使用Docker来安装ES(常用于开发和测试环境)。

2.1 Docker安装ES

首先需要找到Docker的设置:Preferences > Resources > Advanced,调整内存至少4GB。然后执行下面命令:

docker network create elastic

# ES_JAVA_OPTS限制ES使用的内存大小,避免ES占用过多宿主机内存。其中Xms表示min,Xmx表示max,二者应设置同一个值
# 若是开发环境,可以添加 -e xpack.security.enabled=false 关闭身份验证,关闭后Kibana也可无密码连接es
docker run --name elasticsearch --net elastic \
  -p 9200:9200 \
  -e "discovery.type=single-node" \
  -e ES_JAVA_OPTS="-Xms512m -Xmx512m" \
  -t library/elasticsearch:8.11.3

在输出的日志中,搜索关键字current.health会看到一下日志行(其中包含了集群信息、节点基本信息如node-id、node-name等):

{
  "@timestamp": "2024-01-05T09:08:45.532Z",
  "log.level": "INFO",
  "current.health": "GREEN",
  "message": "Cluster health status changed from [YELLOW] to [GREEN] (reason: [shards started [[.security-7][0]]]).",
  "previous.health": "YELLOW",
  "reason": "shards started [[.security-7][0]]",
  "ecs.version": "1.2.0",
  "service.name": "ES_ECS",
  "event.dataset": "elasticsearch.server",
  "process.thread.name": "elasticsearch[b527ebb76d2b][masterService#updateTask][T#1]",
  "log.logger": "org.elasticsearch.cluster.routing.allocation.AllocationService",
  "elasticsearch.cluster.uuid": "yz51YWzAR6CJFTr_xQlVFQ",
  "elasticsearch.node.id": "3dE37A4dTxOOSYOiqw2p3Q",
  "elasticsearch.node.name": "b527ebb76d2b",
  "elasticsearch.cluster.name": "docker-cluster"
}

如果设置xpack.security.enabled=false,则docker日志是以adding index template结束。

从8.0版本开始,ES默认为集群启用了各种安全设置(包含TLS、默认用户名密码等)。
第一次启动Elasticsearch时,生成默认用户名和密码和Kibana注册令牌会输出到终端。通过搜索Password得到:

ℹ️  Password for the elastic user (reset with `bin/elasticsearch-reset-password -u elastic`):
  {elastic账号密码}
ℹ️  HTTP CA certificate SHA-256 fingerprint:
  {CA证书SHA256指纹}

容器日志中还包括Kibana注册token(关键字Configure Kibana)、新节点加入集群的token(关键字join this cluster)。

ES在生产环境中是多节点部署的,这里简单起见,以单节点方式启动ES集群,

使用下面的API端点可以查看集群状态:

# 如果设置xpack.security.enabled=false,则使用http
curl https://localhost:9200/_cat/health
curl https://localhost:9200/_cluster/health?pretty=true

# 查看集群信息
curl https://localhost:9200

2.2 Docker安装Kibana

在这里插入图片描述

ES通过REST APIs与外界交互,包含接收数据和其他请求。通常,我们:

  • 使用[各种编程语言][Client SDKs]中的ES client完成搜索请求
  • 使用Kibana完成日常管理ES需求(配置索引、报表等)
  • 其他日志采集工具向ES发送数据

所以如想要图形化使用ES,需要安装Kibana。参照下面的命令:

docker run --name kibana --net elastic -p 5601:5601 \
  -e "i18n.locale=zh-CN" \
  library/kibana:8.11.3

复制最后日志输出中的链接到浏览器中,替换0.0.0.0localhost,回车。将上一步中得到的Kibana注册token复制到页面中的输入框,再确认。
此刻Kibana将完成ES的连接配置。完成配置后,使用elastic和上一步中得到的密码登录ES,然后点击Explorer on my own
开始通过Kibana使用ES。

Kibana还方便的为开发者提供了控制台(Management > Dev Tools),用于调试ES。

2.3 ES配置

生产环境需要根据实际情况对ES进行配置调优,其中大部分设置可以在集群启动后使用
[Cluster update settings API][ClusterupdatesettingsAPI] 进行修改。

还有很多配置需要我们在安装前进行修改,这些配置很多是节点特定的,每个节点使用的配置不会完全一致。

ES的配置目录根据安装方式而定:

  • 对于归档发行版(tar.gz这种)的安装方式,配置目录为$ES_HOME/config
    • 这个目录下的配置文件存在升级时被删除的风险,建议修改到$ES_HOME之外
  • 对于软件包发行版,配置目录为/etc/elasticsearch。但某些发行版可能不同
    • Debian发行版:/etc/default/elasticsearch
    • RPM发行版:/etc/sysconfig/elasticsearch
  • 对于Docker方式,配置目录为/usr/share/elasticsearch/config

以上都可通过ENV方式修改配置目录,例如ES_PATH_CONF=/etc/elasticsearch

配置文件布局:

$ ls /usr/share/elasticsearch/config
certs                              elasticsearch.keystore  jvm.options    log4j2.file.properties  role_mapping.yml  users
elasticsearch-plugins.example.yml  elasticsearch.yml       jvm.options.d  log4j2.properties       roles.yml         users_roles

其中常修改的配置文件有:

  • elasticsearch.yml是ES的核心配置文件,查看示例
  • jvm.optionsjvm.options.d是ES JVM的配置文件(性能调优)
  • log4j2.propertieslog4j2.file.properties是ES日志的配置文件

其他文件用途如下:

  • elasticsearch.keystore:存储 ES 实例的安全信息和敏感配置。替代以前那种明文存储秘钥的方式
  • elasticsearch-plugins.example.yml:ES插件的示例配置文件
  • roles.ymlrole_mapping.yml:配置角色(授权用户的访问权限)和角色映射,一般不需要修改(而是通过Kibana方式修改)
  • usersusers_roles:存储用户信息和用户与角色的关系,用于配置 ES 的身份验证和授权,一般不需要修改(理由同上)

2.4 安装多节点集群

在2.1小节中介绍的是如何安装单节点集群,这种方式你可能接触不到配置文件的修改以及各字段的含义。
本节将介绍如何安装多节点集群,这个过程中你将会进一步熟悉ES的安装步骤。

集群角色介绍:

  • 主节点(master):ES集群中同一时刻只有一个主节点(从多个具有master角色的节点中选举出),相当于集群大脑。负责管理集群状态,例如选举、索引创建/删除、分片迁移等。
    • 大规模集群中,一般会规划仅master节点,这样可以让主节点避免运行高负载任务导致无法响应更重要的master请求。
  • 数据节点(data):存放数据的节点,负责数据的增删改查。
    • 此节点通常执行I/O、内存和CPU密集型的操作,需要重点监控。并在必要时添加数据节点
    • 大规模集群中,一般会规划仅data节点
  • 仅投票节点(master+voting_only):有投票权,没有被选举权,通常用于降低集群资源消耗(因为减少了一个master节点)
    • 一个多节点es集群至少需要3个主节点,其中需要至少2个非仅投票节点(所以可以是2master+1voting_only的组合)
    • 由于永远不会被选举为master,所以仅投票节点的CPU/内存配置可以低于标准master节点
    • 仅投票节点可以与data角色组合作为仅投票的数据节点(master+voting_only+data)
  • 远程客户端(remote_cluster_client):跨群集搜索时需要,较少使用。

其他还有Ingest和ML等非必要角色,在使用到其他功能时需要。

一个高可用ES集群至少需要3个有资格成为master的节点,其中至少两个不是仅投票(voting_only)节点。

下面介绍安装的是一个比较标准的多节点ES集群配置,如下:

  • 1个仅master节点
  • 2个master+data节点

配置文件如下:

  • [docker-compose.yml]
# 此配置文件比官方给出的更完善,注意查看中文注释的位置
version: "2.2"

services:
  setup:
    image: docker.io/library/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        if [ x${ELASTIC_PASSWORD} == x ]; then
          echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
          exit 1;
        elif [ x${KIBANA_PASSWORD} == x ]; then
          echo "Set the KIBANA_PASSWORD environment variable in the .env file";
          exit 1;
        fi;
        if [ ! -f config/certs/ca.zip ]; then
          echo "Creating CA";
          bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
          unzip config/certs/ca.zip -d config/certs;
        fi;
        if [ ! -f config/certs/certs.zip ]; then
          echo "Creating certs";
          echo -ne \
          "instances:\n"\
          "  - name: es01\n"\
          "    dns:\n"\
          "      - es01\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          "  - name: es02\n"\
          "    dns:\n"\
          "      - es02\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          "  - name: es03\n"\
          "    dns:\n"\
          "      - es03\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          "  - name: kibana\n"\
          "    dns:\n"\
          "      - kibana\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          > config/certs/instances.yml;
          bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
          unzip config/certs/certs.zip -d config/certs;
        fi;
        echo "Setting file permissions"
        chown -R root:root config/certs;
        find . -type d -exec chmod 750 \{\} \;;
        find . -type f -exec chmod 640 \{\} \;;
        echo "Waiting for Elasticsearch availability";
        until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
        echo "Setting kibana_system password";
        until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
        echo "All done!"
      '
    healthcheck:
      test: [ "CMD-SHELL", "[ -f config/certs/es01/es01.crt ]" ]
      interval: 1s
      timeout: 5s
      retries: 120

  es01:
    depends_on:
      setup:
        condition: service_healthy
    image: docker.io/library/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - ${ES_PORT}:9200
    environment:
      - node.name=es01
      # 添加node.roles配置,为每个节点配置不同的角色,es01作为仅master非数据节点
      # remote_cluster_client角色允许远程客户端(如kibana)连接集群,每个具有master资格的节点都需要此角色
      - node.roles=master,remote_cluster_client
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01,es02,es03
      - discovery.seed_hosts=es02,es03
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es01/es01.key
      - xpack.security.http.ssl.certificate=certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es01/es01.key
      - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

  es02:
    depends_on:
      - es01
    image: docker.io/library/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata02:/usr/share/elasticsearch/data
    environment:
      - node.name=es02
      - node.roles=master,data,remote_cluster_client
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} # 作为master之一,需要密码
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01,es02,es03
      - discovery.seed_hosts=es01,es03
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es02/es02.key
      - xpack.security.http.ssl.certificate=certs/es02/es02.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es02/es02.key
      - xpack.security.transport.ssl.certificate=certs/es02/es02.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

  es03:
    depends_on:
      - es02
    image: docker.io/library/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata03:/usr/share/elasticsearch/data
    environment:
      - node.name=es03
      - node.roles=master,data,remote_cluster_client
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} # 作为master之一,需要密码
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01,es02,es03
      - discovery.seed_hosts=es01,es02
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es03/es03.key
      - xpack.security.http.ssl.certificate=certs/es03/es03.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es03/es03.key
      - xpack.security.transport.ssl.certificate=certs/es03/es03.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

  kibana:
    depends_on:
      es01:
        condition: service_healthy
      es02:
        condition: service_healthy
      es03:
        condition: service_healthy
    image: docker.io/library/kibana:${STACK_VERSION}
    volumes:
      - certs:/usr/share/kibana/config/certs
      - kibanadata:/usr/share/kibana/data
    #      - ./kibana.yml:/usr/share/kibana/config/kibana.yml # 配置通过env方式注入即可,基本无需自定义yml
    ports:
      - ${KIBANA_PORT}:5601
    environment:
      # 所有kibana设置都可以通过env注入(下划线替代点)
      - SERVERNAME=kibana
      - I18N_LOCALE=zh-CN
      # 应该设置所有master节点,否则可能无法访问es(当master变更节点后)
      - ELASTICSEARCH_HOSTS=["https://es01:9200","https://es02:9200","https://es03:9200"]
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD="${KIBANA_PASSWORD}"
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
      # 为kibana开启ssl
      # 但这里使用的是上面setup容器生成的内部ca签名的证书,浏览器访问时会提示不安全的证书(需点击信任或设置站点例外)
      # 生产环境请购买正版证书或使用(LetsEncrypted免费证书),要么就不开启ssl(SERVER_SSL_ENABLED=false)
      - SERVER_SSL_ENABLED=true
      - SERVER_SSL_CERTIFICATE=config/certs/kibana/kibana.crt
      - SERVER_SSL_KEY=config/certs/kibana/kibana.key
    mem_limit: ${MEM_LIMIT}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

volumes:
  certs:
    driver: local
  esdata01:
    driver: local
  esdata02:
    driver: local
  esdata03:
    driver: local
  kibanadata:
    driver: local
- 其中的`setup`容器负责完成证书生成、es节点的健康监控工作
- 此文件对比官方配置增加了几项内容:
    - 给不同节点设置角色(而不是默认)
    - 为kibana配置中文语言
    - 为kibana开启ssl
    - 为kibana配置所有master资格节点作为es连接主机
  • [.env]:部分docker-compose文件引用的环境变量需要根据实际情况修改(其中包含es密码设置)
# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD=123456

# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD=123456

# Version of Elastic products
STACK_VERSION=8.11.3

# Set the cluster name
CLUSTER_NAME=docker-cluster

# Set to 'basic' or 'trial' to automatically start the 30-day trial
LICENSE=basic
#LICENSE=trial

# Port to expose Elasticsearch HTTP API to the host
ES_PORT=9200
#ES_PORT=127.0.0.1:9200

# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80

# Increase or decrease based on the available host memory (in bytes)
# 建议不要低于1GB,否则集群大概率会出现异常(亲测)
MEM_LIMIT=1073741824

# Project namespace (defaults to the current folder name if not set)
COMPOSE_PROJECT_NAME=test-es

启动集群:

docker-compose up -d

查看集群容器状态:

# 其中setup容器在完成任务后会变成exited状态
docker-compose ps

若容器异常,可查看容器日志进行排查。

测试完成后,清理创建的资源:

docker-compose stop # 停止文件内定义的容器
docker-compose down # 停止并删除文件内包含的容器和网络,保留volume
docker-compose down -v # 停止并删除文件内包含的容器、volume和网络

如果要重建单个有问题的es或kibana容器,参考下面的命令:

# 由于其他容器都依赖setup容器,所以setup容器也得一起删除再重建
docker stop <container-name/id>
docker volume rm <data-volume-used-by-container> # 注意不要删除setup使用的certs卷
docker-compose up -d --no-recreate

当所有容器处于Running状态大约十多秒后,我们就可以查询集群状态:

# 将123456换成你的es密码
docker exec -it test-es-es01-1 curl \
  -s --cacert config/certs/ca/ca.crt \
  -u elastic:123456 \
  https://localhost:9200/_cluster/health?pretty=true

浏览器访问kibana:https://localhost:5601,用户名是elastic,密码是.env文件中的ELASTIC_PASSWORD
注意,首次访问kibana页面时,浏览器会提示不安全的站点/链接,这是正常的。因为kibana使用的证书的内部CA签名的。具体请查看docker-compose.yml中的注释。

注意,YML文件的kibana_system用户是专门用于 Kibana 内部运作的 Elasticsearch 用户,不是给我们使用的。
ES为它分配了内置的kibana_system角色,该角色不能进行UI登录。

最后请注意,你不能使用除了localhost127.0.0.1以外的HOST来访问es或kibana,这是由他们的证书SAN决定的。
如果需要,你就得修改setup容器的command中生成instances.yml的部分,然后重建所有容器。

登录成功后,你可以在这个页面进行用户/角色管理。

此小节参考官网docker安装es集群指导

3. 核心概念

在 Elasticsearch(ES)中,有两个核心概念:索引(Index)和文档(Document)。理解这两个概念对于有效使用 Elasticsearch 非常重要。

3.1 索引(Index)

概念
索引是 Elasticsearch 存储、索引和搜索的基本单元。它类似于数据库中的表,但是 Elasticsearch
中的索引更加灵活,它可以包含多种类型的文档,并且可以跨越多个物理分片。

在英文中还有一个indexing(翻译过来也是索引),但这个指的是对文档字段进行索引(indexing)的过程,而不是index(文档集合)本身。
可以为文档中的多个字段进行indexing。但和关系型数据库一样,indexing会占用更多磁盘以及内存空间。

在没有特别说明时,索引一般指文档集合。

用途
索引用于组织和存储具有相似结构的文档。每个文档都属于一个索引,而索引本身是一个包含有关这些文档的信息的逻辑容器。

3.2 文档(Document)

概念
文档是 Elasticsearch 中的基本信息单元,它类似于数据库中的一行。每个文档是一条具有结构化或非结构化数据的记录,通常是 JSON
格式。一个document可以包含多个字段(field),每个字段都有名称和值,它是一种JSON表现形式:{"key1": "value1"...}

用途
文档是实际存储在 Elasticsearch 中的数据。它们包含了有关实际信息的字段和值,可以是文本、数字、日期等。

3.3 字段(Field)

字段就是文档中的一个属性,它具有名称和值。字段是文档的基本组成部分,每个文档可以包含多个字段,每个字段都有不同的数据。

3.4 类型(Type)

索引是文档集合,文档是字段的集合,而每个字段都有一个类型。比如Text、Keyword、Numeric、Date、IP和Geopoint等。

  • [所有字段类型][field_types]

在未预先设置时,新插入的文档的每个字段会自动分配一个合适的类型。不同类型支持的搜索模式不同,比如Text支持全文搜索,而Keyword仅支持完全匹配。
所以我们必须为每个字段指定合适的类型(当自动分配的类型不合适时),以便在搜索时使用对应的搜索模式。

此外,为了帮助ES在插入新的文档时能够更好的为每个字段建立索引,我们需要创建(下一小节的)映射规则来使得某些字段必须转化为ES内的某种类型,这样才能支持那些字段的搜索模式。
比如对text类型字段进行模糊搜索、对keyword字段进行精确搜索、对数字字段进行聚合搜索(sort/lt/gt…)。

3.5 映射(Mapping)

[映射][Mapping]是 Elasticsearch 中用于定义文档和字段的结构化方式,类似关系型DB中的表结构。它定义了字段的类型、索引选项等。
每个索引都有且仅有一个映射。

每个index都会有一个默认的映射,这个映射会包含index中的所有字段,并且每个字段都会自动分配一个合适的类型。
如果要修改/定义索引的映射,ES支持两种映射方式:

  • 显式映射:手动为每个字段指定类型
  • 动态映射(默认启用):使用动态模板自定义映射,在文档插入时根据模板定义使用不同的类型,更灵活
    • 默认情况,ES为文档中的字段设置与其值类型相似的类型(如日期、文本和数字等),以便使用对应的搜索模式
    • 为了防止索引(indexing)过多字段导致内存使用爆炸,一般会设置映射限制设置

3.6 分片和副本(Shard and Replica)

  • 分片(Shard):创建索引时可以指定分片数量,每个分片也表现为一个独立的索引,多个分片可以并行处理搜索请求
    • 一个节点可以存放一个索引的多个分片
    • 合理的分片数量可以提高ES性能
  • 副本(Replica):一个分片可以有多个副本,以防止数据丢失和丢失后服务不可用
    • 一个节点最多只能存放一个索引的一个副本

3.7 小结

简而言之,索引是一种组织和存储文档的逻辑结构,而文档则是实际包含数据的记录。一个索引可以包含多个文档,每个文档都有自己的字段和值。在
Elasticsearch 中,你可以使用 RESTful API 或者客户端库来创建索引、添加、更新和检索文档,以及执行各种类型的查询。

当默认的映射类型不满足需求时,你可以自定义映射,以满足你的查询和搜索需求。

4. 快速上手

下面通过Kibana中的控制台来演示ES中document的增删改查。

4.1 添加document

HTTP请求:

# customer是index,1是document-id
# index不存在会自动创建,document-id存在会覆盖
# -- 在请求中可以不包含document-id,这样交给ES自动创建
POST /customer/_doc/1
{
  "firstname": "Jennifer",
  "lastname": "Walters"
}

POST可以改为PUT,语义一致。

HTTP响应:

{
  "_index": "customer",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

返回的_id是文档的唯一主键,当POST不提供document-id时,ES会自动生成一个index中的唯一字符串作为_id

4.2 查询document

HTTP请求:

GET /customer/_doc/1

HTTP响应:

{
  "_index": "customer",
  "_id": "1",
  "_version": 2,
  "_seq_no": 1,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "firstname": "Jennifer",
    "lastname": "Walters"
  }
}

响应中的found字段表示是否查询到目标文档,如有,则存在_source字段,包含目标文档的完整内容。

4.3 批量添加document

HTTP请求:

PUT customer/_bulk
{ "create": { } }
{ "firstname": "Monica","lastname":"Rambeau"}
{ "create": { } }
{ "firstname": "Carol","lastname":"Danvers"}
{ "create": { } }
{ "firstname": "Wanda","lastname":"Maximoff"}
{ "create": { } }
{ "firstname": "Jennifer","lastname":"Takeda"}

HTTP响应:

{
  "errors": false,
  "took": 3,
  "items": [
    {
      "create": {
        "_index": "customer",
        "_id": "ZR4O2YwBNp0JCdUx-pck",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "_seq_no": 3,
        "_primary_term": 1,
        "status": 201
      }
    },
    {
      "create": {
        "_index": "customer",
        "_id": "Zh4O2YwBNp0JCdUx-pck",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "_seq_no": 4,
        "_primary_term": 1,
        "status": 201
      }
    },
    {
      "create": {
        "_index": "customer",
        "_id": "Zx4O2YwBNp0JCdUx-pck",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "_seq_no": 5,
        "_primary_term": 1,
        "status": 201
      }
    },
    {
      "create": {
        "_index": "customer",
        "_id": "aB4O2YwBNp0JCdUx-pck",
        "_version": 1,
        "result": "created",
        "_shards": {
          "total": 2,
          "successful": 1,
          "failed": 0
        },
        "_seq_no": 6,
        "_primary_term": 1,
        "status": 201
      }
    }
  ]
}

4.4 搜索document

搜索是最常用的功能。HTTP请求如下:

# match表示这是一个模糊搜索
GET customer/_search
{
  "query" : {
    "match" : { "firstname": "Jennifer" }
  }
}

HTTP响应:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "max_score": 0.87546873,
    "hits": [
      {
        "_index": "customer",
        "_id": "1",
        "_score": 0.87546873,
        "_source": {
          "firstname": "Jennifer",
          "lastname": "Walters"
        }
      },
      {
        "_index": "customer",
        "_id": "bR4a2YwBNp0JCdUxEZeh",
        "_score": 0.87546873,
        "_source": {
          "firstname": "Jennifer",
          "lastname": "Takeda"
        }
      }
    ]
  }
}

注意:若请求Body为空,则返回全部文档。

4.5 删除document

HTTP请求:

# 删除document-id对应的document
DELETE customer/_doc/1

HTTP响应:

{
  "_index": "customer",
  "_id": "1",
  "_version": 2,
  "result": "deleted",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 1,
  "_primary_term": 1
}

删除整个index的请求:

DELETE customer

HTTP响应:

{
  "acknowledged": true
}

4.6 查看index的汇总信息

HTTP请求:

# customer是index名
GET customer

HTTP响应:

{
  "customer": {
    "aliases": {},
    "mappings": {
      "properties": {
        "firstname": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        },
        "lastname": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    },
    "settings": {
      "index": {
        "routing": {
          "allocation": {
            "include": {
              "_tier_preference": "data_content"
            }
          }
        },
        "number_of_shards": "1",
        "provided_name": "customer",
        "creation_date": "1704449411558",
        "number_of_replicas": "1",
        "uuid": "SPAcuTKXSk63NIN_bJwY7A",
        "version": {
          "created": "8500003"
        }
      }
    }
  }
}

其中:

  • mappings 定义了索引的映射,即字段的数据类型和属性。
  • firstnamelastname 是两个doc字段。
    • 对于每个字段,type 定义了数据类型。在这里,它们的类型都是 text,表示这些字段是用于全文搜索的。
    • 对于每个字段,ES还默认为其创建了{field-name}.keyword的子字段,其类型为keyword,用于不分词的精确搜索
    • 这里提到了text和keyword两种ES字段类型,不同类型支持不同的搜索模式
  • settings 包含了索引的设置信息。
    • number_of_shards: 1 表示索引只有一个主分片(分片数量基本等同于数据节点数量)。
    • number_of_replicas: 1 表示索引有一个副本(同上)。
    • provided_name: customer 是索引的名称。
    • uuid 是索引的唯一标识符。
    • version 包含有关索引创建版本的信息。

4.7 运维使用的API

4.7.1 非JSON响应

这些API返回非JSON格式的响应,主要是查询集群的一些基础信息,仅在监控或维护时使用。

查看集群中的索引列表:

GET /_cat/indices # 非JSON响应,其中包含索引状态、磁盘占用、分片数量、文档数量(包括删除的)、主分片大小、总分片大小等信息

查看集群健康:

GET /_cat/health # 非JSON响应

# 返回示例
1704524822 07:07:02 docker-cluster yellow 1 1 36 36 0 0 2 0 - 94.7%

这个API返回第四列是一个颜色字段,表示集群的健康状态:

  • 绿色:完全健康,所有主分片和副本分片都可用
  • 黄色:所有主分片可用,但部分副本分片不可用
  • 红色:部分主分片不可用,但部分数据仍可查到

在后面的一些API响应中你还会看到这个颜色字段,它们代表相同的含义。

查看集群主节点信息:

GET /_cat/master # 非JSON响应

查看集群所有节点的统计信息:

GET /_cat/nodes # 非JSON响应

查看分片信息:

GET /_cat/shards # 非JSON响应
4.7.2 JSON响应

查看集群健康:

GET /_cluster/health?pretty=true

在单节点集群中,你可能会看到返回的颜色是yellow,这是正常的,因为默认索引的分片和副本数量都是1(通过GET {index_name}
查询),显然还需要一个节点来容纳副本分片的存在。这就验证了上面对yellow状态的解释。我们可以通过下面两个API来进一步定位问题:

# 查看所有分片的状态,返回结果中的
# -- 第三列表示主分片(p)或副本分片(r)
# -- 第四列表示分片状态,STARTED是正常,UNASSIGNED是未分配
# -- 第五列是已分配的节点
# -- 第六列是未分配的原因
# 你会看到一行:你新增的索引的副本分片是未分配状态
GET _cat/shards?v=true&h=index,shard,prirep,state,node,unassigned.reason&s=state

# 查看分配有问题的索引,并返回具体原因
GET _cluster/allocation/explain?filter_path=index,node_allocation_decisions.node_name,node_allocation_decisions.deciders.*

既然得知了副本分片未分配的原因,那么我们就可以通过下面的API来解决:

# 将索引的副本分片数量设置为0,这样你的index就只有一个主分片位于主节点
PUT {index-name}/_settings
{
    "index": {
        "number_of_replicas" : 0
    }
}

注意,这里只是介绍如何定位集群健康处于yellow状态的原因以及如何解决的过程。实际情况中,你可能需要对集群进行扩容,以满足索引分片的需求。

查看集群最全面的统计信息(响应是一个大JSON):

GET /_cluster/state?pretty

查看集群节点监控:

GET /_nodes/state?pretty

4.8 操作映射

下面是一些常见的操作映射的HTTP请求(以customer为索引示例):

# 查询索引customer的映射
GET customer/_mapping

######
# 注意:映射只能在创建索引前设置,否则会得到index xxx already exists的提示。
# -- 也就是说,你必须预先为你的索引创建一个完善的映射配置,无论是动态映射还是显式映射。
######

# 设置动态映射中的日期格式
PUT customer
{
  "mappings": {
    "dynamic_date_formats": ["yyyy/MM/dd"]
  }
}

# 禁用日期识别(默认开启)
PUT customer
{
  "mappings": {
    "date_detection": false
  }
}

# 禁用动态映射(默认开启)。dynamic的其他参数是true/runtime/strict
PUT customer
{
  "mappings": {
    "dynamic": false
  }
}

# 启用数字识别(默认关闭),启用后可以识别字符串形式的整数/浮点数,并为其创建numeric索引以支持聚合搜索
PUT customer
{
  "mappings": {
    "numeric_detection": true
  }
}

# 配置一个简单的动态映射模板
# - 帮助更好的创建字段索引(indexing)
# - 这个模板包含t1,t2两个规则。
#   - t1表示将JSON类型为long的值映射为integer类型
#   - t2表示将JSON类型为string的值映射为text类型并创建一个子类型keyword
PUT customer
{
  "mappings":{
    "dynamic_templates": [
      {
        "t1": {
          "match_mapping_type": "long",
          "mapping":{
            "type":"integer"
          }
        }
      },
      {
        "t2": {
          "match_mapping_type": "string",
          "mapping":{
            "type":"text",
            "fields":{
              "raw": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    ]
  }
}

# 配置动态映射模板:将字符串都映射为text类型,表示此索引的所有字符串都是text类型,不需要精确搜索
PUT customer
{
	"mappings": {
		"dynamic_templates": [{
			"string_to_text": {
				"match_mapping_type": "string",
				"mapping": {
					"type": "text"
				}
			}
		}]
	}
}

# 配置动态映射模板:关闭评分(norms:false),可以节省一定存储空间
PUT customer
{
	"mappings": {
		"dynamic_templates": [{
			"string_to_keyword": {
				"match_mapping_type": "string",
				"mapping": {
					"type": "text",
					"norms": false,
					"fields": {
						"keyword": {
							"type": "keyword",
							"ignore_above": 256
						}
					}
				}
			}
		}]
	}
}

# 配置动态映射模板:禁止为数字类型创建索引(indexing)。在不需要对数字进行聚合搜索时使用。
PUT customer
{
	"mappings": {
		"dynamic_templates": [{
			"not_indexing_long": {
				"match_mapping_type": "long",
				"mapping": {
					"type": "long",
					"index": false
				}
			}
		},
		{
			"not_indexing_double": {
				"match_mapping_type": "double",
				"mapping": {
					"type": "float",
					"index": false
				}
			}
		}]
	}
}

# 配置动态映射模板:根据字段名称映射
# - 其中的doc_values是一种支持对字段值进行聚合以及排序的设置(通过列式存储的方式支持)
#   - 除了text以外的大部分字段类型都默认开启doc_values
PUT customer
{
	"mappings": {
		"dynamic_templates": [{
			"fuzzy_match": {
				"match_mapping_type": "string",
				"match": "long_*",
				"unmatch":"*_text",
				"mapping": {
					"type": "long"
				}
			}
		},
		{
			"regex_match": {
				"match_pattern": "regex",
				"match": "^profit_\\d+$",
				"mapping": {
					"type": "keyword",
					"index": false,
					"norms": false,
					"doc_values": false
				}
			}
		}]
	}
}

# 配置显式映射
# - 下面规则是为name、age等四个命名字段配置各自的类型规则
PUT customer
{
  "mappings":{
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "long"
      },
      "ctime": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      "describe": {
        "type": "keyword",
        "norms": false,
        "doc_values": false
      }
    }
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码踏云端

你的打赏是我精心创作的动力!

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

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

打赏作者

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

抵扣说明:

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

余额充值