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工作原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章