3

架构师修炼之道

 2 years ago
source link: https://tianmingxing.com/2022/08/29/ElasticSearch%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

ElasticSearch 基本使用

2022-08-29 2022-08-31ElasticSearch

4

随着 ES 版本的升级,文中有些概念可能已经废弃。

  • 索引词 (term)
    • 一个能够被索引的精确值,区分大小写,可以通过 term 查询进行准确的搜索。
  • 文本 (text)
    • 一段普通的非结构化文字,文本会被分析成一个个的索引词,存储在 es 的索引库中,这样才能进行搜索。
  • 分析 (analysis)
    • 将文本转换为索引词的过程,分析的结果依赖于分词器。
    • 分析机制用于进行全文文本的分词,以建立供搜索用的反向索引。
  • 索引 (index)
    • 索引类似于关系数据库中的数据库,每个索引有不同字段,可以对应不同的类型;
    • 每个索引都可以有一个或者多个主索引片,同时每个索引还可以有零个或者多个副本索引片。
  • 文档 (document)
    • 文档类似于关系数据库中的表中的行记录,每个存储在索引中的一个文档都有一个原始的 json 文档,被存储在一个叫做_source 的字段中。当搜索文档的时候默认返回的是这个字段。
    • 一个文档不只有数据,还包含了元数据 (metadata)—— 关于文档的信息:
      1. _index:文档存储的地方。名字必须是全部小写,不能以下划线开头,不能包含逗号;
      2. _id:id 仅仅是一个字符串,指一个文件的唯一标识 ,它与_index 组合时就可以在 ES 中唯一标识一个文档。
      3. 创建新文档时,可以自定义 id,也可以让 ES 自动生成 id。自动生成的 ID 有 22 个字符长,URL-safe, Base64-encoded string universally unique identifiers, 或者叫 UUIDs。
  • 映射 (mapping)
    • 映射类似于关系数据库中的表结构,每一个索引都有一个映射,它定义了索引中的每一个字段类型,以及一个索引范围内的设置。一个映射可以事先被定义,或者在第一次存储文档的时候自动识别。
    • 映射 (mapping) 机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型 (string, number, booleans, date 等) 。
  • 字段 (field)
    • 字段类似于关系数据库中表的列,每个字段都对应一个字段类型,例如整数、字符串、对象等,可以指定如何分析该字段的值。
    • 重要的是在 ES 里面:每个文档里的字段都默认会被索引并被查询。也就是说,每个字段专门有一个反向索引用于快速检索。而且与其它数据库不同,它可以在同一个查询中利用所有的这些反向索引,以惊人的速度返回结果。
  • 来源字段 (sourcefield)
    • 默认情况下,原始文档将被存储在_source 这个字段中,查询的时候也是返回这个字段。这样可以从搜索结果中访问原始的对象,这个对象返回一个精确的 json 字符串,这个对象不显示索引分析后的其他任何数据。
  • 集群 (cluster)
    • 由一个或多个共享相同群集名称的节点组成的 ES 组,它们具有相同的 cluster.name,它们协同工作,分享数据和负载。
    • ES 是一个分布式的文档 (document) 存储引擎,它可以实时存储并检索复杂数据结构,序列化的 JSON 文档。换言说,一旦文档被存储在 ES 中,它就可以在集群的任一节点上被检索。
  • 节点 (node)
    • 集群中的一个 ES 服务器,一个节点是一个逻辑上独立的服务,它是集群的一部分,可以存储数据,并参与集群的索引和搜索功能,在启动时,节点将使用广播来发现具有相同群集名称的现有群集,并将尝试加入该群集
  • 主节点
    • 每个群集有一个单独的主节点,这由程序自动选择,如果当前主节点失败,程序会自动选择其他节点作为主节点。
    • 它将临时管理集群级别的一些变更,例如新建或删除索引、增加或移除节点等。主节点不参与文档级别的变更或搜索,这意味着在流量增长的时候,该主节点不会成为集群的瓶颈。
  • 路由 (routing)
    • 操作文档时,选择文档所在分片的过程。一个文档会存储在一个唯一的主分片中,具体存储在哪个分片是通过散列值来进行选择的。
    • 默认情况下,这个值是由文档的 id 生成。
    • 如果文档有一个指定的父文档,从父文档 ID 中生成,该值可以在存储文档的时候进行修改。
  • 分片 (shard)
    • 一个单一的 Lucene 实例,用来真正存储索引并提供搜索功能。这个是由 ES 管理的比较底层的功能,ES 的索引是指向主分片和副本分片的逻辑空间。
    • 对于使用,只需要指定分片的数量,其他不需要做过多的事情。在开发使用的过程中,我们对应的对象都是索引,ES 会自动管理集群中所有的分片,当发生故障的时候,ES 会把分片移动到不同的节点或者添加新的节点。
    • 注意:可以在一个单一的 Lucene 索引中存储的最大值为 2147483519(=integer.max_value - 128)个文档。
  • 主分片 (primary shard)
    • 每个文档都存储在一个分片中,当你存储一个文档的时候,系统会首先存储在主分片中,然后会复制到不同的副本中。默认情况下,一个索引有 5 个主分片。可以在事先制定分片的数量,当分片一旦建立,分片的数量则不能修改。
  • 副本分片 (replica shard)
    • 副本是主分片的复制,每一个分片有零个或多个副本,这样做有两个目的:
      1. 增加高可用性:当主分片失败的时候,可以从副本分片中选择一个作为主分片。
      2. 提高性能:当查询的时候可以到主分片或者副本分片中进行查询。
    • 默认情况下,一个主分片有一个副本,但副本的数量可以在后面动态的配置增加。副本必须部署在不同的节点上,不能部署在和主分片相同的节点上。

以下示例均在 ES 8.3/Kibana 8.3 版本上验证通过

怎样与 ES 交互?

  • 有两种方法,一是直接向 ES 发送 HTTP 请求,拿创建索引举例:
# 注意默认情况下直接发送情况会失败,需要修改ES配置文件,将xpack.security.enabled设置为false。
# 当然在启用安全认证的情况下,其实也可以通过证书+用户的方式发送请求,我在后续文章中专门介绍。
curl -XPUT 'http://192.168.0.110:9200/mytest?pretty' -d '{
"settings": {
"index": {
"number_of_shards": 5,
"number_of_replicas": 2
}
}
}'
  • 另一种方式是使用 Kibana Dev Tools页面,使用起来非常便利,这是推荐的交互方式。
PUT /mytest
{
"settings": {
"index": {
"number_of_shards": 5,
"number_of_replicas": 2
}
}
}
  • 执行成功后会获得如下响应数据:
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "mytest"
}
  • 查看索引,列出所有的索引 GET /_cat/indices,后面跟 ?v 能看到结果的表头。
PUT /mytest/_create/p1
{
"name": "Mac Book笔记本",
"price": 12345,
"description": "这是一款笔记本",
"cats": [
"3c",
"computer"
]
}
  1. 请记住_index、_id 两者唯一确定一个文档,所以要想保证文档是新加入的,最简单的方式是让 ES 自动生成唯一_id。
  2. 如果想使用自定义的_id,必须告诉 ES 应该在_index、_id 两者都不同时才接受请求。
  3. 第一种方法使用 op_type 查询参数:PUT /website_blog/123?op_type=create
  4. 第二种方法是在 URL 后加 /_create 做为端点:PUT /website_blog/123/_create
  5. 如果请求成功的创建了一个新文档,ES 将返回正常的元数据且响应状态码是 201 Created。另一方面,如果包含相同的_index、_type 和_id 的文档已经存在,ES 将返回 409 Conflict 响应状态码。
  6. 当创建文档的时候,如果索引不存在,则会自动创建该索引。
  • 自动创建的索引会自动映射每个字段的类型。
  • 自动创建索引可以通过配置文件设置 action.auto_create_index 为 false 在所有节点的配置文件中禁用。
  • 自动映射的字段类型可以通过配置文件设置 index.mapper.dynamic 为 false 禁用。自动创建索引可以通过模板设置索引名称,例如:可以设置 action.auto_create_index 为 +aaa*, -bbb*, +ccc*, -* (+ 表示准许,- 表示禁止) 。
  1. GET /mytest/_doc/p1 请求将返回文档的全部字段,存储在_source 参数中。但是可能你感兴趣的字段只是 title。请求个别字段可以使用_source 参数。多个字段可以使用逗号分隔:GET /mytest/_doc/p1?_source=cats,name
  2. 如果你想做的只是检查文档是否存在,对内容完全不感兴趣,使用 HEAD 方法来代替 GET HEAD /mytest/_doc/p1
  3. 你也可以禁掉 source,只要设置_source=false 即可。
  4. 如果你只想获取 sorce 中的一部分内容,还可以用_source_include 或者_source_exclude 来包含或者过滤其中的某些字段 GET /mytest/_doc/p1?_source_includes=name
PUT /mytest/_doc/p1
{
"name": "新款Mac Book笔记本",
"price": 54321
}
  1. 文档在 ES 中是不可变的,如果需要更新已存在的文档,可以替换掉它。
  2. 在内部,ES 已经标记旧文档为删除并添加了一个完整的新文档。旧版本文档不会立即消失,但你也不能去访问它。ES 会在你继续索引更多数据时清理被删除的文档。
  • DELETE /mytest/_doc/p1
  • 删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。ES 将会在你之后添加更多索引的时候才会在后台进行删除内容的清理。
  • DELETE /mytest
  • 删除索引后执行 GET /mytest 会得到 status=404 的响应。
  • 通过 url 参数进行搜索 GET /mytest/_search?q=price:12345
    1. q:查询字符串
    2. df:当查询中没有定义前缀的时候默认使用的字段,例如 ?q=123445&df=price
    3. analyzer:当分析查询字符串的时候使用的分词器,例如 ?q=笔记&df=description&analyzer=ik_max_word
    4. lowercase_expanded_terms:搜索的时候忽略大小写标志,默认为 true
    5. analyze_wildcard:通配符或者前缀查询是否被分析,默认 false
    6. default_operator:默认多个条件的关系,AND 或者 OR,默认 OR
    7. lenient:如果设置为 true,字段类型转换失败的时候将被忽略,默认为 false
    8. explain:在每个返回结果中,将包含评分机制的解释
    9. _source:是否包含元数据,同时支持_source_include 和_source_exclude
    10. fields:只返回索引中指定的列,多个列中间用逗号分开
    11. sort:排序,例如 fieldName:asc 或者 fieldName:desc
    12. track_scores:评分轨迹,当排序的时候,设置为 true 的时候返回评分的信息
    13. timeout:超时的时间设置
    14. terminate_after:在每个分片中查询的最大条数,返回结果中会有一个 terminated_early 字段
    15. from:开始的记录数
    16. size:搜索结果中的条数
    17. search_type:搜索的类型,可以是 dfs_query_then_fetch,query_then_fetch,默认 query_then_fetch

关于 Timeout

  • 一般情况下搜索请求不会超时。通常,协调节点会等待接收所有分片的回答。如果有一个节点遇到问题,它会拖慢整个搜索请求。
  • Timeout 参数告诉协调节点最多等待多久,就可以放弃等待而将已有结果返回。返回部分结果总比什么都没有好。搜索请求的返回将会指出这个搜索是否超时,以及有多少分片成功答复了。
  • 你可以定义 timeout 参数为 10 或者 10ms(10 毫秒),或者 1s(1 秒),例如:GET /_search?timeout=10ms,ES 将返回在请求超时前收集到的结果。
  • 注意:超时不是一个断路器,也就是说 timeout 不会停止执行查询,它仅仅告诉你目前顺利返回结果的节点然后关闭连接。在后台,其他分片可能依旧执行查询,尽管结果已经被发送。

多索引和多类别

  • 通过限制搜索的不同索引或类型,可以在集群中跨所有文档搜索。ES 转发搜索请求到集群中的主分片或每个分片的复制分片上,收集结果后选择顶部十个返回给我们。
  • 可以通过定义 URL 中的索引或类型来限定搜索的范围,例如:
    1. /_search:在所有索引中搜索
    2. /mytest/_search:在索引 mytest 中搜索
    3. /mytest,test2/_search:在索引 mytest 和 test2 中搜索
    4. /my*,t*/_search:在以 my 或 t 开头的索引的所有类型中搜索
    5. /_all/_search:在所有索引中搜索
  • 当你搜索包含单一索引时,ES 转发搜索请求到这个索引的主分片或每个分片的复制分片上,然后聚集每个分片的结果。搜索包含多个索引也是同样的方式 —— 只不过或有更多的分片被关联。
  • 注意:搜索一个索引有 5 个主分片和 5 个索引各有一个分片事实上是一样的。
  • ES 接受 from 和 size 参数:
    • size: 结果数,默认 10
    • from: 跳过开始的结果数,默认 0
  • 如果你想每页显示 5 个结果,页码从 1 到 2,那请求如下:
    • GET /_search?size=5
    • GET /_search?size=5&from=5

在集群系统中深度分页

  • 为什么深度分页是有问题?假设在一个有 5 个主分片的索引中搜索,当请求结果的第一页(结果 1 到 10)时,每个分片产生自己最顶端 10 个结果然后返回它们给请求节点,它再排序这所有的 50 个结果以选出顶端的 10 个结果。
  • 现在假设请求第 1000 页 —— 结果 10001 到 10010,工作方式都相同,不同的是每个分片都必须产生顶端的 10010 个结果,然后请求节点排序这 50050 个结果并丢弃 50040 个!
  • 可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何语句不能返回多于 1000 个结果的原因。

关于 routing

  • 可以在操作文档的时候,指定用来计算路由的 routing 值,从而限定操作会落在哪些分片上,如果在新增文档的时候指定了 routing,那么后续对这个文档的所有操作,都应该使用同样的 routing 值,这个技术在设计非常大的搜索系统时非常有用,例如:
    1. 新增 Document ,指定 routing:
      PUT /mytest/_doc/p12?routing=myrouting
      {
      "name": "2022 Mac Book 笔记本",
      "price": 12.59,
      "description": "这是一款笔记本",
      "cats": [
      "3c",
      "computer"
      ]
      }
    2. 查询的时候,也带上相同的 routing 值 GET /mytest/_doc/p12?routing=myrouting

关于加减符号

  • 搜索中还可以使用加减号,+ 前缀表示语句匹配条件必须被满足,类似的 - 前缀表示条件,必须不被满足。
  • 所有条件如果没有 +- 表示是可选的 —— 匹配越多,相关的文档就越多,比如:GET /mytest/_search?q=+price:12.59

关于 API

现在官方推荐大家使用 HTTP 协议的 API,早期也支持 TCP 协议的 API,不过从接口兼容角度考虑后选择废弃了。

多索引参数

  • 大多数 API 支持多索引查询,就是同时可以查询多个索引中的数据,例如,参数 test1,test2,test3,表示同时搜索 test1,test2,test3 三个索引中中的数据,或者用 (_all 全部索引)。
  • 在参数中同时支持通配符的操作,例如 test*, 表示查询所有以 test 开头的索引。同时也支持排除操作,例如 + test*, -test3 表示查询所有 test 开头的索引,排除 test3。
  • 多索引查询还支持以下参数:
    1. ignore_unavailable:当索引不存在或者关闭的时候,是否忽略这些索引,值为 true 和 false。
    2. allow_no_indices:当使用通配符查询时,当有索引不存在的时候是否返回查询失败。
    3. expand_wildcards:控制什么类型的索引被支持,值为 open,close,none,all,open 表示只支持 open 类型的索引,close 表示只支持关闭状态的索引,none 表示不可用,all 表示同时支持 open 和 close 索引。
    4. 注意:文档操作 API 和索引别名 API 不支持多索引参数。
  • pretty 参数,请求的返回值是经过格式化后的 JSON 数据,这样阅读起来更加的方便。系统还提供了另一种格式 ?format=yaml,YAML 格式,这将导致返回的结果具有可读的 YAML 格式。
  • human 参数,对于统计数据,系统支持计算机数据,同时也支持适合人类阅读的数据,比如:计算机数据 size_in_bytes: 1024 更适合人类阅读的数据:"size": "1kb"。当 ?human=ture 的时候输出更适合人类阅读的数据,但这会消耗更多的资源,默认是 false。
  • 响应过滤 (filter_path)
    • 所有的返回值可以通过 filter_path 来减少返回值的内容,多个值可以用逗号分开。例如:
      GET /mytest/_doc/p1?filter_path=found,_source.cats
      {
      "found": true,
      "_source": {
      "cats": [
      "3c",
      "computer"
      ]
      }
      }
    • 它也支持通配符 * 匹配任何部分字段的名称,例如:
      GET /mytest/_doc/p1?filter_path=_source.*
      {
      "_source": {
      "name": "Mac Book笔记本",
      "price": 12345,
      "description": "这是一款笔记本",
      "cats": [
      "3c",
      "computer"
      ]
      }
      }
    • 我们可以用两个通配符 ** 来匹配不确定名称的字段,例如我们可以返回 Lucene 版本的段信息:GET /_segments?filter_path=indices.**.version
    • 有时直接返回 ES 的某个字段的原始值,如_source 字段。如果你想过滤_source 字段,可以结合_source 字段和 filter_path 参数。
  • 基于 URL 的访问控制
    • 当多用户通过 URL 访问 ES 索引的时候,为了防止用户误删除等操作,可以通过基于 URL 的访问控制来限制用户对某个具体索引的访问。
    • 可以在配置文件中添加参数:rest.action.multi.allow_explicit_index: false。这个参数默认为 true。当为 false 的时候。在请求参数中指定具体索引的请求将会被拒绝。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK