Elasticsearh 是 elastic.co 公司開發的分散式搜尋引擎。
Elasticsearch(簡稱ES)是一個開源的分散式、高度可延伸的全文搜尋和分析引擎。它能夠快速、近乎實時的儲存、搜尋和分析大量資料。適用於包括文字、數位、地理空間、結構化和非結構化資料等在內的所有型別資料。
它通常為具有複雜搜尋功能的應用提供底層搜尋技術。
當然,它也可以用來實現分散式資料儲存、紀錄檔統計、分析、系統監控、地理空間查詢等功能。
Elasticsearch 最底層的搜尋引擎技術是 Apache 基金會開源的搜尋引擎類庫 Lucene,Lucene 提供了搜尋引擎核心 API 。
ES 在 Lucene 的基礎上提供了分散式支援,可以水平擴充套件,提供了 Restful 這種簡潔的存取介面,能被任何語言呼叫。
ELK 是 Elasticsearch、Logstash 和 Kibana 的第一個字母組合,也叫 ELK Stack。是一套用於資料採集、儲存、分析和視覺化的開源工具集。
Elasticsearch:儲存、索引、計算、搜尋、分析資料。
Logstash:用於收集、轉換資料,然後將它儲存在 ES 中。後面還開發新的收集資料軟體 Beats。
Beats:它是一個輕量級的資料採集代理工具,可以向 Elasticsearch 傳送資料。
Kibana:用於查詢分析、視覺化 ES 的資料,它還可以用於監控和報警的方案。它是 Elasticsearch 基於瀏覽器的分析和搜尋儀表盤。
它們之間關係圖:
(來自:Elasticsearch 簡介)
把上面的圖簡化下:
Elasticsearch 是面向檔案,它可以儲存整個物件或檔案。它不僅僅是儲存,還會索引每個檔案的內容使之可以被搜尋。在 ES 中,你可以對檔案進行索引、搜尋、排序、過濾。
在 ES 中,檔案是索引資訊的基本單位。
Elasticsearch 使用 json 格式作為檔案序列化格式。這種格式在 NoSQL 資料庫中使用比較多。
一個 json 物件是由 key 和 value 組成。key 是欄位(field)或屬性(property)的名字,值(value)可以是字串、數位、布林型別、另外一個物件、值陣列或其他特殊型別,比如表示日期的字串或表示地理位置的物件。
在關係型資料庫中,使用行和列儲存資料,比如儲存在 MySQL 表中的資料:
id | name |
---|---|
1 | 比亞迪電動車 |
2 | 理想電動車 |
3 | 小鵬電動車 |
4 | 比亞迪電池 |
5 | 理想電池 |
把上面的資料用 json 格式儲存在 elasticsearch 中:
{
"id": 1,
"name": "比亞迪電動車"
}
{
"id": 2,
"name": "理想電動車"
}
{
"id": 3,
"name": "小鵬電動車"
}
{
"id": 4,
"name": "比亞迪電池"
}
{
"id": 5,
"name": "理想電池"
}
上面 json 中的欄位 id 相當於 MySQL 資料表中列 id。
每個檔案就是一條json資料。一條 json 資料相當於 MySQL 表中的一行。
index 索引是具有相似特徵檔案的集合。一個索引通過名字(必須全部是小寫)來標識,並且在對其中的檔案執行索引、搜尋、更新和刪除操作時,都會用到這個索引的名字。
索引可以是一個名詞,相當於檔案儲存的地方。
索引也可以是一個動詞,索引一個檔案表示把一個檔案儲存到索引裡,以便它可以被檢索和查詢。
例如,你有一個使用者資料的索引,索引名稱叫 user,每一份使用者資訊就是一個檔案:
{
"id": 1,
"name": "tom",
"age": 25
},
{
"id": 2,
"name": "hanlei",
"age": 35
},
{
"id": 1,
"name": "tom",
"age": 25
},
{
"id": 3,
"name": "hanmeimei",
"age": 36
}
型別 type 這個概念在 elasticsearch 7.X 已被完全移除(參考檔案 Removal of mapping types)。這裡就不作介紹。
對映(mapping)是索引檔案中欄位的型別和欄位的其它資訊,都儲存在對映(mapping)中,它也叫模式定義(schema definition)。
相當於 MySQL 資料表的 schema,如定義表結構、欄位名稱、欄位型別等資訊。
而在 ES 中,對映可以設定某個欄位的資料型別、預設值、分析器、是否被索引等等,其它處理 ES 裡面的資料使用規則設定也叫對映。
mapping還有許多內容請檢視檔案:https://www.elastic.co/guide/en/elasticsearch/reference/8.4/mapping.html
mapping field doc:https://www.elastic.co/guide/en/elasticsearch/reference/8.4/mapping-fields.html
對映的設定:
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"message": {
"type": "text"
}
}
}
}
一個檔案不僅僅包含 json 資料,也包含後設資料 - 後設資料是有關檔案資訊的一些資料。
建立對映時,可以自定義其中一些後設資料欄位的行為。例如,建立一個檔案:
// 先建立一個對映mapping關係,相當於MySQL中表的schema,定義json檔案中欄位的屬性
PUT test
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"message": {
"type": "text"
}
}
}
}
給檔案寫入一條資料:
// 給test索引寫入一條json檔案資料
PUT test/_doc/1
{
"id": "12",
"message": "hello world"
}
上面 PUT test/_doc/1
命令會返回一條資訊:
{
"_index" : "test",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
更多後設資料資訊請檢視:https://www.elastic.co/guide/en/elasticsearch/reference/8.4/mapping-fields.html
一個節點node表示叢集中的一臺伺服器,它作為叢集的一部分儲存資料,並參與叢集的索引和搜尋功能。
節點由名稱標識,預設情況下是在啟動時分配給節點的一個隨機 UUID 唯一識別符號。如果不想要預設值,可以自定義節點名稱。
可以將節點通過叢集名稱加入特定叢集中。預設情況下,每個節點都加入一個名為 「elasticsearch」 的叢集中,這意味著如果
網路上啟動了多個節點,它們可以相互發現,那麼它們將自動形成一個名為 elasticsearch 的叢集。
在單個叢集中,你可以擁有任意數量的節點。
此外,如果網路上沒有其它節點在執行,則會啟動單個節點將預設形成一個名為 elasticsearch 的新節點叢集。
叢集(cluster)是由一個或多個節點node(伺服器)組成,它們一起儲存全部資料並提供跨所有節點的聯合索引和搜尋功能。叢集由唯一識別符號標識,預設為「elasticsearch」。這個名稱很重要,因為一個節點被設定為通過名稱加入叢集時,該節點才能成為叢集的一部分。
注意:擁有一個節點的叢集也是完全可以的。
此外,你也可以擁有多個獨立的叢集,每個叢集都擁有自己獨立的名稱。
索引可能會儲存大量的資料,而這些資料的容量可能會超過單個節點伺服器的硬體容量限制。比如,佔用 1TB 磁碟空間的 10 億檔案的單個索引可能無法儲存在單個節點的磁碟上,因為節點磁碟容量不足以容納下這麼大容量的資料,或者速度太慢無法滿足來自單個節點的搜尋速度請求。
Elasticsearch 可以將索引的資料進行分割,這些分割的部分稱為分片,每個分片可以分配到不同節點上。
相當於關係型資料中儲存資料太多,而進行分庫分表操作,把資料進行分散儲存。
在 Elasticsearch 中,當你建立索引時,你可以定義想要的分片數量。每個分片都是一個功能齊全、獨立的「索引」,可以在叢集的任意節點上託管。
- 它可以對資料進行水平拆分,擴充套件儲存資料的容量
- 提供效能、吞吐量,它允許跨分片(可以在多個節點上)分佈資料和並行化操作
這時就會用到資料副本replica功能。Elasticsearch 允許將索引分片構造複製成一個或多個副本,即所謂的複製分片,簡稱副本。
這樣就提供了 ES 的高可用性,為了高可用,ES 不允許副本分片和主分片(或原始分片)分配在同一節點上。
在 ES 中,索引 index 是由多個 json 格式的檔案 document 組成的。每個索引 index 又可以劃分為多個分片 Shard。
為了保證高可用,一個分片 shard,又可以分為主分片(primary shard)和副分片(replica shard),副分片是對主分片資料的備份,每個主分片可以有多個副分片,也就是說主分片可以有多個備份資料,
(每個索引index由多個documen組成)
(每個索引index可以劃分為多個分片shard,上圖劃分為shard 1,shard 2,shard 3)
叢集 cluster 和節點 Node,主分片 Primary 和副分片 Replica 的關係圖:
(上圖中虛線框裡同顏色表示同一份資料的不同分片,Primary-主分片,和此主分片的副本(Replica - 副分片))
對上面叢集圖 Cluster 說明:
(上圖:主分片和它所屬副分片,副分片是對主分片資料的備份)
Elasticsearch 與關係型資料庫的一個簡單類比:
Elasticsearch(ES搜尋引擎) | Relational DB(關係型資料庫) |
---|---|
Indices(多個索引) | Databases(資料庫) |
Index(單個索引) | Table(表) |
Document(檔案) | Row(行) |
Field(欄位) | Column(列) |
Elasticsearch叢集可以包含多個索引(indices)(資料庫),每一個索引包含多個檔案(documents)(行),然後每個檔案包含多個欄位(Fields)(列)。用於理解 ES 中的概念,作一個簡單的類比。
下面介紹 Elasticsearch 中最重要的資料結構之一 - 倒排索引。
索引,在生活中最常見的就是書籍的目錄,它就是一種類似索引結構,有時我們也叫索引目錄,它能讓人快速找到書籍相關章節的內容。
在計算機技術中,索引是一種常用的資料結構,目的就是加快查詢資料的速度。比如我們常用的 MySQL 資料庫,就有多種索引。
在搜尋引擎中,面對海量的資料,如何根據關鍵字詞快速找到使用者需要的相關內容?
這裡就要用到 倒排索引 這種資料結構,這是搜尋引擎中最重要的資料結構。
倒排索引中的一些概念:
我們平常使用 MySQL 關係型資料庫儲存資料,裡面有資料表。建立一個關於電動車的資料表:
id | name |
---|---|
1 | 比亞迪電動車 |
2 | 理想電動車 |
3 | 小鵬電動車 |
4 | 比亞迪電池 |
5 | 理想電池 |
怎麼把上面的表用倒排索引來表示呢?
詞條(term) | 檔案id(doc id) |
---|---|
比亞迪 | 1,4 |
電動車 | 1,2,3 |
理想 | 2,5 |
小鵬 | 3 |
電池 | 4,5 |
車 | 1,2,3 |
這張表就是倒排索引。
上面 MySQL 中的表,可以看作是正向索引表,然後把這張表資料倒過來,就變成倒排索引表。
MySQL 表變成倒排索引表的處理過程:
倒排索引表的詞條具有唯一性,然後可以給詞條建立索引加快查詢速度,比如雜湊表索引。
因為我的是windows,所以我下載win的安裝包,如果你是其它系統請下載相應平臺的。我這裡想下載 V8.4.3 版本,下載地址:
但是我電腦上安裝的是 JDK 1.8,不適合 8 以上的 ES 版本,見這裡說明,JDK 和 ES 的對應版本。
後面我換到了能使用jdk 1.8 的 ES V7.17.10 版本。
下載之後直接解壓,然後進入 bin 目錄,點選 elasticsearch.bat
啟動 ES,啟動會有一些時間,稍微等一下;
9300 是 tcp 通訊埠,ES 叢集之間使用 tcp 通訊;9200 是 http 協定埠。
在瀏覽器上輸入 http://localhost:9200/ 檢視,我這裡輸出以下資料,安裝成功了,
{
"name": "AIS",
"cluster_name": "elasticsearch",
"cluster_uuid": "bKg5AkWZScafo0vp03XOyA",
"version": {
"number": "7.17.10",
"build_flavor": "default",
"build_type": "zip",
"build_hash": "fecd68e3150eda0c307ab9a9d7557f5d5fd71349",
"build_date": "2023-04-23T05:33:18.138275597Z",
"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"
}
elasticsearch-head 外掛可以檢視 ES 的各種資料。
通過 git clone 下載 head 外掛:
git clone https://github.com/mobz/elasticsearch-head.git
cd ./elasticsearch-head
npm install
npm run start
瀏覽器上開啟:http://localhost:9100/
當然還有其它多種安裝方式。
第二種方式 chrome 外掛安裝:
還可以通過 chrome extension 執行外掛,Elasticsearch Head 在 Chrome store 的 下載地址。
下載 chrome 外掛後,安裝到 chrome 瀏覽器裡。
第三種方式 docker 安裝:
通過 docker 安裝,具體檢視:https://github.com/mobz/elasticsearch-head
設定跨域:
如果連線不上 ES,需要設定跨域存取,開啟組態檔 config/elasticsearch.yml,在最後增加下面設定項:
http.cors.enabled: true
http.cors.allow-origin: "*"
設定完成後,重新啟動 ES。
開啟 http://localhost:9100/,然後點選連線按鈕,出現下面 green 顏色表示連線成功,如下圖:
使用 curl,將請求從命令列提交到本地 Elasticsearch 範例,這些請求包含任何 HTTP 請求相同部分:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
命令引數說明:
命令引數 | 說明 |
---|---|
<VERB> | HTTP 方法,例如,GET,POST,PUT,HEAD 或 DELETE |
<PROTOCOL> | http 或 https,如果你在 ES 前面有一個 https 代理 |
<HOST> | Elasticsearch 叢集中任何節點的主機名。 或用 localhost 來代表本地機器上的節點 |
<PORT> | 執行 Elasticsearch HTTP 服務的埠號,預設為 9200 |
<PATH> | API 的終端路徑,可以包含多個引數,例如,_cluster/stats |
<QUERY_STRING> | 任何可選的查詢字串引數。 |
<BODY> | JSON 編碼格式的請求正文,如果有需要 |
如果 elasticsearch 啟動了安全功能,則必須提供有許可權執行 API 的有效使用者名稱和密碼:
curl -u elastic:password -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
// elastic 使用者名稱
// password 密碼
我是win這裡用 chocolatey 安裝:
choro install curl
安裝完成後直接 cd 到它的安裝目錄 C:\ProgramData\chocolatey\bin 目錄下,然後執行檢視 curl 版本命令,安裝成功:
不知道安裝到哪裡了?可以使用
where curl
命令來查詢安裝位置
查詢 ES 的 http 伺服器埠 9200,命令:curl.exe -XGET 'http://localhost:9200' -H 'Content-Type: application/json'
這裡還可以使用 Go 語言實現的 curl 工具 curlie 來操作 ES。
go install github.com/rs/[email protected]
用 curlie 在 terminal 上存取 HTTP 埠 9200,我是 win 使用 PowerShell,命令如下:
curlie -XGET 'http://localhost:9200' -H 'Content-Type: application/json'
返回結果:
如果 ES 設定了使用者和密碼,可以用如下命令:
$ curlie -XGET -u "elastic:pwdes" 'http://localhost:9200/' -H 'Content-Type: application/json'
說明:如果執行 curlie 返回安全錯誤資訊,那麼找到ES安裝位置,然後在 config/elasticsearch.yml 檔案最後面加上
xpack.security.enabled: false
,把安全驗證設定為 false。
前面說了,索引 index 可以是名詞儲存檔案的地方,也可以是動詞建立索引的意思。
建立索引基本語法:
PUT /{索引名稱}
建立索引和檔案基本語法:
PUT /{索引名稱}/_doc/檔案id
// 也可以把上面 PUT 換成 POST
查詢索引資訊:
GET /{索引名稱}
例如,建立一個賣書的書店bookmall索引,然後給索引增加一些資料,命令如下:
curl -XPUT "http://localhost:9200/bookmall/_doc/1?pretty" -H "Content-Type: application/json" -d '{"product_id": 123456, "quantity": 100}'
我的是windows,在cmd下執行後出錯,出錯資訊如下:
{
"error" : {
"root_cause" : [
{
"type" : "mapper_parsing_exception",
"reason" : "failed to parse"
}
],
"type" : "mapper_parsing_exception",
"reason" : "failed to parse",
"caused_by" : {
"type" : "json_parse_exception",
"reason" : "Unexpected character ('p' (code 112)): was expecting double-quote to start field name\n at [Source: (ByteArrayInputStream); line: 1, column: 3]"
}
},
"status" : 400
}
需要把上面的命令修改下,雙引號前加上斜線,
curl -XPUT 'http://localhost:9200/bookmall/_doc/1?pretty' -H 'Content-Type: application/json' -d '{\"product_id\": 123456, \"quantity\": 100}'
在執行,成功了,返回資訊:
{
"_index" : "bookmall",
"_type" : "_doc",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
在 linux 下就不需要加這條斜線,所以學習建議在 linux 平臺下。 - -!
上面的命令
curl -XPUT
也可以換成curl -XPOST
例如,部落格巴士的部落格文章,我們可以用 ES 來索引這些部落格文章資訊。
下面我使用 Postman 這款測試 API 的軟體來增加索引,開啟 Postman 軟體(如沒安裝請先安裝),首先新建一個請求的 tab,
然後在 Headers 里加上 Content-Type: application/json
,如下:
然後請求方法選擇 PUT
, url 欄裡填上 http://localhost:9200/blogerbus/_doc/1?pretty
,然後點選 body,選擇 raw 選項,格式選擇 JSON , 填上 json 格式的資料,最後點選 Send 按鈕傳送資料,如下圖:
Status:201 Created ,成功返回資料:
{
"_index": "blogerbus",
"_type": "_doc",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
對這條 URL
http://localhost:9200/blogerbus/_doc/1?pretty
的說明:
http://localhost:9200:ES HTTP 本地伺服器端地址:埠號
blogerbus:索引名稱
_doc:檔案終端endpoint,ES 裡的一個固定欄位
1:檔案 id
?pretty:將返回的json格式化資料,顯示為更易於讓人閱讀的形式
基本語法:
// 根據單個id查詢
GET /{索引名稱}/_doc/檔案id
//批次查詢:查詢該索引庫下的全部檔案
GET /{索引名稱}/_search
// 查詢某個索引詳細資訊
GET /{索引名稱}
// 查詢所有索引部分資訊
GET /_cat/indices
用 Postman 來查詢索引檔案,在url欄輸入 http://localhost:9200/blogerbus/_doc/1?pretty=true
,點選 Send,返回:
返回內容:
{
"_index": "blogerbus",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source": {
"user": "lilei",
"create_at": "2021-09-15T08:12:43",
"title": "hello world",
"article": "hello world, this is good thing"
}
}
// 查詢單個索引詳細資訊
curl -XGET 'http://localhost:9200/bookmall?pretty' -H 'Content-Type:application/json'
// 查詢 ES 中的所有索引部分資訊
curl -XGET 'http://localhost:9200/_cat/indices?pretty' -H 'Content-Type:application/json'
基本語法:
GET /{索引名稱}/_search // 後面可以跟一些查詢字串,也可以跟json的DSL
給索引 blogerbus 多增加幾個檔案,用於我們的搜尋:
// 第2篇檔案,http://localhost:9200/blogerbus/_doc/2?pretty
{
"user": "lilei",
"create_at": "2021-09-18T09:12:04",
"title": "math lesson",
"article": "hello math, this my first lesson"
}
// 第3篇檔案,http://localhost:9200/blogerbus/_doc/3?pretty
{
"user": "hanmeimei",
"create_at": "2021-10-10T03:24:34",
"title": "test lesson",
"article": "hello lesson, this my test lesson"
}
搜尋 user 為 lilei 的所有文章,在 Postman 的url欄輸入:http://localhost:9200/blogerbus/_search?q=user:lilei&pretty=true
,點選 Send 按鈕,返回值:
上面是直接在 url 上用字串查詢,還可以用 json 格式來查詢:
{
"query" : {
"match" : { "user": "lilei" }
}
}
url 修改為 http://localhost:9200/blogerbus/_search?pretty=true
,
返回的資料與上面相同。
curl -XGET 'http://localhost:9200/blogerbus/_search?pretty=true' -H 'Content-Type: application/json' -d '{\"query\": {\"match\":{\"user\": \"lilei\"}}}'
返回的內容與 Postman 搜尋返回內容相同
刪除檔案基本語法:
DELETE /{索引名稱}/_doc/檔案id
例如,curl 刪除一篇 id 為 2 的檔案:
curl -XDELETE 'http://localhost:9200/bookmall/_doc/2'
返回:
{"_index":"bookmall","_type":"_doc","_id":"2","_version":3,"result":"deleted","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":3,"_primary_term":1}
後面沒有加?pretty=true
,所以返回資料排版是不易讀的json。
刪除索引基本語法:
DELETE /{索引名稱}
修改有2種方式:全量修改和增量修改
基本語法:
PUT /{索引名稱}/_doc/檔案id
{
"欄位1": "值1",
"欄位2": "值2",
// ... 略
}
基本語法:
POST /{索引名稱}/_update/檔案id
{
"doc": {
"欄位名": "新值",
}
}
curl 全量修改:
把上面的 bookmall/_doc/1 中 2 個欄位值都修改下:
curl -XPUT "http://localhost:9200/bookmall/_doc/1?pretty" -H "Content-Type: application/json" -d '{\"product_id\": 1234567, \"quantity\": 1000}'
修改成功後返回資料:
{
"_index" : "bookmall",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 4,
"_primary_term" : 1
}
那能不能修改值的同時新增一個欄位?可以的。例如,給檔案 id 為 1 的新增一個欄位 num:50 ,修改 quantity 為 2000,如下:
curl -XPUT "http://localhost:9200/bookmall/_doc/1?pretty" -H "Content-Type: application/json" -d '{\"product_id\": 123456, \"quantity\": 2000,\"name\":\"shiije\",\"num\":50}'
可以修改成功。
curl 部分修改:
修改檔案 id 為 1 中的欄位 product_id 為 123,
curl -XPOST "http://localhost:9200/bookmall/_update/1?pretty" -H "Content-Type: application/json" -d '{\"doc\":{\"product_id\": 123}}'
可以修改成功。