最强 Elastic Stack 保姆级教程(一)

2022-05-22 10:05:24 浏览数 (1)

本文是《Elastic Stack 完整版教程》的第一部分!点击下方卡片关注本公众号,持续更新ES教程!

本文目录: 一、Elastic Stack 家族介绍

  1. 全文检索介绍
  2. Elastic Stack 介绍

二、Elasticsearch 介绍

  1. Elasticsearch
  2. Elasticsearch 使用案例
  3. Elasticsearch 对比 Solr
  4. Elasticsearch 基本概念及架构

三、Elasticsearch 基础使用

  1. Kibana 的基本介绍
  2. ES 操作前须知
  3. ES API 操作
  4. ES 花式查询
  5. 零停机重新索引数据

一、Elastic Stack 家族介绍

1. 全文检索介绍

首先我们谈几个公司,如雷贯耳的:百度、谷歌、维基百科;这些公司都有一个相似性就是门户网站,可以提供我们通过关键字搜索,然后快速的检索出我们想要的信息。

对于一般的公司,初期是没有那么多数据的,所以很多公司更倾向于使用传统的数据库:Mysql;比如我们要查找关键字”五分钟学大数据“,那么查询的方式大概就是:

select * from table where field like ‘%五分钟学大数据%’;

但是随着业务发展,数据会不断的膨胀,那么问题就来了;Mysql 单表查询能力即便经过了优化,它的极限也就是 400W 左右的数据量。而且还会经常出现查询超时的现象。

然后很多公司开始对数据库进行横向和纵向的扩容,开始进行数据库表的“拆分”:横向拆分和纵向拆分;但是即便这样操作,仍然会出现很多问题,比如:

  1. 数据库会出现单点故障问题,于是先天主从复制关系,于是增加了运维成本
  2. 因为对表的拆分,增加了后期维护的难度,同样也是增加了运维成本
  3. 即便做了大量的维护,但对于大数据的检索操作,依然很慢,完全达不到期望值

于是出现了Lucene,全文检索的工具。但是 Lucene 对外暴露出的可用接口对于开发人员来说,操作是非常的复杂,而且没有效率的。

于是在 Lucene 的基础上进一步的封装,有了一个叫做Solr的高性能分布式检索服务框架,但是,Solr 有一个致命的缺点就是:在建立索引期间,Solr 的搜索能力会极度下降,这就在一定程度上造成了 Solr 在实时索引上效率并不高。

最后,出现了一个叫做Elasticsearch的框架,同样是以 Lucene 为基础,并且吸收了前两代的教训而开发出的分布式多用户能力的全文搜索引擎,并且 Elasticsearch 是基于 RESTful Web 接口进行发布的,那么这就意味着,我们开发人员操作起来更方便快捷;同时 ES 拓展节点方便,可用于存储和检索海量数据,接近实时搜索能力,自动发现节点、副本机制保障可用性。

1) 全文检索(Full-text Search)

将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引

例如:字典。字典的拼音表和部首检字表就相当于字典的索引,对每一个字的解释是非结构化的,如果字典没有音节表和部首检字表,在茫茫辞海中找一个字只能顺序扫描。然而字的某些信息可以提取出来进行结构化处理,比如读音,就比较结构化,分声母和韵母,分别只有几种可以一一列举,于是将读音拿出来按一定的顺序排列,每一项读音都指向此字的详细解释的页数。我们搜索时按结构化的拼音搜到读音,然后按其指向的页数,便可找到我们的非结构化数据——也即对字的解释。

这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。

虽然创建索引的过程也是非常耗时的,但是索引一旦创建就可以多次使用,全文检索主要处理的是查询,所以耗时间创建索引是值得的。

2) 全文检索框架

市面上全文检索的框架很多,较早期的一个框架就是Lucene,基本上所有的全文检索的工作都交给 Lucene 来实现,但是 Lucene 最大的弊端就是 API 太原生,没有经过任何封装,不太好使用。

所以后来出现一个叫做Solr的框架,它也是基于 Lucene 进行改造封装和包装,将服务端单独提取出来,客户端进行请求即可。

另外一个框架就是大名鼎鼎的 Elasticsearch 了,ES 也是一个基于 Lucene 打造的全文检索的框架,且一经推出就迅速被市场认可,市场占有率越来越多,现在首选的全文检索的框架基本就是 ES 了。

2. Elastic Stack 介绍

Elastic Stack 是什么?其实就是 ELK Stack。

Elastic Stack 是 ELK Stack 的更新换代产品

那 ELK 又是什么?

“ELK”是三个开源项目的首字母缩写,这三个项目分别是:Elasticsearch、Logstash 和 Kibana

  • Elasticsearch 是一个搜索和分析引擎。
  • Logstash 是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如 Elasticsearch 等“存储库”中。
  • Kibana 则可以让用户在 Elasticsearch 中使用图形和图表对数据进行可视化。
发展历程:
一切都起源于 Elasticsearch…

这个开源的分布式搜索引擎基于 JSON 开发而来,具有 RESTful 风格。它使用简单,可缩放规模,十分灵活,因此受到用户的热烈好评,而且如大家所知,围绕这一产品还形成了一家专门致力于搜索的公司。

引入 Logstash 和 Kibana,产品更强大

Elasticsearch 的核心是搜索引擎,所以用户开始将其用于日志用例,并希望能够轻松地对日志进行采集和可视化。有鉴于此,官方引入了强大的采集管道 Logstash 和灵活的可视化工具 Kibana。

社区越来越壮大,用例越来越丰富

无论是在混乱如麻的文本型文档中找到前 N 个结果,还是分析安全事件,再或是自由地对指标进行切片和切块,全球社区一直都在使用 ELK 不断地拓展使用范围。

然后官方向 ELK 中加入了 Beats

“我只想对某个文件进行 tail 操作,”用户表示。官方用心倾听。在 2015 年,官方向 ELK Stack 中加入了一系列轻量型的单一功能数据采集器,并把它们叫做 Beats。

那么,ELK 需要怎么变化呢?

ELK 这个名称又要变了,的确如此。把它叫做 BELK?BLEK?ELKB?当时官方的确有过继续沿用首字母缩写的想法。然而,对于扩展速度如此之快的堆栈而言,一直采用首字母缩写的确不是长久之计。

就这样,Elastic Stack 这个名字应运而生了

上图就是 Elastic Stack 协议栈,从下面的采集工具,到中间搜索工具,到上层展示工具。

我们接下来就一个个详解,当然先从最核心的 Elasticsearch 开始!

二、Elasticsearch 介绍

1. Elasticsearch

Elasticsearch 是一个基于 Apache Lucene 的开源搜索引擎。无论在开源还是专有领域,Lucene 可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。

特点:

  • 分布式的实时文件存储,每个字段都被索引并可被搜索
  • 分布式的实时分析搜索引擎--做不规则查询
  • 可以扩展到上百台服务器,处理 PB 级结构化或非结构化数据

Elasticsearch 也使用 Java 开发并使用 Lucene 作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的 RESTful API 来隐藏 Lucene 的复杂性,从而让全文搜索变得简单。

ES 能做什么?

全文检索(全部字段)、模糊查询(搜索)、数据分析(提供分析语法,例如聚合)

2. Elasticsearch 使用案例

  • 2013 年初,GitHub 抛弃了 Solr,采取 Elasticsearch 来做 PB 级的搜索。“GitHub 使用 Elasticsearch 搜索 20TB 的数据,包括 13 亿文件和 1300 亿行代码”
  • 维基百科:启动以 Elasticsearch 为基础的核心搜索架构
  • 百度:百度目前广泛使用 Elasticsearch 作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部 20 多个业务线(包括 casio、云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大 100 台机
  • 新浪使用 ES 分析处理 32 亿条实时日志
  • 阿里使用 ES 构建挖财自己的日志采集和分析体系

3. Elasticsearch 对比 Solr

  • Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能
  • Solr 支持更多格式的数据,而 Elasticsearch 仅支持 json 文件格式
  • Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供
  • Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch

4. Elasticsearch 基本概念及架构

1) Elasticsearch 基本概念
1. 索引 index
  • 一个索引就是一个拥有几分相似特征的文档的集合。比如说,可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引
  • 一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字
  • 在一个集群中,可以定义任意多的索引。
2. 映射 mapping
  • Elasticsearch 中的映射(Mapping)用来定义一个文档
  • mapping 是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等,这些都是映射里面可以设置的
3. 字段 Field

相当于是数据表的字段,对文档数据根据不同属性进行的分类标识

4. 类型 Type

每一个字段都应该有一个对应的类型,例如:Text、Keyword、Byte 等

5. 文档 document
  • 一个文档是一个可被索引的基础信息单元。比如,可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以 JSON(Javascript Object Notation)格式来表示,而 JSON 是一个到处存在的互联网数据交互格式
6. 集群 cluster
  • 一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能
  • 一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”
  • 这个名字是重要的,因为一个节点只能通过指定某个集群的名字,来加入这个集群
7. 节点 node
  • 一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能
  • 一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中
  • 这意味着,如果在网络中启动了若干个节点,并假定它们能够相互发现彼此,它们将会自动地形成并加入到一个叫做“elasticsearch”的集群中
  • 在一个集群里,可以拥有任意多个节点。而且,如果当前网络中没有运行任何 Elasticsearch 节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。
8. 分片和副本 shards&replicas

分片:

  • 一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有 10 亿文档的索引占据 1TB 的磁盘空间,而任一节点都没有这样大的磁盘空间;或者单个节点处理搜索请求,响应太慢
  • 为了解决这个问题,Elasticsearch 提供了将索引划分成多份的能力,这些份就叫做分片
  • 当创建一个索引的时候,可以指定你想要的分片的数量
  • 每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上
  • 分片很重要,主要有两方面的原因
    1. 允许水平分割/扩展你的内容容量
    2. 允许在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量
  • 至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由 Elasticsearch 管理的,对于作为用户来说,这些都是透明的

副本:

  • 在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch 允许你创建分片的一份或多份拷贝,这些拷贝叫做副本分片,或者直接叫副本
  • 副本之所以重要,有两个主要原因
    1. 在分片/节点失败的情况下,提供了高可用性。注意到复制分片从不与原/主要(original/primary)分片置于同一节点上是非常重要的
    2. 扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行
  • 每个索引可以被分成多个分片。一个索引有 0 个或者多个副本
  • 一旦设置了副本,每个索引就有了主分片和副本分片,分片和副本的数量可以在索引创建的时候指定
  • 在索引创建之后,可以在任何时候动态地改变副本的数量,但是不能改变分片的数量
2) Elasticsearch 基本架构

Gateway 是 ES 用来存储索引的文件系统,支持多种类型。

Gateway 的上层是一个分布式的 lucene 框架。

Lucene 之上是 ES 的模块,包括:索引模块、搜索模块、映射解析模块等。

ES 模块之上是 Discovery、Scripting 和第三方插件。

  • Discovery 是 ES 的节点发现模块,不同机器上的 ES 节点要组成集群需要进行消息通信,集群内部需要选举 master 节点,这些工作都是由 Discovery 模块完成。支持多种发现机制,如 Zen 、EC2、gce、Azure。
  • Scripting 用来支持在查询语句中插入 javascript、python 等脚本语言,scripting 模块负责解析这些脚本,使用脚本语句性能稍低。ES 也支持多种第三方插件。

再上层是 ES 的传输模块和 JMX.传输模块支持多种传输协议,如 Thrift、memecached、http,默认使用 http。JMX 是 java 的管理框架,用来管理 ES 应用。

最上层是 ES 提供给用户的接口,可以通过 RESTful 接口和 ES 集群进行交互。

三、Elasticsearch 基础使用

为节省篇幅,使更重要的知识点展示出来,让各位朋友更好的抓住重点,本文不讲解 Elasticsearch 安装,如未安装相关软件,可网上搜索相关教程安装。

对 Elasticsearch 操作有多种工具,这里我们使用 Kibana,先简单介绍下。

1. Kibana 的基本介绍

Kibana 是一个开源的分析和可视化平台,设计用于和 Elasticsearch 一起工作。

可以用 Kibana 来搜索,查看,并和存储在 Elasticsearch 索引中的数据进行交互

可以轻松地执行高级数据分析,并且以各种图标、表格和地图的形式可视化数据

Kibana 使得理解大量数据变得很容易。它简单的、基于浏览器的界面使你能够快速创建和共享动态仪表板,实时显示 Elasticsearch 查询的变化。

2. ES 操作前须知

1) REST 和 CURL 介绍:
REST:

REST(REpresentational State Transfer) 从字面意思看“表述性状态传输”,它通常是开发的一种约定,当所有的开发者都遵从这种约定的时候,可以大大简化开发的沟通成本。REST 约定用 HTTP 的请求头 POST、GET、PUT、DELETE 正好可以对应 CRUD(Create、Read、Update、Delete)四种数据操作。

如下表所示:

HTTP 方法

数据处理

说明

POST

Create

新增一个没有 ID 的资源

GET

Read

获取一个资源

PUT

Update

更新一个资源。或新增一个含 ID 的资源(如果 ID 不存在)

DELETE

Delete

删除一个资源

CURL:

curl 是利用 URL 语法在命令行方式下工作的开源文件传输工具,使用 curl 可以简单实现常见的 get/post 请求。简单的认为是可以在命令行下面访问 url 的一个工具。在 centos 的默认库里面是有 curl 工具的,如果没有请 yum 安装即可。

curl 后面可跟参数:

-X 指定 http 的请求方法 有HEAD GET POST PUT DELETE

-d 指定要传输的数据

-H 指定 http 请求头信息

Elasticsearch 提供了非常全面和强大的 REST API,通过这些 API,我们可以了解集群的信息。这些 API 可以做如下事情:

  1. 检查集群、节点和索引的情况、状态和统计。
  2. 管理集群、节点、索引数据和文档数据。
  3. 执行 CRUD(创建、读取、更新和删除)操作,可以对索引进行操作。
  4. 执行高级搜索操作,如分页、排序、过滤、脚本、聚合及其他操作。
2) ES 请求方式

首先说下请求 ES 的方式,后面例子都是这种方式:

请求参数:curl -X{PUT} http://{host}:{port}

  • 例如:curl -XPUT http://120.0.0.1:9200/blog1?pretty
  • 在 Kibana 中可直接输入 PUT /blog1?pretty

body 参数:{参数内容}

  • 例如:
代码语言:javascript复制
{
  "settings":{
  "index":{
      "number_of_shards":1,
      "number_of_replicas":0
   }
  }
}

注意:

请求方法 GET 和 DELETE 上面介绍了,比较简单,不过多说,主要说下 POST 和 PUT 在 ES 中的使用:

POST 和 PUT 共同点:

  • ES 中的 PUT 和 POST 同样都具备创建和更新的功能;

POST 和 PUT 不同点:

  • PUT 需要精确到某一个资源文件,这样才能进行对数据的更新和创建操作;
  • POST 能对整个资源集合进行操作,如果没有指定具体修改的文件 id,那么 POST 指令会自动生成一个唯一的 id 进行文件的创建,并将数据写入该文件中。如果指定了文件 id,那么就会根据填写的参数对数据进行创建和更新操作;
  • PUT、GET、DELETE 是幂等的,由于同一条这样的指令,执行多次结果都一样。比如 PUT /uri/xxx 多次,那么结果和这条指令执行一次效果一样。
  • 而 POST 是非幂等的,执行多次更改多次服务器状态。比如 POST /uri 多次,那么生成多个 UUID 的 document,执行多次效果和执行一次不一样

3. ES API 操作

1) 创建索引

ES 的索引是一个逻辑概念,它包括了分词列表及文档列表。同一个索引中存储了相同类型的文档,它就相当于 MySQL 中的表,或相当于 Mongodb 中的集合

关于索引这个语:

索引(名词):ES 是基于 Lucene 构建的一个搜索服务,它要从索引库搜索符合条件索引数据。 索引(动词):索引库刚创建起来是空的,将数据添加到索引库的过程称为索引。

  • 创建索引命令

PUT http://node01:9200/索引名称?pretty

  • pretty

上面命令最后有个 pretty,什么意思呢,在任意的查询字符串中增加 pretty 参数,会让 Elasticsearch 美化输出(pretty-print) JSON 响应以便更加容易阅读。

  • 创建索引举例

打开 kibana,找到 dev tools

出现如下页面,左边是执行区,右边是结果返回区:

左边框中执行以下语句:

代码语言:javascript复制
curl -XPUT http://node01:9200/blog001/?pretty

上述语句直接粘贴到 kibana 的 dev tools,如下左边所示,它会自动帮我们格式化语句,像 curl -X,和地址都自动隐藏了。

上面执行的命令并没有设置 body 参数,执行的就是默认参数。

执行如下带 body 参数的语句:

代码语言:javascript复制
curl -XPUT http://node01:9200/blog2/?pretty
{
  "settings":{
    "index":{
      "number_of_shards":1,
      "number_of_replicas":0
    }
  }
}

number_of_shards:设置分片的数量,在集群中通常设置多个分片,表示一个索引库将拆分成多片分别存储不同的结点,提高了 ES 的处理能力和高可用性,使用单机环境,这里设置为 1。

number_of_replicas:设置副本的数量,设置副本是为了提高 ES 的高可靠性,单机环境设置为 0。

创建索引相当于关系数据库中的数据库还是表?

  1. 如果相当于数据库就表示一个索引库可以创建很多不同类型的文档,这在 ES 中也是允许的。
  2. 如果相当于表就表示一个索引库只能存储相同类型的文档,ES 官方建议在一个索引库中只存储相同类型的文档。
2) 插入文档

ES 中的文档相当于 MySQL 数据库表中的数据

使用 PUT 将一个文档添加到 /_doc(文档类型),并为该文档分配 ID 为 1。URL 路径显示为 index/doctype/ID(索引/文档类型/ID)

代码语言:javascript复制
curl -XPUT http://node01:9200/blog01/_doc/1?pretty
{"id": "1", "title": "五分钟学大数据"}

注意

上面说的文档类型,也叫映射类型(type)

什么是映射类型?

映射类型 简单理解相当于关系数据库的表(不准确,后面会说),每个索引里都可以有一个或多个映射类型,映射类型是 index 中的一个逻辑数据分类,一个映射类型下的文档(document),都有相同的字段(field)。

ES 6.0 之前的版本有映射类型(type) 概念,但是官方将在 ES9.0 版本中彻底删除映射类型。

为什么要移除映射类型?

开始的时候,我们说“索引(index)”类似于 SQL 数据库中的“数据库”,将“类型(type)”等同于“表”。

这是一个糟糕的类比,并且导致了一些错误的假设。在 SQL 数据库中,表之间是相互独立的。一个表中的各列并不会影响到其它表中的同名的列。而在映射类型(mappingtype)中却不是这样的。

在同一个 Elasticsearch 索引中,其中不同映射类型中的同名字段在内部是由同一个 Lucene 字段来支持的。换句话说,假如有两个映射类型 user 和 tweet,user 类型中的 user_name 字段与 tweet 类型中的 user_name 字段是完全一样的,并且两个 user_name 字段在两个类型中必须具有相同的映射(定义)。

这会在某些情况下导致一些混乱,比如,在同一个索引中,当你想在其中的一个类型中将 deleted 字段作为 date 类型,而在另一个类型中将其作为 boolean 字段。

在此之上需要考虑一点,如果同一个索引中存储的各个实体如果只有很少或者根本没有同样的字段,这种情况会导致稀疏数据,并且会影响到 Lucene 的高效压缩数据的能力。

基于这些原因,将映射类型的概念从 Elasticsearch 中移除。

所以

在 ES6.0 及之后版本中要弱化映射类型的概念,尽量设置一个无任何业务含义的名字,本文档的所有映射类型都统一设为_doc

3) 查询文档
代码语言:javascript复制
curl -XGET http://node01:9200/blog01/_doc/1?pretty
4) 更新文档
代码语言:javascript复制
curl -XPUT http://node01:9200/blog01/_doc/1?pretty
{"id": "1", "title": "公众号【五分钟学大数据】"}
5) 搜索文档
代码语言:javascript复制
GET /blog01/article/_search?q=title:公众号【五分钟学大数据】
返回值说明

1. Hits

返回结果中最重要的部分是 hits ,它包含 total 字段来表示匹配到的文档总数,并且一个 hits 数组包含所查询结果的前十个文档。

在 hits 数组中每个结果包含文档的 _index_type_id ,加上 _source 字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的 ID,需要你单独去获取文档。

每个结果还有一个 _score ,它衡量了文档与查询的匹配程度。默认情况下,首先返回最相关的文档结果,就是说,返回的文档是按照 _score 降序排列的。

max_score 值是与查询所匹配文档的 _score 的最大值。

2. took

took 值告诉我们执行整个搜索请求耗费了多少毫秒。

3. Shard

_shards 部分告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。

如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。

4. timeout

timed_out 值告诉我们查询是否超时。默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10 毫秒),或者 1s(1 秒):

GET /_search?timeout=10ms

在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。

6) 删除文档
代码语言:javascript复制
DELETE /blog01/article/1?pretty
7) 删除索引
代码语言:javascript复制
DELETE /blog01?pretty
8) 创建映射

概念说明

在索引中每个文档都包括了一个或多个字段(field),创建映射就是向索引库中创建字段及字段类型等的过程,下面是 document 和 field 与关系数据库的概念的类比:

文档(Document)------Row 记录 字段(Field)------Columns 列

为什么要映射?

ES 中的文档等价于 java 中的对象,那么在 java 对象中有字段类型(比如 string、int、long 等),同理在 ES 索引中的具体字段也是有类型的。

像下面这个操作:

代码语言:javascript复制
PUT /document/_doc/1
{
  "title" : "Elasticsearch 怎么学",
  "author" : "五分钟学大数据",
  "titleScore" : 60
}

这种操作并没有指定字段类型,那么 Elasticsearch 会自动根据数据类型的格式识别字段的类型;

查看索引的字段类型:

GET /document/_mapping

可以发现 titleScore 的类型是 long。

然后在插入一条数据,注意此时 titleScore 的值不是 long 类型:

代码语言:javascript复制
PUT /document/_doc/2
{
  "title" : "Elasticsearch 怎么学",
  "author" : "五分钟学大数据",
  "titleScore" : 66.666
}

查询数据:

GET /document/_doc/2

我们会发现 ES 能存入,并没有报错(注意),这其实是一个问题,因为如果后期 elaticsearch 对接 java 的时候,我们会写一个类对数据做封装,比如:

代码语言:javascript复制
class Article{
private String title;
private String author;
private String titleScore
}

对于 titleScore,设置什么类型合适?使用 long 类型,那么后面肯定会有数据格式转换的异常 double-long

所以,我们如果能提前知道字段类型,那么最好使用 mapping 的映射管理,提前指定字段的类型,防止后续的程序问题

先删除之前创建的索引:DELETE document

创建映射命令

创建索引,并指定映射:

代码语言:javascript复制
PUT document
{
  "mappings": {
      "properties":
      {
        "title" : {"type": "text"} ,
        "author" : {"type": "text"} ,
        "titleScore" : {"type": "double"}

      }
  }
}

查看索引:GET document/_mapping

对这个索引继续添加字段

代码语言:javascript复制
POST /document/_mapping
{
  "properties": {"number" : {"type": "text"}}
}

查看索引:GET document/_mapping

9) 映射字段的类型

在 Elasticsearch 中,每一个字段都有一个类型(type)。以下为 Elasticsearch 中可以使用的类型:

分类

类型名称

说明

简单类型

text

需要进行全文检索的字段,通常使用 text 类型来对应正文、产品描述或者短文等非结构化文本数据。分词器先会将文本进行分词转换为词条列表。将来就可以基于词条来进行检索了。文本字段不能用户排序、也很少用户聚合计算。

简单类型

keyword

使用 keyword 来对应结构化的数据,如 ID、电子邮件地址、主机名、状态代码、邮政编码或标签。可以使用 keyword 来进行排序或聚合计算。**注意:keyword 是不能进行分词的**。

简单类型

date

保存格式化的日期数据,例如:2015-01-01 或者 2015/01/01 12:10:30。在 Elasticsearch 中,日期都将以字符串方式展示。可以给 date 指定格式:”format”: “yyyy-MM-dd HH:mm:ss”

简单类型

long/integer/short/byte

64 位整数/32 位整数/16 位整数/8 位整数

简单类型

double/float/half_float

64 位双精度浮点/32 位单精度浮点/16 位半进度浮点

简单类型

boolean

“true”/”false”

简单类型

ip

IPV4(192.168.1.110)/IPV6(192.168.0.0/16)

JSON 分层嵌套类型

object

用于保存 JSON 对象

JSON 分层嵌套类型

nested

用于保存 JSON 数组

特殊类型

geo_point

用于保存经纬度坐标

特殊类型

geo_shape

用于保存地图上的多边形坐标

10) 获取映射字段

语法:GET /{index}/_mapping/field/{field}

代码语言:javascript复制
GET /document/_mapping/field/number
11) 索引库配置管理(settings)

所谓的 settings 就是用来修改索引分片和副本数的。

比如有的重要索引,副本数很少甚至没有副本,那么我们可以通过 setting 来添加副本数。

查看 settings:GET /document/_settings

可以看到当前的副本数是 1,那么为了提高容错性,我们可以把副本数改成 2:

代码语言:javascript复制
PUT /document/_settings
{
  "number_of_replicas": 2
}

注意:副本可以改,分片不能改!

执行如下语句会报错:

代码语言:javascript复制
PUT /document/_settings
{
  "number_of_shards": 3
}

4. ES 花式查询

我们先插入一些数据,后面查询时以这些数据为例。

在 Kibana 界面上执行如下语句,插入一些数据:

代码语言:javascript复制
POST /student/_doc/_bulk
{ "index": { "_id": 1 }}
{ "name" : "liubei", "age" : 20 , "sex": "boy", "birth": "1996-01-02" , "about": "i like diaocan he girl" }
{ "index": { "_id": 2 }}
{ "name" : "guanyu", "age" : 21 , "sex": "boy", "birth": "1995-01-02" , "about": "i like diaocan" }
{ "index": { "_id": 3 }}
{ "name" : "zhangfei", "age" : 18 , "sex": "boy", "birth": "1998-01-02" , "about": "i like travel" }
{ "index": { "_id": 4 }}
{ "name" : "diaocan", "age" : 20 , "sex": "girl", "birth": "1996-01-02" , "about": "i like travel and sport" }
{ "index": { "_id": 5 }}
{ "name" : "panjinlian", "age" : 25 , "sex": "girl", "birth": "1991-01-02" , "about": "i like travel and wusong" }
{ "index": { "_id": 6 }}
{ "name" : "caocao", "age" : 30 , "sex": "boy", "birth": "1988-01-02" , "about": "i like xiaoqiao" }
{ "index": { "_id": 7 }}
{ "name" : "zhaoyun", "age" : 31 , "sex": "boy", "birth": "1997-01-02" , "about": "i like travel and music" }
{ "index": { "_id": 8 }}
{ "name" : "xiaoqiao", "age" : 18 , "sex": "girl", "birth": "1998-01-02" , "about": "i like caocao" }
{ "index": { "_id": 9 }}
{ "name" : "daqiao", "age" : 20 , "sex": "girl", "birth": "1996-01-02" , "about": "i like travel and history" }
1) 使用 match_all 做查询
代码语言:javascript复制
GET /student/_doc/_search?pretty
{
    "query": {
        "match_all": {}
    }
}

问题:通过 match_all 匹配后,会把所有的数据检索出来,但是往往真正的业务需求并非要找全部的数据,而是检索出自己想要的;并且对于 es 集群来说,直接检索全部的数据,很容易造成 GC 现象。所以,我们要学会如何进行高效的检索数据。

2) 通过关键字段进行查询
代码语言:javascript复制
GET /student/_doc/_search?pretty
{
    "query": {
         "match": {"about": "travel"}
     }
}

如果此时想查询喜欢旅游的,并且不能是男孩的,怎么办?

这样写是否可以:

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
    "query": {
         "match":
           {
             "about": "travel",
             "sex": "girl"
           }
     }
}

发现报错了,因为一个 match 下,不能出现多个字段值,需要使用复合查询。

3) bool 的复合查询

当出现多个查询语句组合的时候,可以用 bool 来包含。

bool 合并聚包含:must,must_not 或者 should, should 表示 or 的意思。

例:查询非男性中喜欢旅行的人

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
"query": {
   "bool": {
      "must": { "match": {"about": "travel"}},
      "must_not": {"match": {"sex": "boy"}}
     }
  }
}
4) bool 的复合查询中的 should

should 表示可有可无的(如果 should 匹配到了就展示,否则就不展示)

例:查询喜欢旅行的,如果有男性的则显示,否则不显示

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
"query": {
   "bool": {
      "must": { "match": {"about": "travel"}},
      "should": {"match": {"sex": "boy"}}
     }
  }
}
5) term 匹配

使用 term 进行精确匹配(比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型))

语法:

{ "term": { "age": 20 }} { "term": { "date": "2018-04-01" }} { "term": { "sex": “boy” }} { "term": { "about": "trivel" }}

例:

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
"query": {
   "bool": {
      "must": { "term": {"about": "travel"}},
      "should": {"term": {"sex": "boy"}}
     }}
}
6) 使用 terms 匹配多个值
代码语言:javascript复制
GET /student/_doc/_search?pretty
{
"query": {
   "bool": {
      "must": { "terms": {"about": ["travel","history"]}}
     }
  }
}

注意:term 和 match 区别:

  • term 主要是用于精确的过滤比如说:”我爱你”
  • 在 match 下面匹配可以为包含:我、爱、你、我爱等等的解析器
  • 在 term 语法下面就精准匹配到:”我爱你”
7) Range 过滤

Range 过滤允许我们按照指定的范围查找一些数据:操作范围:gt: 大于,gae: 大于等于,lt: 小于,lte: 小于等于

例:查找出大于 20 岁,小于等于 25 岁的学生

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
"query": {
   "range": {
    "age": {"gt":20,"lte":25}
         }
      }
}
8) exists 和 missing 过滤

exists 和 missing 过滤可以找到文档中是否包含某个字段或者是没有某个字段。

例:查找字段中包含 age 的文档

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
 "query": {
   "exists": {
     "field": "age"
   }
 }
}
9) bool 的多条件过滤

用 bool 也可以像之前 match 一样来过滤多行条件:

must : 多个查询条件的完全匹配,相当于 and 。 must_not : 多个查询条件的相反匹配,相当于 not 。 should : 至少有一个查询条件匹配, 相当于 or 。

例:过滤出 about 字段包含 travel 并且年龄大于 20 岁小于 30 岁的同学

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
  "query": {
    "bool": {
      "must": [
        {"term": {
          "about": {
            "value": "travel"
          }
        }},
        {"range": {
          "age": {
            "gte": 20,
            "lte": 30
          }
        }}
      ]
    }
  }
}
10) 查询与过滤条件合并

通常复杂的查询语句,我们也要配合过滤语句来实现缓存,用 filter 语句就可以来实现。

例:查询出喜欢旅行的,并且年龄是 20 岁的文档

代码语言:javascript复制
GET /student/_doc/_search?pretty
{
  "query": {
   "bool": {
     "must": {"match": {"about": "travel"}},
     "filter": [{"term":{"age": 20}}]
     }
  }
}

5. 零停机重新索引数据

实际生产,对于文档的操作,偶尔会遇到这种问题:

某一个字段的类型不符合后期的业务了,但是当前的索引已经创建了,我们知道 ES 在字段的 mapping 建立后就不可再次修改 mapping 的值。

1) 新建索引库 articles1
代码语言:javascript复制
PUT articles1
{
    "settings":{
         "number_of_shards":3,
         "number_of_replicas":1
    },
    "mappings":{
             "dynamic":"strict",
             "properties":{
                 "id":{"type": "text", "store": true},
                 "title":{"type": "text","store": true},
                 "readCounts":{"type": "integer","store": true},
                 "times": {"type": "text", "index": false}
             }
    }
}
2) 索引库 articles1 添加数据
代码语言:javascript复制
PUT articles1/_doc/1
{
  "id" : "1",
  "title" : "世界1",
  "readCounts" : 2 ,
  "times" : "2022-05-01"
}
3) 新建索引库 articles2
代码语言:javascript复制
PUT articles2
{
    "settings":{
         "number_of_shards":5,
         "number_of_replicas":1
    },
    "mappings":{
             "dynamic":"strict",
             "properties":{
                 "id":{"type": "text", "store": true},
                 "title":{"type": "text","store": true},
                 "readCounts":{"type": "integer","store": true},
                 "times": {"type": "date", "index": false}
             }
    }
}
4) 拷贝数据并验证
代码语言:javascript复制
POST _reindex
{
  "source": {
    "index": "articles1"
  },
  "dest": {
    "index": "articles2"
  }
}

验证数据:

代码语言:javascript复制
GET articles2/_doc/1

未完待续...

0 人点赞