一文帶你掌握 ElasticSearch 原理和技術

語言: CN / TW / HK

1 ElasticSearch 概述

1.1 全文搜尋引擎

小夥伴們經常使用 google 或者百度進行搜尋內容,在輸入框中輸入 關鍵字 ,這個時候,網站會將包含 關鍵字 的所有網頁返回,大家有沒有想過, 為什麼輸入關鍵字就可以查到結果呢?

同時網站上返回的頁面內容大多都是一些 非結構化 的文字資料,對於大量的文字資料,肯定是不會儲存到 資料庫 中的,原因如下:

(1)非結構化文字資料,關係型資料庫搜尋不能很好支援全文索引掃描整張表。

(2)查詢效率低下,即使對 SQL 進行大量優化,其效果也收效甚微。

(3)對於 insert 和 update 操作都會重新構建索引,維護非常麻煩。

針對上述問題,在生產環境中,面對海量的資料,若想要 毫秒級 查詢到結構化資料或非結構化資料,我們就需要專業,健壯,強大的全文搜尋引擎。

全文搜尋引擎的 工作原理 :計算機索引程式通過 掃描 文章中的每一個 ,對每一個詞 建立一個索引 ,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程式就 根據事先建立的索引 進行查詢,並將 查詢結果 反饋給使用者。這個過程類似於通過字典中的檢索字表查字的過程

1.2 什麼是 ElasticSearch ?

為了實現毫秒級的搜尋效果,ElasticSearch 出現了~

ElasticSearch(彈性搜尋):是一款開源的分散式、RESTful 風格的搜尋和資料分析引擎,它底層基於 Apache Lucene 開源庫進行封裝,其不僅僅提供分散式多使用者能力的全文搜尋引擎,還可以被準確形容為:

1、一個分散式的實時文件儲存,每個欄位可以被 索引搜尋

2、一個分散式 實時 分析搜尋引擎;

3、能勝任 上百個 節點的擴充套件,並支援 PB 級別結構化和非結構化資料。

ElasticSearch搜尋整體架構如下圖所示:

1.3 ElasticSearch 基本概念

1.3.1 節點&叢集

Elasticsearch 本質上是一個 分散式資料庫 ,允許多臺伺服器協同工作,每臺伺服器可以執行多個 Elasticsearch 例項。單個 Elasticsearch 例項稱為一個節點(Node),一組節點構成一個叢集(Cluster)。

1.3.2 索引&型別&文件&欄位&對映

對映關係如下圖:

什麼是 index (索引) ?

一個 索引 就是一個擁有幾分相似特徵的文件的集合。ES 將資料儲存於一個或多個索引中, 索引 就相當於 SQL 中的一個 資料庫

什麼是 Type(型別)?

型別是索引內部的邏輯分割槽(category/partition),然而其意義完全取決於使用者需求。因此,一個索引內部可定義一個或多個型別(type)。一般來說,型別就是為那些擁有相同的域的文件做的預定義。類比傳統的關係型資料庫領域來說, 型別 相當於 ,7.x 版本預設使用 _doc 作為 type 。

什麼是 Document(文件)?

文件是 Lucene 索引和搜尋的 原子單位 ,它是包含了一個或多個域的容器,基於 Json 格式進行表示。文件有一個或多個域組成,每個域擁有一個名字及一個或多個值,有多個值的域通常被稱為 多值域 ,每個文件可以儲存不同的域集,但同一型別下的文件應該有某種程度上的相似之處。相當於 mysql 表中的 row

什麼是 Field (欄位)?

Field 是相當於資料庫中的 Column

上述索引&型別&文件&欄位結構圖如下:

什麼是 Mapping(對映)?

Mapping 是定義文件及其包含的欄位如何儲存和索引的過程。 MappingES 中的一個很重要的內容,它類似於傳統關係型資料中 tableschema ,用於定義一個索引(index)的某個型別(type)的資料結構。

1.3.3 分片&副本

什麼是 Shard (分片)?

一個 索引 可以儲存超出單個結點硬體限制的大量資料。比如,一個具有 10億文件的索引佔據 1TB 的磁碟空間,而任一節點都沒有這樣大的磁碟空間;或者單個節點處理搜尋請求,響應太慢。

為了解決這個問題, Elasticsearch 提供了將索引劃分成多份的能力 ,這些份就叫做 分片 。當你建立一個索引的時候,你可以指定你想要的 分片的數量 。每個分片本身也是一個功能完善並且獨立的 索引 ,這個 索引 可以被放置到叢集中的任何節點上。

分片之所以重要,主要有兩方面的原因:

  1. 允許你水平分割/擴充套件你的內容容量

  2. 允許你在分片之上進行分散式的、並行的操作,進而提高效能/吞吐量

  3. 一個分片怎樣分佈,它的文件怎樣聚合回搜尋請求,是完全由 Elasticsearch 管理的,對於作為使用者的你來說,這些都是透明的

什麼是 Replica (副本)?

副本是一個分片的精確複製,每個分片可以有零個或多個副本。副本的作用:

  1. 提高系統的容錯性,當某個節點某個分片損壞或丟失時,可以從副本中恢復。

  2. 提高 ES 查詢效率,ES 會自動對搜尋請求進行負載均衡。

2 ElasticSearch 安裝部署

2.1 建立普通使用者

#1 建立普通使用者名稱,密碼
[root@hlink1 lyz]# useradd lyz
[root@hlink1 lyz]# passwd lyz

#2 然後 關閉xshell 重新登入 ip 地址 用 lyz 使用者登入

#3 為 lyz 使用者分配 sudoer 許可權
[lyz@hlink1 ~]$ su
[lyz@hlink1 ~]$ vi /etc/sudoers
# 在 root ALL=(ALL) ALL 下面新增普通使用者許可權
lyz ALL=(ALL) ALL

2.2 下載安裝 ES

# 4 下載安裝包
[lyz@hlink1 ~]$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.15.2-linux-x86_64.tar.gz
# 5 解壓安裝包
[lyz@hlink1 ~]$ tar -xzf elasticsearch-7.15.2-linux-x86_64.tar.gz

# 6 修改配置
[lyz@hlink1 ~]# cd elasticsearch-7.15.2/config
[lyz@hlink1 elasticsearch-7.15.2]# mkdir log
[lyz@hlink1 elasticsearch-7.15.2]# mkdir data
[lyz@hlink1 elasticsearch-7.15.2]# cd config
[lyz@hlink1 config]# rm -rf elasticsearch.yml
[lyz@hlink1 config]# vim elasticsearch.yml

# 貼上如下內容

# 配置叢集名稱,保證每個節點的名稱相同,如此就能都處於一個叢集之內了
cluster.name: lyz-es
# # 每一個節點的名稱,必須不一樣
node.name: hlink1
path.data: /home/lyz/elasticsearch-7.15.2/log
path.logs: /home/lyz/elasticsearch-7.15.2/data
network.host: 0.0.0.0
# # http埠(使用預設即可)
http.port: 9200
# # 叢集列表,你es叢集的ip地址列表
discovery.seed_hosts: ["hlink1"]
# # 啟動的時候使用一個master節點
cluster.initial_master_nodes: ["hlink1"]
bootstrap.system_call_filter: false
bootstrap.memory_lock: false
http.cors.enabled: true
http.cors.allow-origin: "*"


2.3 修改 jvm.option

修改 jvm.option 配置檔案,調整 jvm 堆記憶體大小,每個人根據自己伺服器的記憶體大小來進行調整

# 7 修改 jvm.option 配置檔案
[lyz@hlink1 config]# vim jvm.options
-Xms2g
-Xmx2g

2.4 修改系統配置,解決啟動問題

由於使用普通使用者來安裝 es 服務,且 es 服務對伺服器的資源要求比較多,包括記憶體大小,執行緒數等。所以我們需要給普通使用者解開資源的束縛

ES 因為需要大量的建立索引檔案,需要大量的開啟系統的檔案,所以我們需要解除linux 系統當中開啟檔案最大數目的限制,不然 ES 啟動就會報錯

進入 Root 使用者

# 8 進入 root 使用者
[lyz@hlink1 config]# su
Password:

# 9 在最下面新增如下內容: 注意*不要去掉了
[root@hlink1 config]# sudo vim /etc/security/limits.conf

* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096

2.5 普通使用者啟動執行緒數限制

修改普通使用者可以建立的最大執行緒數

10 若為 Centos7,執行下面的命令
[root@hlink1 config]# sudo vim /etc/security/limits.d/20-nproc.conf

# 找到如下內容:
* soft nproc 1024#修改為
* soft nproc 4096

2.6 普通使用者調大虛擬記憶體

# 11 調大系統的虛擬記憶體
[root@hlink1 config]# vim /etc/sysctl.conf

vm.max_map_count=262144

# 12 執行 sysctl -p
# 行完了 sysctl -p 若輸出的結果和你配置的一樣,說明配置成功了.
[root@hlink1 config]# sysctl -p
vm.max_map_count = 262144

2.7 啟動 ES 服務

# 13 切換使用者
[root@hlink1 config]# exit
exit
[lyz@hlink1 config]$

# 直接啟動 es 或者 後臺啟動 es
[lyz@hlink1 config]$ cd ..
[lyz@hlink1 elasticsearch-7.15.2]$ cd bin
# 直接啟動
[lyz@hlink1 bin]$ ./elasticsearch
# 後臺啟動 nohup ./elasticsearch 2>&1 &

# 瀏覽器訪問 http://hlink1:9200/?pretty

3 ElasticSearch 介面

3.1 RESTful

ElasticSearch 對外提供的 API 是以 HTTP 協議的方式,通過 JSON 格式以 REST 約定對外提供。

HTTP 配置檔案放在 elasticsearch.yml 中。REST 通常是開發的一種約定,當所有開發者都遵循這種約定時,就會簡化開發的溝通成本。

REST 約定用 HTTP 的請求頭 POST、GET、PUT、DELETE 正好對應 CRUD(Create、Read、Update、Delete)四種資料操作。如果應用程式符合 REST 原則,可稱為 RESTful Web Service。

3.2 Postman 軟體安裝

本文使用 Postman 作為 Elasticsearch 的客戶端進行連線使用

Postman 是一款強大的網頁除錯工具,提供功能強大的 Web API 和 HTTP 請求除錯。軟體功能強大,介面簡潔明晰、操作方便快捷,設計得很人性化。Postman 中文版能夠傳送任何型別的 HTTP 請求 (GET, HEAD, POST, PUT..),不僅能夠表單提交,且可以附帶任意型別請求體。

直接在官網進行下載安裝即可:

Postman 下載:https://www.getpostman.com/apps

安裝完之後進行簡單的註冊

4 ElasticSearch 操作命令

4.1 叢集資訊操作命令

4.1.1 查詢叢集狀態

(1)使用 Postman 客戶端直接向 ES 伺服器發 GET 請求

http://hlink1:9200/_cat/health?v

(2)使用服務端進行查詢

curl -XGET "hlink1:9200/_cat/health?v"

epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1638181746 10:29:06 lyz-es yellow 1 1 2 2 0 0 1 0 - 66.7%

返回結果的主要欄位意義:

  • cluster:叢集名,是在 ES 的配置檔案中配置的 cluster.name 的值。

  • status:叢集狀態。叢集共有 green、yellow 或 red 中的三種狀態。green 代表一切正常(叢集功能齊全),yellow 意味著所有的資料都是可用的,但是某些複製沒有被分配(叢集功能齊全),red 則代表因為某些原因,某些資料不可用。如果是 red 狀態,則要引起高度注意,資料很有可能已經丟失。

  • node.total:叢集中的節點數。

  • node.data:叢集中的資料節點數。

  • shards:叢集中總的分片數量。

  • pri:主分片數量,英文全稱為 private。

  • relo:複製分片總數。

  • unassign:未指定的分片數量,是應有分片數和現有的分片數的差值(包括主分片和複製分片)。

4.1.2 使用 help 引數查詢

(1)使用 Postman 客戶端查詢

http://hlink1:9200/_cat/health?help

在請求中新增 help 引數來檢視每個操作返回結果欄位的意義。

(2)使用 服務端 查詢

curl -XGET "hlink1:9200/_cat/health?help"
# 指定返回的引數
curl -XGET "hlink1:9200/_cat/health?h=cluster,pri,relo&v"

4.1.3 查詢叢集中的節點資訊

(1)使用 Postman 客戶端查詢

http://hlink1:9200/_cat/nodes?v

(2)使用服務端查詢

curl -XGET "hlink1:9200/_cat/nodes?v"

4.1.4 查詢叢集索引資訊

(1)使用 Postman 客戶端查詢

http://hlink1:9200/_cat/indices?v

(2)使用服務端查詢

curl -XGET "hlink1:9200/_cat/indices?v"

4.2 index 操作命令

後面操作命令全部使用 Postman 客戶端進行操作,想檢視詳細文件,在公眾號:【3分鐘秒懂大資料】,回覆:【elasticsearch】,獲取更詳細文件。

4.2.1 建立索引

在 Postman 中,向 ES 伺服器發 PUT 請求

http://hlink1:9200/study

{
"acknowledged"【響應結果】: true, # true 操作成功
"shards_acknowledged"【分片結果】: true, # 分片操作成功
"index"【索引名稱】: "study"
}

4.2.2 檢視索引

在 Postman 中,向 ES 伺服器發 GET 請求

http://hlink1:9200/study

{
"study": {
"aliases": {},
"mappings": {},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "study",
"creation_date": "1638183519598",
"number_of_replicas": "1", #分片數量
"uuid": "dg7HnAAiQEeDMJ7kMtA2Qw",
"version": {
"created": "7150299"
}
}
}
}
}

4.2.3 刪除索引

在 Postman 中,向 ES 伺服器發 DELETE 請求

http://hlink1:9200/study

4.3 document 操作命令

4.3.1 建立文件

索引建立完成後,可以建立 document 文件,該文件對應資料庫中 表的行資料 新增的資料格式為 JSON 內容

#請求體內容如下:
{
"name": "3分鐘秒懂大資料",
"introduction": "專注於大資料技術研究"
}
# 向 ES 伺服器發 POST 請求
http://hlink1:9200/study/_doc

4.3.2 更新文件

上面的資料建立後,沒有指定資料唯一性標識(ID),預設情況下,ES 伺服器會隨機生成一個。更新時可以指定唯一性標識

#請求體內容如下:
{
"name": "3分鐘秒懂大資料",
"introduction": "專注於Hadoop、Kafka、Flink等多個元件的技術研究"
}
# 向 ES 伺服器發 POST 請求
http://hlink1:9200/study/_doc/1

4.3.3 查詢文件

檢視文件時,需要指明文件的唯一性標識

http://hlink1:9200/study/_doc/1

4.3.4 刪除文件

刪除一個文件不會立即從磁碟上移除,它只是被標記成已刪除(邏輯刪除) Postman 向伺服器傳送 delete 請求

http://hlink1:9200/study/_doc/1

5 ElasticSearch 讀寫原理

5.1 寫操作(Write):針對文件的 CRUD 操作

索引新文件(Create)

當用戶向一個節點 提交 了一個 索引 新文件的 請求 ,節點會計算新文件應該加入到哪個分片( shard )中。每個節點都有每個分片儲存在哪個節點的資訊,因此 協調節點 會將請求傳送給對應的節點。注意這個請求會發送給 主分片 ,等主分片完成 索引 ,會 並行 將請求傳送到 其所有副本分片 ,保證每個分片都持有最新資料。

每次寫入新文件時,都會先寫入 記憶體 中,並將這一操作寫入一個 translog 檔案(transaction log)中,此時如果執行搜尋操作,這個新文件還不能被索引到。

ES會每隔 1 秒時間(這個時間可以修改)進行一次重新整理操作(refresh),此時在這 1 秒時間內寫入記憶體的新文件都會被寫入一個檔案系統快取( filesystem cache )中,並構成一個 分段(segment) 。此時這個 segment 裡的文件可以被搜尋到,但是 尚未寫入硬碟 ,即如果此時發生斷電, 則這些文件可能會丟失

不斷有新的文件寫入,則這一過程將不斷重複執行。每隔一秒將生成一個新的 segment,而 translog 檔案將越來越大。

每隔 30 分鐘或者 translog 檔案變得很大,則 執行一次 fsync 操作 。此時所有在檔案系統快取中的 segment 將被寫入磁碟,而 translog 將被刪除(此後會生成新的 translog)

由上面的流程可以看出,在 兩次 fsync 操作 之間,儲存在 記憶體檔案系統 快取中的文件是不安全的,一旦出現斷電這些文件就會丟失。 所以 ES 引入了 translog 來記錄兩次 fsync 之間所有的操作,這樣機器從故障中恢復或者重新啟動,ES 便可以根據 translog 進行還原

當然, translog 本身也是檔案 ,存在於記憶體當中,如果發生斷電一樣會丟失。因此, ES 會在每隔 5 秒 時間或是一次寫入請求完成後將 translog 寫入磁碟。可以認為一個對文件的操作一旦寫入磁碟便是安全的可以復原的,因此只有在當前操作記錄被寫入磁碟, ES 才會將操作成功的結果返回傳送此操作請求的客戶端。

此外,由於每一秒就會生成一個新的 segment ,很快將會有大量的 segment。對於一個分片進行查詢請求,將會輪流查詢分片中的所有 segment,這將降低搜尋效率。 因此 ES 會自動啟動合併 segment 的工作,將一部分相似大小的 segment合併成一個新的大 segment 。合併的過程實際上是建立了一個新的 segment,當新 segment 被寫入磁碟,所有被合併的舊 segment 被清除。

5.2 更新(Update)和刪除(Delete)文件

ES 的索引是不能修改的,因此 更新刪除 操作並不是直接在原索引上直接執行。

每一個磁碟上的 segment 都會維護一個 del 檔案,用來記錄被刪除的檔案。 每當使用者提出一個刪除請求,文件並沒有被真正刪除 ,索引也沒有發生改變, 而是在 del 檔案中標記該文件已被刪除 。因此被刪除的文件依然可以被檢索到,只是在返回檢索結果時被過濾掉了。 每次在啟動 segment 合併工作時,那些被標記為刪除的文件才會被真正刪除

更新文件會 首先 查詢 原文件 ,得到該文件的 版本號 。然後將修改後的文件寫入記憶體,此過程與寫入一個新文件相同。同時,舊版本文件被標記為刪除,同理,該文件可以被搜尋到,只是最終被過濾掉。

讀操作(Read):查詢過程

查詢的過程大體上分為 查詢(query)取回(fetch) 兩個階段。這個節點的任務是廣播查詢請求到所有相關分片,並將它們的響應整合成全域性排序後的結果集合,這個結果集合會返回給客戶端。

查詢階段

當一個節點接收到一個搜尋請求,則這個節點就變成了協調節點。

第一步是 廣播請求 到索引中每一個節點的 分片拷貝 。查詢請求可以被某個主分片或某個副本分片處理, 協調節點將在之後的請求中輪詢所有的分片拷貝來分攤負載

每個分片將會在本地構建一個 優先順序佇列 。如果客戶端要求返回結果排序中從第 from 名開始的數量為 size 的結果集,則每個節點都需要生成一個 from+size 大小的結果集,因此優先順序佇列的大小也是 from+size 。分片僅會返回一個輕量級的結果給協調節點,包含結果集中的每一個文件的 ID 和進行排序所需要的資訊。

協調節點會將所有分片的結果彙總,並進行全域性排序,得到最終的查詢排序結果。此時查詢階段結束。

取回階段

查詢過程得到的是一個排序結果,標記出哪些文件是符合搜尋要求的,此時仍然需要獲取這些文件返回客戶端。

協調節點會確定實際需要返回的文件,並向含有該文件的分片傳送 get 請求;分片獲取文件返回給協調節點; 協調節點 將結果返回給客戶端。