ELK学习笔记(一)之ElasticStack入门

135 阅读36分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情

一、预备知识-Restful

起源

在没有前后端分离概念之前,一个网站的完成总是“all in one”,在这个阶段,页面、数据、渲染全部在服务端完成,这样做的最大的弊端是后期维护,扩展极其痛苦,开发人员必须同时具备前后端知识。 于是后来慢慢的兴起了前后端分离的思想:即后端负责数据编造,而前端则负责数据渲染,前端静态页面调用指定 api 获取到有固定格式的数据,再将数据展示出来,这样呈现给用户的就是一个”动态“的过程。

而关于 api 这部分的设计则成了一个问题。如何设计出一个便于理解,容易使用的 api 则成了一个问题,而所谓的 restful 就是用来规范我们的 API 的一种约束。

REST

REST,其实是 Representational State Transfer(表象层,状态,转变)三个单词的缩写,它由 Roy Fielding(Fielding 是 HTTP 协议(1.0 版和 1.1 版)的主要设计者、Apache 服务器软件的作者之一、Apache 基金会的第一任主席) 于 2000 年论文中提出,他在论文中提到:"我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。

REST 指的是一组架构约束条件和原则。" 如果一个架构符合 REST 的约束条件和原则,我们就称它为 restful 架构。

要理解 restful 架构,最好的方法就是去理解 Representational State Transfer 这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会 REST 是一种什么样的设计。

资源(Resources)

REST 的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源 "(Resources)的"表现层"。

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。

要让一个资源可以被识别,需要有个唯一标识,在 Web 中这个唯一标识就是 URI(Uniform Resource Identifier)。

URI 既可以看成是资源的地址,也可以看成是资源的名称。如果某些信息没有使用 URI 来表示,那它就不能算是一个资源, 只能算是资源的一些信息而已。

你可以用一个 URI(统一资源定位符)指向它,每种资源对应一个特定的 URI。要获取这个资源,访问它的 URI 就可以,因此 URI 就成了每一个资源的地址或独一无二的识别符。

所谓"上网",就是与互联网上一系列的"资源"互动,调用它的 URI。

URI 设计技巧:

1. 使用或-来让URI可读性更好

曾经 Web 上的 URI 都是冰冷的数字或者无意义的字符串,但现在越来越多的网站使用 _- 来分隔一些单词,让 URI 看上去更为人性化。 例如某些社区新闻,它上面的地址就采用这种风格,如: www.example/news/38119/…

2. 使用 / 来表示资源的层级关系

例如:/git/git/commit/e3af72cdafab5993d18fae056f87eld675913d08 就表示了一个多级的资源, 指的是 git 用户的 git 项目的某次提交记录,又例如/orders/2022/12/xxx 可以用来表示 2022 年 12 月的订单相关信息。

3. 使用?用来过滤资源

很多人只是把 ? 简单的当做是参数的传递,很容易造成 URI 过于复杂、难以理解。可以把 ? 用于对资源的过滤, 例如:/git/git/pulls 用来表示 git 项目的所有推入请求,而 /pulls?state=closed 用来表示 git 项目中已经关闭的推入请求,这种 URL 通常对应的是一些特定条件的查询结果或算法运算结果。

; 可以用来表示同级资源的关系

有时候我们需要表示同级资源的关系时,可以使用 ; 来进行分割。例如哪天 github 可以比较某个文件在随意两次提交记录之间的差异,或许可以使用 git/git block-sha1/shal.h/compare/e3af72cdafab5993d18fae056f87e1d675913d08;bd63e61bdf38e872d5215c07b264dcc16e4febca 作为 URI。 不过,现在 github 是使用···来做这个事情的,例如 /git/git/compare/master···next。

URI不应该包含动词

因为"资源"表示一种实体,所以应该是名词,URI 不应该有动词,动词应该放在 HTTP 协议中。

举例来说,某个 URI 是/posts/show/1,其中 show 是动词,这个 URI 就设计错了,正确的写法应该是/posts/1,然后用 GET 方法表示 show。

如果某些动作是 HTTP 动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户 1向账户 2 汇款 500 元,错误的 URI 是: POST /accounts/1/transfer/500/to/2 正确的写法是把动词 transfer 改成名词 transaction,资源不能是动词,但是可以是一种服务。

4. URI 中不宜加入版本号

例如:

因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个 URI。版本号可以在 HTTP 请求头信息的 Accept字段中进行区分。

表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。

比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式;图片可以用 JPG 格式表现,也可以用 PNG 格式表现。

URI 只代表资源的实体,不代表它的形式。严格地说,有些网址最后的 ".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而 URI 应该只代表"资源"的位置。它的具体表现形式,应该在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对"表现层"的描述。

状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议 HTTP 协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

客户端用到的手段,目前来说只能是 HTTP 协议。具体来说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源。GET、PUT 和 DELETE 请求都是幂等的,无论对资源操作多少次, 结果总是一样的,

什么是 restful

架构综合上面的解释,我们总结一下什么是 restful 架构:

  1. 架构里,每一个 URI 代表一种资源;
  2. 客户端和服务器之间,传递这种资源的某种表现层;
  3. 客户端通过四个 HTTP 动词(get、post、put、delete),对服务器端资源进行操作,实现“表现层状态转化”。

注意:REST 架构风格并不是绑定在 HTTP 上,只不过目前 HTTP 是唯一与 REST 相关的实例。 所以我们这里描述的 REST 也是通过 HTTP 实现的 REST

辨析 URI、URL、URN

RFC 3986中是这么说的:

A Uniform Resource Identifier (URI) 是一个紧凑的字符串用来标示抽象或物理资源。

一个 URI 可以进一步被分为定位符、名字或两者都是 术语“Uniform Resource Locator” (URL) 是 URI 的子集, 除了确定一个资源,还提供一种定位该资源的主要访问机制。

所以,URI = Universal Resource Identifier 统一资源标志符,包含 URL 和 URN,支持的协议有 http、https、ftp、mailto、magnet、telnet、data、file、 nfs、gopher、ldap 等,java 还大量使用了一些非标准的定制模式,如 rmi,jar、 jndi 和 doc,来实现各种不同用途。

URL = Universal Resource Locator 统一资源定位符,URL 唯一地标识一个资源在 Internet 上的位置。不管用什么方法表示,只要能定位一个资源,就叫 URL。

URN = Universal Resource Name 统一资源名称,URN 它命名资源但不指定如何定位资源,比如:只告诉你一个人的姓名,不告诉你这个人在哪。

对于一个资源来说,URN 就好比他的名字,而 URL 就好比是资源的街道住址。换句话说,URN 标识了一个资源项目,而 URL 则提供了一种找到他的方法。

比如同时指定基本的获取机制和网络位置。举个例子, example.org/wiki/Main_P… 的资源,这个资源的表现形式是 HTML 和相关的代码。而获取这个资源的方法是在网络中从一个名为 example.org 的主机上,通过 HTTP( Hypertext Transfer Protocol)获得。

而 URN 则是一种在特定的名称空间中通过通过名字来标识资源的 URI。当讨论一种资源而不需要知道它的位置或者如何去获得它的时候,就可以使用 URN。例如,在 International Standard Book Number (ISBN)系统中,* ISBN 0-486-27557-4 用来指定莎士比亚的作品《罗密欧与朱丽叶》的一个特定版本。指示这一版本的 URN 是 urn:isbn:0-486-27557-4*,但是如果想要获得这个版本的书,就需要知道它的位置,这样就必须知道它的 URL。

二、什么是ELK

为什么需要 ELK

官网的说法:Elasticsearch 是一个开源的分布式 RESTful 搜索和分析引擎,能够解决越来越多不同的应用场景。

现有一个应用场景,常见的 WEB 应用日志分析。一般我们会怎么做?

登录到每台服务器上,直接在日志文件中 grep、awk 就可以获得自己想要的信息。但在规模较大的场景中,此方法效率低下,面临问题:包括日志量太大如何归档?文本搜索太慢怎么办?如何多维度查询?等。

这个时候我们希望集中化的日志管理,所有服务器上的日志收集汇总。常见解决思路是建立集中式日志收集系统,将所有节点上的日志统一收集,管理,访问。

这样对于大型系统来说,都是一个分布式部署的架构,不同的服务模块部署在不同的服务器上,问题出现时,大部分情况需要根据问题暴露的关键信息,定位到具体的服务器和服务模块,构建一套集中式日志系统,可以提高定位问题的效率。

一个完整的集中式日志系统,需要包含以下几个主要特点:

  • 收集-能够采集多种来源的日志数据
  • 传输-能够稳定的把日志数据传输到中央系统
  • 存储-如何存储日志数据
  • 分析-可以支持
  • UI 分析警告-能够提供错误报告,监控机制

ELK 就是这样一整套解决方案,并且都是开源软件,之间互相配合使用,完美衔接,高效的满足了很多场合的应用,而不仅仅是日志分析。

什么是 ELK

ELK 是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件。

Elasticsearch 是个开源分布式搜索引擎,提供搜集、分析、存储数据三大功能。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制, restful 风格接口,多数据源,自动搜索负载等。

Logstash 主要是用来日志的搜集、分析、过滤日志的工具,支持大量的数据获取方式。一般工作方式为 c/s 架构,client 端安装在需要收集日志的主机上, server 端负责将收到的各节点日志进行过滤、修改等操作在一并发往elasticsearch 上去。

Kibana 也是一个开源和免费的工具,Kibana 可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。

新增了一个 Beats 系列组件,它是一个轻量级的日志收集处理工具(Agent), Beats 占用资源少,适合于在各个服务器上搜集日志或信息后传输给 Logstash。

加入 Beats 系列组件后,官方名称就变为了 Elastic Stack,产品如下:

image.png

ElasticSearch的作用

分布式搜索和分析引擎,具有高可伸缩、高可靠和易管理等特点。基于 Apache Lucene 构建,能对大容量的数据进行接近实时的存储、搜索和分析操作。通常被用作某些应用的基础搜索引擎,使其具有复杂的搜索功能;

Logstash的作用

数据收集引擎。它支持动态的从各种数据源搜集数据,并对数据进行过滤、分析、丰富、统一格式等操作,然后存储到用户指定的位置;

Kibana的作用

数据分析和可视化平台。通常与 Elasticsearch 配合使用,对其中数据进行搜索、分析和以统计图表的方式展示;
BEATS - 轻量的数据采集器

Beats的作用

是一款轻量级的数据采集器,采用 Go 语言编写。它集合了多种单一用途数据采集器。这些采集器安装后可用作轻量型代理,从成百上千或成千上万台机器向 Logstash 或 Elasticsearch 发送数据。

轻量 —— Beats 是数据采集的得力工具。将这些采集器安装在你的服务器中,它们就会把数据汇总到 Elasticsearch。如果需要更加强大的处理性能,Beats 还能将数据输送到 Logstash 进行转换和解析。

可拓展 —— 每款开源采集器都是以用于转发数据的通用库 libbeat 为基石。需要监控某个专用协议,可以自己构建采集器。

如何安装 ELK

安装文档可以看我 Centos7安装Elasticsearch和Kibana以及Logstash 这篇文章

三、Elasticsearch 基本原理和概念

使用Kibana管理界面简单使用Elasticsearch

首先点击左上角“更多菜单”,往下滑动找到面板中的“Dev Tools”按钮,进入 Dev 工具,开始我们的 Elasticsearch 初次访问之旅。

1669813812869.png

创建索引

在左边命令窗口中输入 put elastic_test,并点击相关按钮 ,于是看到右边的结果窗口中 es 给我们返回了处理结果,表示我们在 es 中创建索引成功。

1669814033003.png

查看索引

比如要查看我们刚刚创建的索引,执行 get elastic_test

1669814199422.png

添加文档

往这个索引中添加文档,执行:

PUT /elastic_test/_doc/1
{
  "msg":"Hello World"
}

1669814524676.png

查看文档

查询我们刚刚加入的文档,执行get /elastic_test/_doc/1

1669814636472.png

Elasticsearch 基本原理和概念

从上面的例子中,我们看到了很多熟悉又陌生的概念,比如索引、文档等等。这些概念和我们平时看到的数据库里的比如索引有什么区别呢?

举个例子,现在我们要保存唐宋诗词,数据库中我们们会怎么设计?诗词表(poem_table) 我们可能的设计如下:

朝代作者标题诗词全文
李白静夜思床前明月光,疑是地上霜。举头望明月,低头思故乡。
温庭筠望江南梳洗罢,独倚望江楼。过尽千帆皆不是,斜晖脉脉水悠悠。肠断白萍洲。
梅之焕题李太白墓采石江边一堆土,李白之名高千古。来来往往一首诗,鲁班门前弄大斧。
------------

要根据朝代或者作者寻找诗,都很简单,比如 select * from poem_table where 作者='李白',如果数据很多,查询速度很慢,怎么办?我们可以在对应的查询字段上建立索引加速查询。

但是如果我们现在有个需求:要求找到包含 “望” 字的诗词怎么办?

select * from poem_table where 诗词全文 like '%望%' ,这个意味着要扫描库中的诗词全文字段,逐条比对,找出所有包含关键词 “望” 字的记录。基本上,数据库中一般的 SQL 优化手段都是用不上的。数量少,大概性能还能接受,如果数据量稍微大点,就完全无法接受了,更何况在互联网这种海量数据的情况下呢?

怎么解决这个问题呢,用倒排索引。

倒排索引(Inverted index)

还是以上面表格中的诗为例:

都有 “望” 字,于是我们可以这么保存

序号关键词静夜思望江南题李太白墓
1
---------------

如果查哪个诗词全文中包含 “白” ,怎么办,上述的表格可以继续填入新的记录

序号关键词静夜思望江南题李太白墓
1
2

其实,上述诗词的中每个字都可以作为关键字,然后建立关键字和文档之间的对应关系,也就是标识关键字被哪些文档包含。

所以,倒排索引就是,将文档中包含的关键字全部提取处理,然后再将关键字和文档之间的对应关系保存起来,最后再对关键字本身做索引排序。用户在检索某一个关键字是,先对关键字的索引进行查找,再通过关键字与文档的对应关系找到所在文档。

在存储在关系型数据库中的数据,需要我们事先分析将数据拆分为不同的字段,而在 es 这类的存储中,需要应用程序根据规则自动提取关键字,并形成对应关系。

这些预先提取的关键字,在全文检索领域一般被称为 term(词项),文档的词项提取在 es 中被称为文档分析,这是全文检索很核心的过程,必须要区分哪些是词项,哪些不是,比如很多场景下,appleapples 是同一个东西, 其实是同一个动作。

Elasticsearch 基本概念

Elasticsearch 中比较关键的基本概念有索引、文档、映射、映射类型、文档字段概念,为了方便理解,可以和关系数据库中的相关概念进行个比对:

ES7.0之前版本:

1669816853895.png

ES7.0之后版本:

image.png

Elasticsearch 索引

Elasticsearch 索引是映射类型的容器。一个 Elasticsearch 索引非常像关系型世界的数据库,是独立的大量文档集合。当然在底层,肯定用到了倒排索引,最基本的结构就是keywordPosting List,Posting list 就是一个 int 的数组,存储了所有符合某个 term 的文档 id。

另外,这个倒排索引相比特定词项出现过的文档列表,会包含更多其它信息。它会保存每一个词项出现过的文档总数, 在对应的文档中一个具体词项出现的总次数,词项在文档中的顺序,每个文档的长度,所有文档的平均长度等等相关信息。

文档 (Document)

文档是 ES 中所有可搜索数据的最小单位,比如日志文件中的日志项、一部电影的具体信息等等。文档会被序列化 JSON 格式保存到 ElasticSearch 中,JSON 对象由字段组成,每个字段都有对象的字段类型(字符串,数值,布尔,日期,二进制,范围类型)。同时每个文档都有一个 Unique ID,可以自己指定 ID,或者通过 ElasticSearch 自动生成。所以严格来说,ES 中存储的文档是一种半结构化的数据。

映射

映射(mapping)定义了每个字段的类型、字段所使用的分词器等。

1669817332995.png

get elastic_test/_mapping 可以显式映射,由我们在索引映射中进行预先定义;也可以动态映射,在添加文档的时候,由 ES 自动添加到索引,这个过程不需要事先在索引进行字段数据类型匹配等等,ES 会自己推断数据类型。

既然说到了字段类型,当然就离不开字段的数据类型了。

文档字段

文档中的一个字段 field 就相当于关系型数据库中的一列 column,那么它肯定有数据类型,ES 提供的数据类型有:

1669817541416.png

数据类型

核心数据类型

  • 字符串类型:string 字符串类还可被分为 text 和 keyword 类型,如果我们让 es 自动映射数据,那么 es 会把字符串定义为 text,并且还加了一个 keyword 类型字段。

    • text 文本数据类型,用于索引全文值的字段。使用文本数据类型的字段,它们会被分词,在索引之前将字符串转换为单个术语的列表(倒排索引),分词过程允许 ES 搜索每个全文字段中的单个单词。什么情况适合使用 text,只要不具备唯一性的字符串一般都可以使用 text。
    • keyword,关键字数据类型,用于索引结构化内容的字段。使用 keyword 类型的字段,其不会被分析,给什么值就原封不动地按照这个值索引,所以关键字字段只能按其确切值进行搜索。什么情况下使用 keyword,具有唯一性的字符串,例如:电子邮件地址、MAC 地址、身份证号、状态代码...等等。
  • 数字型数据类型:long、integer、short、byte、double、float

  • 日期类型:date -

  • 布尔类型:boolean

  • 复杂数据类型

    • 数组:无需专门的数据类型
    • 对象数据类型:单独的 JSON 对象
    • 嵌套数据类型:nested,关于 JSON 对象的数组
  • 地理数据类型:

    • 地理点数据类型
    • 地理形状数据类型
  • 专门数据类型:

    • IPv4 数据类型
    • 单词计数数据类型 token_count

结合前面的映射来看看: 创建一个新的索引: put /open-soft

显式映射:

image.png

索引或者说入库一个文档,注意这个文档的字段,比我们显示映射的字段要 多个 star 字段:

image.png

通过 get /open-soft/_mapping,我们可以看到 es 自动帮我们新增了 stars 这 个字段。

image.png

修改映射,增加一个新的字段:

image.png

数组

不需要特殊配置,一个字段如果被配置为基本数据类型,就是天生支持数组 类型的。任何字段都可以有 0 个或多个值,但是在一个数组中数据类型必须一样。

image.png

如上图是没问题的,但是如果:

image.png 就会报错。

对象

JSON 文档是有层次结构的,一个文档可能包含其他文档,如果一个文档包 含其他文档,那么该文档值是对象类型,其数据类型是对象。当然 ElasticSearch 中是没有所谓对象类型的,比如:

image.png

image.png 对象类型可以在定义索引的映射关系时进行指定。

多数据类型

如果说数组允许你使用同一个设置索引多项数据,那么多数据类型允许使用 不同的设置,对同一项数据索引多次。带来的好处就是可以同一文本有多种不同 的索引方式,比如一个字符串类型的字段,可以使用 text 类型做全文检索,使用 keyword 类型做聚合和排序。我们可以看到 es 的动态映射生成的字段类型里, 往往字符串类型都使用了多数据类型。当然,我们一样也可以自己定义:

image.png

在上面的代码里,我们使用"fields"就把 name 字段扩充为多字段类型,为 name 新增了两个子字段 raw 和 length,raw 设置类型为 keyword,length 设置类 型为 token_count,告诉 es 这个字段在保存还需要做词频统计。

通过 fields 字段设置的子字段 raw 和 length,在我们添加文档时,并不需要 单独设置值,他们 name 共享相同的值,只是 es 会以不同的方式处理字段值。 同样在检索文档的时候,它们也不会显示在结果中,所以它们一般都是在检索中 以查询条件的形式出现,以减少检索时的性能开销。

字段参数

在上面的代码里出现了analyzer这个词,这是什么?这个叫字段参数,和type 一样,可以用来对字段进行配置。常用的字段参数和作用如下:

analyzer

指定分词器。elasticsearch 是一款支持全文检索的分布式存储系统,对于 text 类型的字段,首先会使用分词器进行分词,然后将分词后的词根一个一个存储在 倒排索引中,后续查询主要是针对词根的搜索。

analyzer 该参数可以在每个查询、每个字段、每个索引中使用,其优先级如 下(越靠前越优先):

  1. 字段上定义的分词器
  2. 索引配置中定义的分词器
  3. 默认分词器(standard)
normalizer

规范化,主要针对 keyword 类型,在索引该字段或查询字段之前,可以先对 原始数据进行一些简单的处理,然后再将处理后的结果当成一个词根存入倒排索 引中,默认为 null,比如:

image.png

  • 代码 1:首先在 settings 中的 analysis 属性中定义 normalizer。
  • 代码 2:设置标准化过滤器,示例中的处理器为小写、asciifolding。
  • 代码 3:在定义映射时,如果字段类型为 keyword,可以使用 normalizer 引用定义好的 normalizer
boost

权重值,可以提升在查询时的权重,对查询相关性有直接的影响,其默认值 为 1.0。其影响范围为词根查询(team query),对前缀、范围查询。5.0 版本后已废 止。

coerce

数据不总是我们想要的,由于在转换 JSON body 为真正 JSON 的时候,整型数 字 5 有可能会被写成字符串"5"或者浮点数 5.0,这个参数可以将数值不合法的部 分去除。默认为 true。

例如:将字符串会被强制转换为整数、浮点数被强制转换为整数。

例如:存在如下字段类型: "number_one": { "type": "integer" }

声明 number_one 字段的类型为数字类型,那是否允许接收“6”字符串形 式的数据呢?因为在 JSON 中,“6”用来赋给 int 类型的字段,也是能接受的, 默认 coerce 为 true,表示允许这种赋值,但如果 coerce 设置为 false,此时 es 只能接受不带双引号的数字,如果在 coerce=false 时,将“6”赋值给 number_one 时会抛出类型不匹配异常。

copy_to

copy_to 参数允许您创建自定义的_all 字段。换句话说,多个字段的值可以 复制到一个字段中。

例如,first_name 和 last_name 字段可以复制到 full_name 字段如下:

image.png

表示字段 full_name 的值来自 first_name + last_name。

关于 copy_to 重点说明:

  • 1、字段的复制是原始值。
  • 2、同一个字段可以复制到多个字段,写法如下:“copy_to”: [ “field_1”, “field_2” ]
doc_values

Doc values 的存在是因为倒排索引只对某些操作是高效的。 倒排索引的优 势 在于查找包含某个项的文档,而对于从另外一个方向的相反操作并不高效, 即:确定哪些项是否存在单个文档里,聚合需要这种次级的访问模式。 对于以下倒排索引:

image.png

如果我们想要获得所有包含 brown 的文档的词的完整列表,倒排索引是根据 项来排序的,所以我们首先在词项列表中找到 brown ,然后扫描所有列,找到包 含 brown 的文档。我们可以快速看到Doc_1和Doc_2 包含brown这个token。

然后,对于聚合部分,我们需要找到 Doc_1 和 Doc_2 里所有唯一的词项。 用倒 排索引做这件事情代价很高: 我们会迭代索引里的每个词项并收 集 Doc_1 和 Doc_2 列里面 token。这很慢而且难以扩展:随着词项和文档的数量 增加,执行时间也会增加。

Doc values 通过转置两者间的关系来解决这个问题。倒排索引将词项映射到包 含它们的文档,doc values 将文档映射到它们包含的词项:

image.png

当数据被转置之后,想要收集到 Doc_1 和 Doc_2 的唯一 token 会非常容易。获 得每个文档行,获取所有的词项,然后求两个集合的并集。 doc_values 缺省是 true,即是开启的,并且只适用于非 text 类型的字段。

dynamic

是否允许动态的隐式增加字段。在执行 index api 或更新文档 API 时,对于 _source 字段中包含一些原先未定义的字段采取的措施,根据 dynamic 的取值, 会进行不同的操作:

  • true,默认值,表示新的字段会加入到类型映射中。
  • false,新的字段会被忽略,即不会存入_souce 字段中,即不会存储新字段, 也无法通过新字段进行查询。
  • strict,会显示抛出异常,需要新使用 put mapping api 先显示增加字段映射。
enabled

是否建立索引,默认情况下为 true,es 会尝试为你索引所有的字段,但有时 候某些类型的字段,无需建立索引,只是用来存储数据即可。也就是说, ELasticseaech 默认会索引所有的字段,enabled 设为 false 的字段,elasicsearch 会 跳过字段内容,该字段只能从_source 中获取,但是不可搜。只有映射类型(type) 和 object 类型的字段可以设置 enabled 属性。

eager_global_ordinals

表示是否提前加载全局顺序号。Global ordinals 是一个建立在 doc values 和 fielddata 基础上的数据结构, 它为每一个精确词按照字母顺序维护递增的编号。 每一个精确词都有一个独一无二的编号 并且 精确词 A 小于精确词 B 的编号. Global ordinals 只支持 keyword 和 text 型字段,在 keyword 字段中, 默认是启 用的 而在 text 型字段中 只有 fielddata 和相关属性开启的状态下才是可用的。

fielddata

为了解决排序与聚合,elasticsearch 提供了 doc_values 属性来支持列式存储, 但 doc_values 不支持 text 字段类型。因为 text 字段是需要先分析(分词),会 影响 doc_values 列式存储的性能。

es 为了支持 text 字段高效排序与聚合,引入了一种新的数据结构(fielddata), 使用内存进行存储。默认构建时机为第一次聚合查询、排序操作时构建,主要存 储倒排索引中的词根与文档的映射关系,聚合,排序操作在内存中执行。因此 fielddata 需要消耗大量的 JVM 堆内存。一旦 fielddata 加载到内存后,它将永久 存在。

通常情况下,加载 fielddata 是一个昂贵的操作,故默认情况下,text 字段的 字段默认是不开启 fielddata 机制。再使用 fielddata 之前请慎重考虑为什么要开 启 fielddata。

format

在 JSON 文档中,日期表示为字符串。Elasticsearch 使用一组预先配置的格式 来识别和解析这些字符串,并将其解析为 long 类型的数值(毫秒),支持自定义格 式,也有内置格式。

image.png

elasticsearch 为我们内置了大量的格式,如下:

  • epoch_millis 时间戳,单位,毫秒,范围受限于 Java Long.MIN_VALUE 和 Long.MAX_VALUE。

  • epoch_second 时间戳,单位,秒,范围受限于 Java 的限制 Long.MIN_VALUE 并 Long. MAX_VALUE 除以 1000(一秒钟的毫秒数)。

  • date_optional_time 或者 strict_date_optional_time 日期必填,时间可选,其支持的格式如下:

    • date-opt-time = date-element ['T' [time-element] [offset]]
    • date-element = std-date-element | ord-date-element | week-date-element
    • std-date-element = yyyy ['-' MM ['-' dd]]
    • ord-date-element = yyyy ['-' DDD]
    • week-date-element = xxxx '-W' ww ['-' e]
    • time-element = HH [minute-element] | [fraction]
    • minute-element = ':' mm [second-element] | [fraction]
    • second-element = ':' ss [fraction]

比如"yyyy-MM-dd"、"yyyyMMdd"、"yyyyMMddHHmmss"、 "yyyy-MM-ddTHH:mm:ss"、"yyyy-MM-ddTHH:mm:ss.SSS"、 "yyyy-MM-ddTHH:mm:ss.SSSZ"格式,不支持常用的"yyyy-MM-dd HH:mm:ss"等格式。 注意,"T"和"Z"是固定的字符。

tips:如果看到“strict_”前缀的日期格式要求,表示 date_optional_time 的 严格级别,这个严格指的是年份、月份、天必须分别以 4 位、2 位、2 位表示, 不足两位的话第一位需用 0 补齐。

  • basic_date 其格式表达式为 :yyyyMMdd
  • basic_date_time 其格式表达式为:yyyyMMdd’T’HHmmss.SSSZ
  • basic_date_time_no_millis 其格式表达式为:yyyyMMdd’T’HHmmssZ
  • basic_ordinal_date 4 位数的年 + 3 位(day of year),其格式字符串为 yyyyDDD - basic_ordinal_date_time 其格式字符串为 yyyyDDD’T’HHmmss.SSSZ
  • basic_ordinal_date_time_no_millis 其格式字符串为 yyyyDDD’T’HHmmssZ
  • basic_time 其格式字符串为 HHmmss.SSSZ
  • basic_time_no_millis 其格式字符串为 HHmmssZ
  • basic_t_time 其格式字符串为’T’HHmmss.SSSZ
  • basic_t_time_no_millis 其格式字符串为’T’HHmmssZ
  • basic_week_date 其格式字符串为 xxxx’W’wwe,4 为年 ,然后用’W’, 2 位 week of year (所在年里周序号) 1 位 day of week。
  • basic_week_date_time 其格式字符串为 xxxx’W’wwe’T’HH:mm:ss.SSSZ.
  • basic_week_date_time_no_millis 其格式字符串为 xxxx’W’wwe’T’HH:mm:ssZ.
  • date 其格式字符串为 yyyy-MM-dd date_hour 其格式字符串为 yyyy-MM-dd’T’HH
  • date_hour_minute 其格式字符串为 yyyy-MM-dd’T’HH:mm
  • date_hour_minute_second 其格式字符串为 yyyy-MM-dd’T’HH:mm:ss
  • date_hour_minute_second_fraction 其格式字符串为 yyyy-MM-dd’T’HH:mm:ss.SSS
  • date_hour_minute_second_millis 其格式字符串为 yyyy-MM-dd’T’HH:mm:ss.SSS
  • date_time 其格式字符串为 yyyy-MM-dd’T’HH:mm:ss.SSS
  • date_time_no_millis 其格式字符串为 yyyy-MM-dd’T’HH:mm:ss
  • hour 其格式字符串为 HH
  • hour_minute 其格式字符串为 HH:mm
  • hour_minute_second 其格式字符串为 HH:mm:ss
  • hour_minute_second_fraction 其格式字符串为 HH:mm:ss.SSS
  • hour_minute_second_millis 其格式字符串为 HH:mm:ss.SSS
  • ordinal_date 其格式字符串为 yyyy-DDD,其中 DDD 为 day of year。
  • ordinal_date_time 其格式字符串为 yyyy-DDD‘T’HH:mm:ss.SSSZZ,其中 DDD 为 day of year。
  • ordinal_date_time_no_millis 其格式字符串为 yyyy-DDD‘T’HH:mm:ssZZ
  • time 其格式字符串为 HH:mm:ss.SSSZZ
  • time_no_millis 其格式字符串为 HH:mm:ssZZ
  • t_time 其格式字符串为’T’HH:mm:ss.SSSZZ
  • t_time_no_millis 其格式字符串为’T’HH:mm:ssZZ
  • week_date 其格式字符串为 xxxx-'W’ww-e,4 位年份,ww 表示 week of year,e 表示 day of week。
  • week_date_time 其格式字符串为 xxxx-'W’ww-e’T’HH:mm:ss.SSSZZ
  • week_date_time_no_millis 其格式字符串为 xxxx-'W’ww-e’T’HH:mm:ssZZ
  • weekyear 其格式字符串为 xxxx
  • weekyear_week 其格式字符串为 xxxx-'W’ww,其中 ww 为 week of year。
  • weekyear_week_day 其格式字符串为 xxxx-'W’ww-e,其中 ww 为 week of year,e 为 day of week。
  • year 其格式字符串为 yyyy
  • year_month 其格式字符串为 yyyy-MM
  • year_month_day 其格式字符串为 yyyy-MM-dd
ignore_above

ignore_above 用于指定字段索引和存储的长度最大值,超过最大值的会被忽略。

ignore_malformed

ignore_malformed 可以忽略不规则数据,对于 login 字段,有人可能填写的 是 date 类型,也有人填写的是邮件格式。给一个字段索引不合适的数据类型发 生异常,导致整个文档索引失败。如果 ignore_malformed 参数设为 true,异常会 被忽略,出异常的字段不会被索引,其它字段正常索引。

index

index 属性指定字段是否索引,不索引也就不可搜索,取值可以为 true 或者 false,缺省为 true。

index_options
  • index_options 控制索引时存储哪些信息到倒排索引中,用于搜索和突出显 示目的。 - docs 只存储文档编号
  • freqs 存储文档编号和词项频率。
  • positions 文档编号、词项频率、词项的位置被存储
  • offsets 文档编号、词项频率、词项的位置、词项开始和结束的字符位置都 被存储。
fields

fields 可以让同一文本有多种不同的索引方式,比如一个 String 类型的字段, 可以使用 text 类型做全文检索,使用 keyword 类型做聚合和排序。

norms

norms 参数用于标准化文档,以便查询时计算文档的相关性。norms 虽然对 评分有用,但是会消耗较多的磁盘空间,如果不需要对某个字段进行评分,最好 不要开启 norms。

null_value

一般来说值为 null 的字段不索引也不可以搜索,null_value 参数可以让值为 null 的字段显式的可索引、可搜索。

image.png

文档 1 可以被搜索到,因为 status_code 的值为 null,文档 2 不可以被搜索 到,因为 status_code 为空数组,但是不是 null。

position_increment_gap

文本数组元素之间位置信息添加的额外值。

举例,一个字段的值为数组类型:"names": [ "John Abraham", "Lincoln Smith"]

为了区别第一个字段和第二个字段,Abraham 和 Lincoln 在索引中有一个间距,默认是 100。例子如下,这是查询”Abraham Lincoln”是查不到的

image.png

指定间距大于 100 可以查询到:

image.png

想要调整这个值,在 mapping 中通过 position_increment_gap 参数指定间距 即可。

properties

Object 或者 nested 类型,下面还有嵌套类型,可以通过 properties 参数指定。 比如:

image.png

对应的文档结构:

image.png

search_analyzer

通常,在索引时和搜索时应用相同的分析器,以确保查询中的术语与反向索 引中的术语具有相同的格式,如果想要在搜索时使用与存储时不同的分词器,则 使用 search_analyzer 属性指定,通常用于 ES 实现即时搜索(edge_ngram)。

similarity

指定相似度算法,其可选值:

  • BM25 当前版本的默认值,使用 BM25 算法。
  • classic 使用 TF/IDF 算法,曾经是 es,lucene 的默认相似度算法。
  • boolean 一个简单的布尔相似度,当不需要全文排序时使用,并且分数应该只基于查 询条件是否匹配。布尔相似度为术语提供了一个与它们的查询boost相等的分数。
store

默认情况下,字段值被索引以使其可搜索,但它们不存储。这意味着可以查 询字段,但无法检索原始字段值。通常这并不重要。字段值已经是_source 字段 的一部分,该字段默认存储。如果您只想检索单个字段或几个字段的值,而不是 整个_source,那么这可以通过字段过滤上下文 source filting context 来实现。

在某些情况下,存储字段是有意义的。例如,如果您有一个包含标题、日期 和非常大的内容字段的文档,您可能只想检索标题和日期,而不需要从大型 _source 字段中提取这些字段,可以将标题和日期字段的 store 定义为 ture。

term_vector

Term vectors 包含分析过程产生的索引词信息,包括:

  • 索引词列表
  • 每个索引词的位置(或顺序)
  • 索引词在原始字符串中的原始位置中的开始和结束位置的偏移量。
  • term vectors 会被存储,索引它可以作为一个特使的文档返回。
  • term_vector 可取值:
    • no 不存储 term_vector 信息,默认值。
    • yes 只存储字段中的值。
  • with_positions 存储字段中的值与位置信息。
  • with_offsets 存储字段中的值、偏移量
  • with_positions_offsets 存储字段中的值、位置、偏移量信息。

元字段

一个文档根据我们定义的业务字段保存有数据之外,它还包含了元数据字段 (meta-fields)。元字段不需要用户定义,在任一文档中都存在,有点类似于数据库 的表结构数据。在名称上有个显著的特征,都是以下划线“_”开头。

我们可以看看:get /open-soft/_doc/1

大体分为五种类型:身份(标识)元数据、索引元数据、文档元数据、路由 元数据以及其他类型的元数据,当然不是每个文档这些元字段都有的。

身份(标识)元数据
  • _index:文档所属索引 , 自动被索引,可被查询,聚合,排序使用,或者脚本里访问 。
  • _type:文档所属类型,自动被索引,可被查询,聚合,排序使用,或者脚 本里访问。
  • _id:文档的唯一标识,建索引时候传入 ,不被索引,可通过_uid 被查询, 脚本里使用,不能参与聚合或排序。
  • _uid:由_type 和_id 字段组成,自动被索引 ,可被查询,聚合,排序使用, 或者脚本里访问,6.0.0 版本后已废止。
索引元数据
  • _all: 自动组合所有的字段值,以空格分割,可以指定分器词索引,但是整 个值不被存储,所以此字段仅仅能被搜索,不能获取到具体的值。6.0.0 版本后 已废止。

  • _field_names:索引了每个字段的名字,可以包含 null 值,可以通过 exists 查询或 missing 查询方法来校验特定的字段。

文档元数据
  • _source : 一个 doc 的原生的 json 数据,不会被索引,用于获取提取字段 值 ,启动此字段,索引体积会变大,如果既想使用此字段又想兼顾索引体积, 可以开启索引压缩。
  • _source 是可以被禁用的,不过禁用之后部分功能不再支持,这些功能包括:
    • 部分 update api、运行时高亮搜索结果
    • 索引重建、修改 mapping 以及分词、索引升级
    • debug 查询或者聚合语句
    • 索引自动修复
  • _size: 整个_source 字段的字节数大小,需要单独安装一个 mapper-size 插 件才能展示。
路由元数据

_routing: 一个 doc 可以被路由到指定的 shard 上。

其他

_meta:一般用来存储应用相关的元信息。

例如:

put /open-soft/_mapping 
{ 
"_meta": {
    "class": "site.iswho.User", 
    "version": {"min": "1.0", "max": "1.3"} 
    }
}

主要参考:享学课堂Mark老师