概述

在 ElasticSearch 学习的开始,我们首先需要了解一些 ElasticSearch 中的一些最核心的基本概念。
包括:

  • 文档
  • 索引
  • 节点
  • 分片等

文档

首先,我们来了解 ElasticSearch 中一个最基础的概念:文档
ElasticSearch 本身是面向文档的,文档是可搜索数据的最小单位
例如:

  • 日志文件中的一条日志记录
  • 一部电影详细信息
  • 一首MP3歌曲等

在 ElasicSearch 中,文档是以 JSON 格式进行保存的,JSON中可以包括多个字段,每个字段都有其对应的字段类型,包括字符串、数值、布尔、日期、二进制、范围等。
此外,每个文档都会有一个唯一标识 id,可以自己指定,也可以让 ElasticSearch 自动生成。

例如,对于一个如下的 CSV 文件记录而言:
image.png
将其转化至 ElasicSearch 存储后,得到的数据格式如下:
image.png
其中,ElasticSearch 中文档的格式可以不用预先定义,如果没有定义,ElasticSearch 会根据写入的数据自动进行推算并设置文档格式,它支持数组和嵌套类型。

一个文档在 ElasticSearch 中存储的完整数据如下所示:

  1. {
  2. "_index" : ".kibana-event-log-7.14.0-000001",
  3. "_type" : "_doc",
  4. "_id" : "RpTDinsBHEYqSWTd01YF",
  5. "_score" : 1.0,
  6. "_source" : {
  7. "@timestamp" : "2021-08-28T03:18:01.907Z",
  8. "event" : {
  9. "provider" : "eventLog",
  10. "action" : "starting"
  11. },
  12. "message" : "eventLog starting",
  13. "ecs" : {
  14. "version" : "1.8.0"
  15. },
  16. "kibana" : {
  17. "server_uuid" : "a2a9736e-6b5e-4032-aeb0-4302a42e7412"
  18. }
  19. }
  20. }

可以看到,其最外层为一组以 _ 开头的Key,它们其实都有着特殊的作用和含义:

  • _index 表示了文档所属的索引名
  • _type 表示了文档所属的类型,目前仅支持 _doc 值的 type。
  • _id 表示了文档的唯一ID
  • _source 表示了文档的原始JSON数据
  • _score 表示根据搜索条件进行的相关性打分

索引

了解了什么是文档之后,我们再看来一下 索引 是什么。
索引是文档的容器,是一类文档的结合:

  • 每个索引都有自己的 Mapping 定义,用于定义其中包含的文档的字段名称和字段类型。
  • 每个索引也都有自己的 Setting 配置,用于定义其对应的分片数和副本数等。

注意:索引(Index)这个词其实是有很多重含义的,这个对于初学 ElasticSearch 的同学而言,还是比较令人困惑的。具体来说:

  • 在ES中,索引作为名词,表示的是一类文档的结合,即文档的容器,一个ElasticSearch中可以包含多个索引。
  • 同样,在ES中,索引还可以作为动作,表示保存一个文档到ElasticSearch的动作,也叫做索引(indexing)。
  • 此外,对于传统的数据库领域而言,索引是一个名词,例如B树索引或者倒排索引。

如果我们将 ES 与传统的关系型数据库进行对比,那么大致的对比关系如下:

关系型数据库 ElasticSearch
Table Index
Row Document
Column Field
Schema Mapping
SQL DSL

集群与节点

我们都知道,ElasticSearch 本身是一个分布式架构的服务,具有高可用和横向扩展的能力。
具体来说:

  • 高可用包括:
    • 服务高可用:部分节点故障后,整体服务可以正常访问。
    • 数据高可用:部分节点故障后,数据不会丢失。
  • 可扩展性体现在:
    • 横向扩展后,可以支持更多的请求QPS
    • 横向扩展后,可以存储更多的数据

在ElasticSearch的分布式架构中,需要注意的是:

  • ElasticSearch 中不同的集群是用不同的名字来区分的,默认集群名称为 elasticsearch,可以通过配置文件或者命令行参数来修改。
  • 一个集群中可以包含1个或多个节点。
  • 每个节点就是一个 ElasticSearch 的实例,生产环境中,建议每台机器仅运行一个实例。
  • 每个节点也都有自己的节点名称,可以在配置文件或者命令行参数中设置。
  • 每个节点启动之后,都会随机分为一个UID并保存在data目录下。

节点类型

ElasticSearch 中的节点可以扮演不同的角色,即不同的节点可以有不同的节点类型,常用的节点类型包括:

  • Master Node: Master 节点是整个集群的大脑,只有 Master 节点才能修改集群的状态信息。
  • Master Eligible: Master Eligible 节点是 Master 节点的候选者,当当前的Master节点故障后,其他的Master Eligible节点将会通过选举的机制从当前的Master Eligible节点中选出新的Master节点。
  • Data Node: 用于存放数据的节点,负责保存分片数据。
  • Ingest Node: 用于通过 pipeline 的方式优化数据写入,建议作为数据写入的入口。
  • Coordinate Node: 负责接收 Client 的请求,并分发到合适的节点查询数据,并将结果汇总后进行返回,默认每个节点都会充当 Coordinate Node 的角色。
  • Hot && Warm Node: 用于将数据优先级进行分类,针对不同的数据存在不同硬件配置的Data Node,降低集群成本。
  • MachineLearning Node: 负责跑机器学习任务的Node,用于异常检测等功能。

Ps: 在生产环境中,建议每个节点承担单一的角色,保证高性能和稳定性。

节点类型 配置参数 默认值
master eligible node.master true
data node.data true
ingest node.ingest true
coordinating only 每个节点默认都是 Coordinate 节点
machine learning node.ml true(需要 x-pack 开启)

分片与副本

分片

在 ElasticSearch 中,分片可以用于解决数据水平扩展的问题。具体来说,可以将一个索引中的数据拆分成多份,每一份就对应于一个分片。
实际上,一个分片就是一个运行的 Lucene 实例。
需要注意的是,分片数在创建索引时需要指定,后续除非Reindex,否则不允许修改。

在生产环境中,分片数量的设置不宜过大,如果配置过大,会导致:

  • 影响搜索结果打分和统计结果的准确性。
  • 单个节点上存在多个分片,导致性能降低。

从 ElasticSearch 7.0 开始,默认主分片数目为1。

副本

与分片同步,副本主要用于解决数据的高可用问题。
对于一个索引而言,如果是三副本,则表明将每份数据都保留三份,即存在三份一样的数据在持久化存储中。
副本数是可以动态调整的,增加副本数在一定程度上可以提高数据读取的吞吐量。

ElasticSearch API

ElasticSearch 一个最大的优点就是其提供了完善的 REST API,从而可以很容易的被各种语言调用。

各个语言客户端访问 ElasticSearch 的方式如下:
image.png
下面,我们先粗略的了解一下 ElasticSearch 中基本的 API 是如何调用的。
我们使用 Kibana 中提供的 DevTools 来测试 ElasticSearch 的 API 调用。
image.png
其中,对于 Kibana 的 DevTools 而言,有很多使用的小技巧,例如:

  • 在某个 URL 下,按下 Command + / 键,就可以自动去打开一个新的窗口查询该 API 的接口文档。

indices

索引是 ElasticSearch 中最重要的概念之一了,下面,我们就来看一下如何查看和管理索引吧。
首先,Kibana 中提供了一个页面专门用于索引的管理。
image.png
点击索引的名称,可以看到当前索引更加详细的信息:
image.png
其中,包括了索引的Setting和Mapping等相关的配置和统计信息。

下面我们来看一下ElasticSearch 针对索引提供了哪些 API。

查询索引信息

ElasticSearch 查询索引信息的 URL 非常简单:

  1. GET jaeger-span-2021-09-09
  2. {
  3. "jaeger-span-2021-09-09" : {
  4. "aliases" : { },
  5. "mappings" : {
  6. "dynamic_templates" : [
  7. {
  8. "span_tags_map" : {
  9. "path_match" : "tag.*",
  10. "mapping" : {
  11. "ignore_above" : 256,
  12. "type" : "keyword"
  13. }
  14. }
  15. },
  16. {
  17. "process_tags_map" : {
  18. "path_match" : "process.tag.*",
  19. "mapping" : {
  20. "ignore_above" : 256,
  21. "type" : "keyword"
  22. }
  23. }
  24. }
  25. ],
  26. "properties" : {
  27. "duration" : {
  28. "type" : "long"
  29. },
  30. "flags" : {
  31. "type" : "integer"
  32. },
  33. "logs" : {
  34. "type" : "nested",
  35. "dynamic" : "false",
  36. "properties" : {
  37. "fields" : {
  38. "type" : "nested",
  39. "dynamic" : "false",
  40. "properties" : {
  41. "key" : {
  42. "type" : "keyword",
  43. "ignore_above" : 256
  44. },
  45. "tagType" : {
  46. "type" : "keyword",
  47. "ignore_above" : 256
  48. },
  49. "value" : {
  50. "type" : "keyword",
  51. "ignore_above" : 256
  52. }
  53. }
  54. },
  55. "timestamp" : {
  56. "type" : "long"
  57. }
  58. }
  59. },
  60. "operationName" : {
  61. "type" : "keyword",
  62. "ignore_above" : 256
  63. },
  64. "parentSpanID" : {
  65. "type" : "keyword",
  66. "ignore_above" : 256
  67. },
  68. "process" : {
  69. "properties" : {
  70. "serviceName" : {
  71. "type" : "keyword",
  72. "ignore_above" : 256
  73. },
  74. "tag" : {
  75. "type" : "object"
  76. },
  77. "tags" : {
  78. "type" : "nested",
  79. "dynamic" : "false",
  80. "properties" : {
  81. "key" : {
  82. "type" : "keyword",
  83. "ignore_above" : 256
  84. },
  85. "tagType" : {
  86. "type" : "keyword",
  87. "ignore_above" : 256
  88. },
  89. "value" : {
  90. "type" : "keyword",
  91. "ignore_above" : 256
  92. }
  93. }
  94. }
  95. }
  96. },
  97. "references" : {
  98. "type" : "nested",
  99. "dynamic" : "false",
  100. "properties" : {
  101. "refType" : {
  102. "type" : "keyword",
  103. "ignore_above" : 256
  104. },
  105. "spanID" : {
  106. "type" : "keyword",
  107. "ignore_above" : 256
  108. },
  109. "traceID" : {
  110. "type" : "keyword",
  111. "ignore_above" : 256
  112. }
  113. }
  114. },
  115. "spanID" : {
  116. "type" : "keyword",
  117. "ignore_above" : 256
  118. },
  119. "startTime" : {
  120. "type" : "long"
  121. },
  122. "startTimeMillis" : {
  123. "type" : "date",
  124. "format" : "epoch_millis"
  125. },
  126. "tag" : {
  127. "type" : "object"
  128. },
  129. "tags" : {
  130. "type" : "nested",
  131. "dynamic" : "false",
  132. "properties" : {
  133. "key" : {
  134. "type" : "keyword",
  135. "ignore_above" : 256
  136. },
  137. "tagType" : {
  138. "type" : "keyword",
  139. "ignore_above" : 256
  140. },
  141. "value" : {
  142. "type" : "keyword",
  143. "ignore_above" : 256
  144. }
  145. }
  146. },
  147. "traceID" : {
  148. "type" : "keyword",
  149. "ignore_above" : 256
  150. }
  151. }
  152. },
  153. "settings" : {
  154. "index" : {
  155. "routing" : {
  156. "allocation" : {
  157. "include" : {
  158. "_tier_preference" : "data_content"
  159. }
  160. }
  161. },
  162. "mapping" : {
  163. "nested_fields" : {
  164. "limit" : "50"
  165. }
  166. },
  167. "number_of_shards" : "5",
  168. "provided_name" : "jaeger-span-2021-09-09",
  169. "creation_date" : "1631145601332",
  170. "requests" : {
  171. "cache" : {
  172. "enable" : "true"
  173. }
  174. },
  175. "number_of_replicas" : "1",
  176. "uuid" : "p1c_uFGlRmu19FSKN4I61g",
  177. "version" : {
  178. "created" : "7140099"
  179. }
  180. }
  181. }
  182. }
  183. }

对,直接访问索引的名称即可,它会返回索引相关的信息,包括setting, mapping等信息。

索引列表查询

ElasticSearch 除了支持指定索引名称的查询,还支持索引列表的查询:

  1. GET _cat/indices
  2. yellow open jaeger-span-2021-09-09 p1c_uFGlRmu19FSKN4I61g 5 1 1090963 0 39.1mb 39.1mb
  3. yellow open jaeger-span-2021-09-08 qsguXsAWSSG-7OiutgprYA 5 1 799891 0 27.8mb 27.8mb
  4. yellow open jaeger-span-2021-09-03 bGlo8vHXQ7Cf_rij4P-HCw 5 1 868892 0 30.3mb 30.3mb
  5. yellow open jaeger-span-2021-09-02 tt92ktDgThKIxr8tNNrEHw 5 1 1034723 0 36.5mb 36.5mb
  6. yellow open jaeger-span-2021-09-01 aqU1y7gYSVuAe1PHLSMhEQ 5 1 819014 0 28.8mb 28.8mb
  7. yellow open jaeger-span-2021-09-07 CEChzzADT8q9RI7QoUenGA 5 1 833043 0 28.8mb 28.8mb
  8. yellow open jaeger-span-2021-09-06 Ym9TB2yJRX2mS05hPrhHZQ 5 1 982653 0 34.2mb 34.2mb
  9. yellow open jaeger-span-2021-09-05 lrZkh5o7TuKzuVt777AS6w 5 1 726351 0 24.7mb 24.7mb
  10. yellow open jaeger-span-2021-09-04 JfSHo2SGQxu-y16Lc90cKA 5 1 722567 0 24.3mb 24.3mb
  11. yellow open movies pQ1c784tQtKWREvHnKFItA 1 1 9743 0 1.2mb 1.2mb

health

ElasticSearch 提供了如下 API 可以查询集群的健康状态:

  1. GET _cluster/health
  2. {
  3. "cluster_name" : "easyenv-elasticsearch",
  4. "status" : "yellow",
  5. "timed_out" : false,
  6. "number_of_nodes" : 1,
  7. "number_of_data_nodes" : 1,
  8. "active_primary_shards" : 214,
  9. "active_shards" : 214,
  10. "relocating_shards" : 0,
  11. "initializing_shards" : 0,
  12. "unassigned_shards" : 202,
  13. "delayed_unassigned_shards" : 0,
  14. "number_of_pending_tasks" : 0,
  15. "number_of_in_flight_fetch" : 0,
  16. "task_max_waiting_in_queue_millis" : 0,
  17. "active_shards_percent_as_number" : 51.442307692307686
  18. }

其中,最核心的资源之一就是 status 了,它表示了集群的状态。
集群的状态可能是如下三种状态:

  • Green: 主分片和副本都正常分配。
  • Yellow: 主分片分配正常,有副本分片未能正常分配。
  • Red: 有主分片未分配。

node

ElasticSearch 提供了如下 API 可以查询节点列表:

  1. GET _cat/nodes
  2. 10.128.51.41 47 19 5 2.47 2.26 1.94 cdfhilmrstw * easyenv-elasticsearch-1