Elasticsearch 问题收集

Elasticsearch 问题收集

Mac端安装

安装elasticsearch

安装elasticsearch

❯ brew install elasticsearch
Running `brew update --auto-update`...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/core).

Error: elasticsearch has been disabled because it is switching to an incompatible license. Check out `opensearch` instead!

异常

安装elasticsearch-full

❯ brew install elastic/tap/elasticsearch-full
==> Tapping elastic/tap
Cloning into '/usr/local/Homebrew/Library/Taps/elastic/homebrew-tap'...
remote: Enumerating objects: 1395, done.
remote: Counting objects: 100% (574/574), done.
remote: Compressing objects: 100% (53/53), done.
remote: Total 1395 (delta 547), reused 531 (delta 521), pack-reused 821
Receiving objects: 100% (1395/1395), 331.44 KiB | 2.42 MiB/s, done.
Resolving deltas: 100% (1087/1087), done.
Tapped 17 formulae (37 files, 445.5KB).
==> Fetching elastic/tap/elasticsearch-full
==> Downloading https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.4-darwin-x86_64.tar.gz?tap=elastic/homeb
######################################################################## 100.0%
==> Installing elasticsearch-full from elastic/tap
Warning: Tried to install empty array to /usr/local/etc/elasticsearch/jvm.options.d
==> codesign -f -s - /usr/local/Cellar/elasticsearch-full/7.17.4/libexec/modules/x-pack-ml/platform/darwin-x86_64/controller.app
==> find /usr/local/Cellar/elasticsearch-full/7.17.4/libexec/jdk.app/Contents/Home/bin -type f -exec codesign -f -s - {} ;
==> Caveats
Data:    /usr/local/var/lib/elasticsearch/elasticsearch_hongda/
Logs:    /usr/local/var/log/elasticsearch/elasticsearch_hongda.log
Plugins: /usr/local/var/elasticsearch/plugins/
Config:  /usr/local/etc/elasticsearch/

To start elastic/tap/elasticsearch-full now and restart at login:
  brew services start elastic/tap/elasticsearch-full
Or, if you don't want/need a background service you can just run:
  elasticsearch
==> Summary
🍺  /usr/local/Cellar/elasticsearch-full/7.17.4: 946 files, 476.2MB, built in 8 seconds
==> Running `brew cleanup elasticsearch-full`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

查看

❯ brew info elasticsearch-full
==> elastic/tap/elasticsearch-full: stable 7.17.4
Distributed search & analytics engine
https://www.elastic.co/products/elasticsearch
Conflicts with:
  elasticsearch
/usr/local/Cellar/elasticsearch-full/7.17.4 (946 files, 476.2MB) *
  Built from source on 2022-12-21 at 11:37:26
From: https://github.com/elastic/homebrew-tap/blob/HEAD/Formula/elasticsearch-full.rb
==> Caveats
Data:    /usr/local/var/lib/elasticsearch/elasticsearch_hongda/
Logs:    /usr/local/var/log/elasticsearch/elasticsearch_hongda.log
Plugins: /usr/local/var/elasticsearch/plugins/
Config:  /usr/local/etc/elasticsearch/

To restart elastic/tap/elasticsearch-full after an upgrade:
  brew services restart elastic/tap/elasticsearch-full
Or, if you don't want/need a background service you can just run:
  elasticsearch

启动

brew services start elastic/tap/elasticsearch-full
❯ elasticsearch -version
warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME
Future versions of Elasticsearch will require Java 11; your Java version from [/Library/Java/JavaVirtualMachines/graalvm-ee-java8/Contents/Home/jre] does not meet this requirement. Consider switching to a distribution of Elasticsearch with a bundled JDK. If you are already using a distribution with a bundled JDK, ensure the JAVA_HOME environment variable is not set.
warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME
Future versions of Elasticsearch will require Java 11; your Java version from [/Library/Java/JavaVirtualMachines/graalvm-ee-java8/Contents/Home/jre] does not meet this requirement. Consider switching to a distribution of Elasticsearch with a bundled JDK. If you are already using a distribution with a bundled JDK, ensure the JAVA_HOME environment variable is not set.
Version: 7.17.4, Build: default/tar/79878662c54c886ae89206c685d9f1051a9d6411/2022-05-18T18:04:20.964345128Z, JVM: 1.8.0_321

Curl查看

❯ curl http://127.0.0.1:9200/
{
  "name" : "hongda-iMac.local",
  "cluster_name" : "elasticsearch_hongda",
  "cluster_uuid" : "xs9f7EnQRw28UJLcZUbJvg",
  "version" : {
    "number" : "7.17.4",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "79878662c54c886ae89206c685d9f1051a9d6411",
    "build_date" : "2022-05-18T18:04:20.964345128Z",
    "build_snapshot" : false,
    "lucene_version" : "8.11.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

安装kibana

❯ brew install elastic/tap/kibana-full
==> Fetching elastic/tap/kibana-full
==> Downloading https://artifacts.elastic.co/downloads/kibana/kibana-7.17.4-darwin-x86_64.tar.gz?tap=elastic/homebrew-tap
######################################################################## 100.0%
==> Installing kibana-full from elastic/tap
==> Caveats
Config: /usr/local/etc/kibana/
If you wish to preserve your plugins upon upgrade, make a copy of
/usr/local/opt/kibana-full/plugins before upgrading, and copy it into the
new keg location after upgrading.

To start elastic/tap/kibana-full now and restart at login:
  brew services start elastic/tap/kibana-full
Or, if you don't want/need a background service you can just run:
  kibana
==> Summary
🍺  /usr/local/Cellar/kibana-full/7.17.4: 37,690 files, 630.8MB, built in 49 seconds
==> Running `brew cleanup kibana-full`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

查看版本

❯ kibana --version
7.17.4

启动

❯ brew services start elastic/tap/kibana-full
==> Successfully started `kibana-full` (label: homebrew.mxcl.kibana-full)

查看

❯ curl --location --request GET 'http://127.0.0.1:9200/_cat/indices?v'
health status index                           uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   .geoip_databases                uZ17Xau6RgGNet5_dZZqGw   1   0         40           37     38.1mb         38.1mb
green  open   .apm-custom-link                mE3YFQn0RCuhbfNd7t2JPQ   1   0          0            0       226b           226b
yellow open   user_admin                      g3-wI7CaSKGjTu7r0d4FSg   1   1          3            0     10.1kb         10.1kb
green  open   .apm-agent-configuration        5P4eIDpcTn2UuDuLouOuPg   1   0          0            0       226b           226b
green  open   .kibana_task_manager_7.17.4_001 Ni7AVwi9QLCFpRmY-mWCMQ   1   0         17          466     95.6kb         95.6kb
yellow open   user                            eKuLL-pvT4irwxEsXra15Q   1   1          2            0      9.5kb          9.5kb
green  open   .kibana_7.17.4_001              RNF7VkEfSIOWjdelat-2aQ   1   0        317         1920      2.5mb          2.5mb

安装smartcn分词器

❯ elasticsearch-plugin install analysis-smartcn
warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME
Future versions of Elasticsearch will require Java 11; your Java version from [/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre] does not meet this requirement. Consider switching to a distribution of Elasticsearch with a bundled JDK. If you are already using a distribution with a bundled JDK, ensure the JAVA_HOME environment variable is not set.
-> Installing analysis-smartcn
-> Downloading analysis-smartcn from elastic
[=================================================] 100%
-> Installed analysis-smartcn
-> Please restart Elasticsearch to activate any plugins installed

smartcn是目前ES官方推荐的中文分词插件,不过目前不支持自定义词库。

重启

❯ brew services restart elasticsearch-full
Stopping `elasticsearch-full`... (might take a while)
==> Successfully stopped `elasticsearch-full` (label: homebrew.mxcl.elasticsearch-full)
==> Successfully started `elasticsearch-full` (label: homebrew.mxcl.elasticsearch-full)

安装ik中文分词

安装ik插件:

// 到这里找跟自己ES版本一致的插件地址
https://github.com/medcl/elasticsearch-analysis-ik/releases

我本地使用的ES版本是7.17.4,所以选择的Ik插件版本地址是:

https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.4/elasticsearch-analysis-ik-7.17.4.zip

当然你也可以尝试根据我给出这个地址,直接修改版本号,试试看行不行。

安装命令

{ES安装目录}/bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.4/elasticsearch-analysis-ik-7.17.4.zip

ik分词插件支持 ik_smart 和 ik_max_word 两种分词器

  • ik_smart - 粗粒度的分词
  • ik_max_word - 会尽可能的枚举可能的关键词,就是分词比较细致一些,会分解出更多的关键词
❯ elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.4/elasticsearch-analysis-ik-7.17.4.zip
warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME
Future versions of Elasticsearch will require Java 11; your Java version from [/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre] does not meet this requirement. Consider switching to a distribution of Elasticsearch with a bundled JDK. If you are already using a distribution with a bundled JDK, ensure the JAVA_HOME environment variable is not set.
-> Installing https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.4/elasticsearch-analysis-ik-7.17.4.zip
-> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.4/elasticsearch-analysis-ik-7.17.4.zip
[=================================================] 100%
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@     WARNING: plugin requires additional permissions     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* java.net.SocketPermission * connect,resolve
See https://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
for descriptions of what these permissions allow and the associated risks.

Continue with installation? [y/N]y
-> Installed analysis-ik
-> Please restart Elasticsearch to activate any plugins installed

ik自定义词库

/usr/local/etc/elasticsearch/analysis-ik                                                                at 08:59:01 
❯ touch demo.dic

demo.dict内容

上海大学
复旦大学
人民广场

一行一个词条即可

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict">demo.dic</entry>
	 <!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords"></entry>
	<!--用户可以在这里配置远程扩展字典 -->
	<!-- <entry key="remote_ext_dict">words_location</entry> -->
	<!--用户可以在这里配置远程扩展停止词字典-->
	<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

重启es

Ik新增扩展词库,支持热更新,不用重启ES,使用remote_ext_dict和remote_ext_stopwords配置远程词库地址即可,词库地址需要返回两个头部(header),一个是 Last-Modified,一个是 ETag,ES靠这两个头识别是否需要更新词库,不了解这两个HTTP头,可以搜一下。

Elasticsearch vs RDBMS

Elasticsearch 中,索引是一个集合,相当于数据库 ( RDBMS , 关系数据库管理系统 ) 中的数据库

集合中的每个映射,相当于 RDBMS 中的表

而索引中的每个 JSON 对象,又相当于 RDBMS 中的数据行

所以,Elasticsearch,集合是一些包含了 JSON 对象的映射的集合

详细的对比如下

Elasticsearch RDBMS
Index 索引 Database
Shard Shard
Mapping Table
Field Field
JSON Object Tuple
Elasticsearch 索引 类型 文档
DB

集群内部通过 Elasticsearch 的选主算法选出主节点,而集群外部则是可以通过任何节点进行操作,无主从节点之分 ( 对外表现对等/去中心化,有利于客户端编程,例如故障重连 )

type(文档类型)

在新版的Elasticsearch中,已经不使用文档类型了,在Elasticsearch老的版本中文档类型,代表一类文档的集合,index(索引)类似mysql的数据库、文档类型类似Mysql的表。

既然新的版本文档类型没什么作用了,那么index(索引)就类似mysql的表的概念,ES没有数据库的概念了。

提示:在Elasticsearch7.0以后的版本,已经废弃文档类型了,如果大家不是接手老的项目,可以不必理会文档类型,可以直接将index(索引)类比Mysql的表。

分片

Elasticsearch 在保存索引时会选择适合的 「 主分片 」 ( Primary Shard ),把索引保存到其中

我们可以把分片理解为一块物理存储区域

分片的分法是固定的,而且是安装时候就必须要决定好的 ( 默认是 5 ),后面就不能改变了

既然有主分片,那肯定是有 「 从 」 分片的,但 Elasticsearch 里称之为 「 副本分片 」 ( Replica Shard )

副本分片主要有两个作用:

  1. 高可用

    某分片节点挂了的话可走其他副本分片节点,节点恢复后上面的分片数据可通过其他节点恢复

  2. 负载均衡

    Elasticsearch 会自动根据负载情况控制搜索路由,副本分片可以将负载均摊

img

上图中:

  1. 3 个 Elasticsearch 节点 ( es-58/59/60 ) 组成一个集群
  2. 搭建集群时使用默认的主分片数 5,shard0 ~ shard4
  3. 该集群内有加入两个索引 index1、index2
  4. 这两个索引中分别 「 索引 」 ( 保存 ) 了两个文档
  5. index1 索引中这个文档被 Elasticsearch 自动保存到了分片 2 中,主分片在 es-58 节点,副本分片在 es-59 节点
  6. index2 索引中这个文档被 Elasticsearch 自动保存到了分片 2 中,主分片在 es-59 节点,副本分片在 es-58 节点

primary shard ( 主分片 )

每个文档都会被保存在一个主分片上

当我们索引一个文档时,它将在一个主分片上进行索引,然后才放到该主分片的各副本分片上

默认情况下,一个索引有 5 个主分片

我们可以指定更少或更多的主分片来伸缩索引可处理的文档数

需要注意的是,一旦索引创建,就不能修改主分片个数

replica shard ( 副本分片 )

每个主分片可以拥有 0 个或多个副本分片,一个副本分片是主分片的一份拷贝

这样做有两个主要原因:

  1. 故障转移

    当主分片失效时,一个副本分片会被提升为主分片

  2. 提高性能

    获取与搜索请求可以被主分片或副本分片处理

    默认情况下,每个主分片都有一个副本分片,副本分片的数量可以动态调整

    在同一个节点上,副本分片和其主分片不会同时运行

筛选

boolean

  • must:必须匹配,会影响算分结果
  • should:选择性匹配,也会影响算分结果
  • must_not:必须不能匹配,不会影响算分
  • filter:必须匹配,不会影响算分

那么如果不指定minimum_should_match,可能这四个条件中有一个满足就能查到,但是如果指定了minimum_should_match=3,那么这四个条件中必须满足三个才会返回。

POST _search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "user.id" : "kimchy" }
      },
      "filter": {
        "term" : { "tags" : "production" }
      },
      "must_not" : {
        "range" : {
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },
      "should" : [    # 一个数组,包括了两个term查询,如果没有指定must条件,那么should查询中的term必须至少满足一条查询
        { "term" : { "tags" : "env1" } },
        { "term" : { "tags" : "deployed" } }
      ],
      "minimum_should_match" : 1,
      "boost" : 1.0
    }
  }
}

更新 put是替换了整个文档,而不是更新name这一个字段

​ post更新的是文档的局部字段,原来有的字段更新,没有的字段则新增这个字段

1)数据先写入内存buffer,在写入buffer的同时将数据写入translog日志文件,注意:此时数据还没有被成功es索引记录,因此无法搜索到对应数据;

2)如果buffer快满了或者到一定时间,es就会将buffer数据refresh到一个新的segment file中,但是此时数据不是直接进入segment file的磁盘文件,而是先进入os cache的。这个过程就是refresh。
每隔1秒钟,es将buffer中的数据写入一个新的segment file,因此每秒钟会产生一个新的磁盘文件segment file,这个segment file中就存储最近1秒内buffer中写入的数据。
操作系统中,磁盘文件其实都有一个操作系统缓存os cache,因此数据写入磁盘文件之前,会先进入操作系统级别的内存缓存os cache中。

一旦buffer中的数据被refresh操作,刷入os cache中,就代表这个数据就可以被搜索到了。

这就是为什么es被称为准实时(NRT,near real-time):因为写入的数据默认每隔1秒refresh一次,也就是数据每隔一秒才能被 es 搜索到,之后才能被看到,所以称为准实时。

只要数据被输入os cache中,buffer就会被清空,并且数据在translog日志文件里面持久化到磁盘了一份,此时就可以让这个segment file的数据对外提供搜索了。

3)重复1~2步骤,新的数据不断进入buffer和translog,不断将buffer数据写入一个又一个新的segment file中去,每次refresh完,buffer就会被清空,同时translog保留一份日志数据。随着这个过程推进,translog文件会不断变大。当translog文件达到一定程度时,就会执行commit操作。

4)commit操作发生第一步,就是将buffer中现有数据refresh到os cache中去,清空buffer。

5)将一个 commit point 写入磁盘文件,里面标识着这个 commit point 对应的所有 segment file,同时强行将 os cache 中目前所有的数据都 fsync 到磁盘文件中去。

6)将现有的translog清空,然后再次重启启用一个translog,此时commit操作完成。

translog日志文件的作用是什么?

在你执行commit操作之前,数据要么是停留在buffer中,要么是停留在os cache中,无论是buffer还是os cache都是内存,一旦这台机器死了,内存中的数据就全丢了。

因此需要将数据对应的操作写入一个专门的日志文件,也就是translog日志文件,一旦此时机器宕机,再次重启的时候,es会自动读取translog日志文件中的数据,恢复到内存buffer和os cache中去。

综上可以看出:

es是准实时的,因此数据写入1秒后才可以搜索到。
如果translog是异步写入的话,es可能会丢失数据:有n秒的数据停留在buffer、translog的os cache、segment file的os cache中,也就是这n秒的数据不在磁盘上,此时如果宕机,会导致n秒的数据丢失。

translog

写入ES的数据首先会被写入translog文件,该文件持久化到磁盘,保证服务器宕机的时候数据不会丢失,由于顺序写磁盘,速度也会很快。

同步写入:每次写入请求执行的时候,translog在fsync到磁盘之后,才会给客户端返回成功
异步写入:写入请求缓存在内存中,每经过固定时间之后才会fsync到磁盘,写入量很大,对于数据的完整性要求又不是非常严格的情况下,可以开启异步写入

ES整体结构

ElasticSearch原理知识点和整体结构详解

  • 一个 ES Index 在集群模式下,有多个 Node (节点)组成。每个节点就是 ES 的Instance (实例)。
  • 每个节点上会有多个 shard (分片), P1 P2 是主分片, R1 R2 是副本分片
  • 每个分片上对应着就是一个 Lucene Index(底层索引文件)
  • Lucene Index 是一个统称由多个 Segment (段文件,就是倒排索引)组成。每个段文件存储着就是 Doc 文档。commit point记录了所有 segments 的信息

Master选举

master选举

选举策略
如果集群中存在master,认可该master,加入集群
如果集群中不存在master,从具有master资格的节点中选id最小的节点作为master

选举时机
集群启动:后台启动线程去ping集群中的节点,按照上述策略从具有master资格的节点中选举出master
现有的master离开集群:后台一直有一个线程定时ping master节点,超过一定次数没有ping成功之后,重新进行master的选举

避免脑裂
脑裂问题是采用master-slave模式的分布式集群普遍需要关注的问题,脑裂一旦出现,会导致集群的状态出现不一致,导致数据错误甚至丢失。
ES避免脑裂的策略:过半原则,可以在ES的集群配置中添加一下配置,避免脑裂的发生

文档的唯一性由 _index, _type, 和 routing values的组合来确定。

搜索发生时

搜索时,Lucene会搜索所有的segment然后将每个segment的搜索结果返回,最后合并呈现给客户。

Lucene的一些特性使得这个过程非常重要:

  • Segments是不可变的(immutable)

    • Delete? 当删除发生时,Lucene做的只是将其标志位置为删除,但是文件还是会在它原来的地方,不会发生改变
    • Update? 所以对于更新来说,本质上它做的工作是:先删除,然后重新索引(Re-index)
  • 随处可见的压缩

    Lucene非常擅长压缩数据,基本上所有教科书上的压缩方式,都能在Lucene中找到。

  • 缓存所有的所有

    Lucene也会将所有的信息做缓存,这大大提高了它的查询效率。

分步骤看数据持久化过程

通过分步骤看数据持久化过程write -> refresh -> flush -> merge

  • write 过程

img

一个新文档过来,会存储在 in-memory buffer 内存缓存区中,顺便会记录 Translog(Elasticsearch 增加了一个 translog ,或者叫事务日志,在每一次对 Elasticsearch 进行操作时均进行了日志记录)。

这时候数据还没到 segment ,是搜不到这个新文档的。数据只有被 refresh 后,才可以被搜索到。

  • refresh 过程

img

refresh 默认 1 秒钟,执行一次上图流程。ES 是支持修改这个值的,通过 index.refresh_interval 设置 refresh (冲刷)间隔时间。refresh 流程大致如下:

  1. in-memory buffer 中的文档写入到新的 segment 中,但 segment 是存储在文件系统的缓存中。此时文档可以被搜索到
  2. 最后清空 in-memory buffer。注意: Translog 没有被清空,为了将 segment 数据写到磁盘
  3. 文档经过 refresh 后, segment 暂时写到文件系统缓存,这样避免了性能 IO 操作,又可以使文档搜索到。refresh 默认 1 秒执行一次,性能损耗太大。一般建议稍微延长这个 refresh 时间间隔,比如 5 s。因此,ES 其实就是准实时,达不到真正的实时。
  • flush 过程

每隔一段时间—例如 translog 变得越来越大—索引被刷新(flush);一个新的 translog 被创建,并且一个全量提交被执行

img

上个过程中 segment 在文件系统缓存中,会有意外故障文档丢失。那么,为了保证文档不会丢失,需要将文档写入磁盘。那么文档从文件缓存写入磁盘的过程就是 flush。写入次怕后,清空 translog。具体过程如下:

  1. 所有在内存缓冲区的文档都被写入一个新的段。
  2. 缓冲区被清空。
  3. 一个Commit Point被写入硬盘。
  4. 文件系统缓存通过 fsync 被刷新(flush)。
  5. 老的 translog 被删除。
  • merge 过程

由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。

Elasticsearch通过在后台进行Merge Segment来解决这个问题。小的段被合并到大的段,然后这些大的段再被合并到更大的段。

当索引的时候,刷新(refresh)操作会创建新的段并将段打开以供搜索使用。合并进程选择一小部分大小相似的段,并且在后台将它们合并到更大的段中。这并不会中断索引和搜索。

img

一旦合并结束,老的段被删除:

  1. 新的段被刷新(flush)到了磁盘。 ** 写入一个包含新段且排除旧的和较小的段的新提交点。
  2. 新的段被打开用来搜索。
  3. 老的段被删除。

img

合并大的段需要消耗大量的I/O和CPU资源,如果任其发展会影响搜索性能。Elasticsearch在默认情况下会对合并流程进行资源限制,所以搜索仍然 有足够的资源很好地执行。

采用多个副本后,避免了单机或磁盘故障发生时,对已经持久化后的数据造成损害,但是Elasticsearch里为了减少磁盘IO保证读写性能,一般是每隔一段时间(比如5分钟)才会把Lucene的Segment写入磁盘持久化,对于写入内存,但还未Flush到磁盘的Lucene数据,如果发生机器宕机或者掉电,那么内存中的数据也会丢失,这时候如何保证?

对于这种问题,Elasticsearch学习了数据库中的处理方式:增加CommitLog模块,Elasticsearch中叫TransLog。

img

索引优化设置

索引优化主要是在 Elasticsearch 的插入层面优化,Elasticsearch 本身索引速度其实还是蛮快的,具体数据,我们可以参考官方的 benchmark 数据。我们可以根据不同的需求,针对索引优化。

批量提交

当有大量数据提交的时候,建议采用批量提交(Bulk 操作);此外使用 bulk 请求时,每个请求不超过几十M,因为太大会导致内存使用过大。

比如在做 ELK 过程中,Logstash indexer 提交数据到 Elasticsearch 中,batch size 就可以作为一个优化功能点。但是优化 size 大小需要根据文档大小和服务器性能而定。

像 Logstash 中提交文档大小超过 20MB,Logstash 会将一个批量请求切分为多个批量请求。

如果在提交过程中,遇到 EsRejectedExecutionException 异常的话,则说明集群的索引性能已经达到极限了。这种情况,要么提高服务器集群的资源,要么根据业务规则,减少数据收集速度,比如只收集 Warn、Error 级别以上的日志。

增加 Refresh 时间间隔

为了提高索引性能,Elasticsearch 在写入数据的时候,采用延迟写入的策略,即数据先写到内存中,当超过默认1秒(index.refresh_interval)会进行一次写入操作,就是将内存中 segment 数据刷新到磁盘中,此时我们才能将数据搜索出来,所以这就是为什么 Elasticsearch 提供的是近实时搜索功能,而不是实时搜索功能。

如果我们的系统对数据延迟要求不高的话,我们可以通过延长 refresh 时间间隔,可以有效地减少 segment 合并压力,提高索引速度。比如在做全链路跟踪的过程中,我们就将 index.refresh_interval 设置为30s,减少 refresh 次数。再如,在进行全量索引时,可以将 refresh 次数临时关闭,即 index.refresh_interval 设置为-1,数据导入成功后再打开到正常模式,比如30s。

在加载大量数据时候可以暂时不用 refresh 和 repliccas,index.refresh_interval 设置为-1,index.number_of_replicas 设置为0。

相关原理,请参考[原理:ES原理之索引文档流程详解]

修改 index_buffer_size 的设置

索引缓冲的设置可以控制多少内存分配给索引进程。这是一个全局配置,会应用于一个节点上所有不同的分片上。

indices.memory.index_buffer_size: 10%
indices.memory.min_index_buffer_size: 48mb

indices.memory.index_buffer_size 接受一个百分比或者一个表示字节大小的值。默认是10%,意味着分配给节点的总内存的10%用来做索引缓冲的大小。这个数值被分到不同的分片(shards)上。如果设置的是百分比,还可以设置 min_index_buffer_size (默认 48mb)和 max_index_buffer_size(默认没有上限)。

修改 translog 相关的设置

一是控制数据从内存到硬盘的操作频率,以减少硬盘 IO。可将 sync_interval 的时间设置大一些。默认为5s。

index.translog.sync_interval: 5s

也可以控制 tranlog 数据块的大小,达到 threshold 大小时,才会 flush 到 lucene 索引文件。默认为512m。

index.translog.flush_threshold_size: 512mb

translog我们在[原理:ES原理之索引文档流程详解]也有介绍。

注意 _id 字段的使用

_id 字段的使用,应尽可能避免自定义 _id,以避免针对 ID 的版本管理;建议使用 ES 的默认 ID 生成策略或使用数字类型 ID 做为主键。

注意 _all 字段及 _source 字段的使用

_all 字段及 _source 字段的使用,应该注意场景和需要,_all 字段包含了所有的索引字段,方便做全文检索,如果无此需求,可以禁用;_source 存储了原始的 document 内容,如果没有获取原始文档数据的需求,可通过设置 includes、excludes 属性来定义放入 _source 的字段。

合理的配置使用 index 属性

合理的配置使用 index 属性,analyzed 和 not_analyzed,根据业务需求来控制字段是否分词或不分词。只有 groupby 需求的字段,配置时就设置成 not_analyzed,以提高查询或聚类的效率。

减少副本数量

Elasticsearch 默认副本数量为3个,虽然这样会提高集群的可用性,增加搜索的并发数,但是同时也会影响写入索引的效率。

在索引过程中,需要把更新的文档发到副本节点上,等副本节点生效后在进行返回结束。使用 Elasticsearch 做业务搜索的时候,建议副本数目还是设置为3个,但是像内部 ELK 日志系统、分布式跟踪系统中,完全可以将副本数目设置为1个。

查询方面优化

Elasticsearch 作为业务搜索的近实时查询时,查询效率的优化显得尤为重要。

路由优化

当我们查询文档的时候,Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?它其实是通过下面这个公式来计算出来的。

shard = hash(routing) % number_of_primary_shards

routing 默认值是文档的 id,也可以采用自定义值,比如用户 ID。

不带 routing 查询

在查询的时候因为不知道要查询的数据具体在哪个分片上,所以整个过程分为2个步骤:

  • 分发:请求到达协调节点后,协调节点将查询请求分发到每个分片上。
  • 聚合:协调节点搜集到每个分片上查询结果,再将查询的结果进行排序,之后给用户返回结果。

带 routing 查询

查询的时候,可以直接根据 routing 信息定位到某个分配查询,不需要查询所有的分配,经过协调节点排序。

向上面自定义的用户查询,如果 routing 设置为 userid 的话,就可以直接查询出数据来,效率提升很多。

Filter VS Query

尽可能使用过滤器上下文(Filter)替代查询上下文(Query)

  • Query:此文档与此查询子句的匹配程度如何?
  • Filter:此文档和查询子句匹配吗?

Elasticsearch 针对 Filter 查询只需要回答「是」或者「否」,不需要像 Query 查询一样计算相关性分数,同时Filter结果可以缓存。

深度翻页

在使用 Elasticsearch 过程中,应尽量避免大翻页的出现。

正常翻页查询都是从 from 开始 size 条数据,这样就需要在每个分片中查询打分排名在前面的 from+size 条数据。协同节点收集每个分配的前 from+size 条数据。协同节点一共会受到 N*(from+size) 条数据,然后进行排序,再将其中 from 到 from+size 条数据返回出去。如果 from 或者 size 很大的话,导致参加排序的数量会同步扩大很多,最终会导致 CPU 资源消耗增大。

可以通过使用 Elasticsearch scroll 和 scroll-scan 高效滚动的方式来解决这样的问题。

也可以结合实际业务特点,文档 id 大小如果和文档创建时间是一致有序的,可以以文档 id 作为分页的偏移量,并将其作为分页查询的一个条件。

京东面试题:ElasticSearch深度分页解决方案!

协调节点(Coordinating Node)

协调节点用于做分布式里的协调,将各分片或节点返回的数据整合后返回。该节点不会被选作主节点,也不会存储任何索引数据。该服务器主要用于查询负载均衡。在查询的时候,通常会涉及到从多个 node 服务器上查询数据,并将请求分发到多个指定的 node 服务器,并对各个 node 服务器返回的结果进行一个汇总处理,最终返回给客户端。在 ES 集群中,所有的节点都有可能是协调节点,但是,可以通过设置 node.master、node.data、node.ingest 都为 false 来设置专门的协调节点。需要较好的 CPU 和较高的内存。

  • node.master:false和node.data:true,该node服务器只作为一个数据节点,只用于存储索引数据,使该node服务器功能单一,只用于数据存储和数据查询,降低其资源消耗率。
  • node.master:true和node.data:false,该node服务器只作为一个主节点,但不存储任何索引数据,该node服务器将使用自身空闲的资源,来协调各种创建索引请求或者查询请求,并将这些请求合理分发到相关的node服务器上。
  • node.master:false和node.data:false,该node服务器即不会被选作主节点,也不会存储任何索引数据。该服务器主要用于查询负载均衡。在查询的时候,通常会涉及到从多个node服务器上查询数据,并将请求分发到多个指定的node服务器,并对各个node服务器返回的结果进行一个汇总处理,最终返回给客户端。

收集

  • now的值不受time-zone影响

参考:

基础入门

Elasticsearch 基础教程

Elasticsearch 教程

Elasticsearch 教程

ElasticSearch查询DSL之组合查询(bool、boosting、constant_score、dis_max)介绍

理解ElasticSearch工作原理

posted @ 2022-12-22 11:57  hongdada  阅读(275)  评论(0编辑  收藏  举报