ES學習記錄2——ES核心概念和基本操作.md
2. Elasticsearch基本核心概念
Elasticsearch中具備一些基本的概念,做如下描述:
Near Realtime(NRT)
:ES是幾乎是一個實時查詢平臺,從索引文件到可搜尋文件有一點延遲,通常是1秒;Cluster
:叢集是一個或多個節點(伺服器)的集合,它們共同儲存整個資料,並提供跨所有節點的聯合索引和搜尋功能,一般一個叢集唯一的名字作識別符號(預設是"elatsicsearch"),這個名稱很重要,因為如果節點設定為按名稱加入群集,則該節點只能是群集的一部分,千萬不要在不同的環境中用相同的叢集名字,否則很容易導致節點加入錯誤的叢集;Node
:節點是組成叢集的一個單獨伺服器,用於儲存資料、提供叢集索引和搜尋功能,和叢集一樣,節點由名稱標識,預設情況下,該名稱是在啟動時分配給節點的隨機通用唯一識別符號(UUID),當然這個名字是可以自定義的,節點名稱對於管理目的非常重要,可以在其中識別網路中哪些伺服器與Elasticsearch叢集中的哪些節點相對應。可以通過叢集的名字將節點配置進去,預設情況下,每個節點都是被設定加入到一個名字為"elatsicsearch"的叢集,這就表示在網路上啟動一些節點(假設它們之間可以相互發現),那麼它們將會自動形成/加入到名字為elasticsearch
Index
:索引是具有某些類似特徵的文件集合,比如擁有客戶資料的索引,產品目錄的索引以及訂單資料的索引。索引由名稱標識(必須全部小寫),此名稱用於在對其中的文件執行索引、搜尋、更新和刪除操作時引用索引。在單個群集中,可以根據需要定義任意數量的索引。Document
:文件(在Elasticsearch中使用JSON的形式表示)是可以編制索引的基本資訊單元。例如,可以為單個客戶提供文件,為單個產品提供另一個文件,為單個訂單再提供另一個文件。在索引Index
document
。請注意,儘管文件實際上駐留在索引中,但實際上必須將文件編入索引/分配給索引中的型別。Shards
:索引Index
可能儲存了超過單個節點的硬體限制的資料。例如,佔用1TB磁碟空間的十億個文件的單個索引可能不適合單個節點的磁碟,或者可能太慢而無法單獨從單個節點提供搜尋請求。為了解決這樣的問題,Elasticsearch提供了將索引細分為多個稱為碎片(Shards
)的功能,建立索引的時候就可以自定義碎片shards
的數量,這些碎片shards
本身都是功能齊全、可以託管到叢集中任何節點的獨立索引(index
)。碎片的分佈方式和如何將其文件聚合回搜尋請求都是完全由Elasticsearch管理的並且這些都是對使用者透明的,分片(將索引分為一些碎片)最主要的原因有兩個:- 允許平行拆分/模組化內容容量;
- 允許跨分片(這些碎片可能在多個節點上)分佈和並行化操作,從而提高效能/吞吐量;
Replicas
:在網路/雲端環境中,故障可能隨時發生,為了避免某些碎片shards
或者節點node
由於某些原因離線或者消失,強烈推薦使用故障轉移機制。為此,Elasticsearch允許將索引的分片shards
的一個或多個副本製作成所謂的副本分片replica shards
或簡稱副本replicas
。副本replicas
重要的原因有兩個:- 在碎片或節點掛掉時可以提供高度可用性。注意,碎片副本從來都不會被分配在源碎片所在的節點上(要掛一起掛,我還要副本幹嘛( ఠൠఠ )ノ);
- 由於搜尋可以在所有副本上並行執行,所以它擴充套件了搜尋效能和吞吐量。
【總結】
每個索引index
可以分割為許多碎片shards
,當然這些索引可以有0個或多個副本,一旦索引有了副本,那就有了主碎片shards
和副本碎片replica shards
了。這些碎片shards
和副本replicas
的數量在索引被建立時就可以被定義出來,當然副本數量可以在索引建立後動態進行更改。雖然可以通過_shrink
和_split
介面對索引index
的碎片shards
數量進行更改,但是提前計劃好碎片shards
的數量是最好的方式,也是非常重要的。預設情況下,Elasticsearch中的每個索引index
都被分了5個主碎片shards
和1個副本replica
,那就表示如果群集中至少有兩個節點,則索引將包含5個主分片和另外5個副本分片(1個完整副本),每個索引總共有10個分片。
【注意】 每個Elasticsearch碎片shards
都是Lucene中的一個索引index
,對於一個Lucene索引index
所含的文件documents
數量通常都是有一個最大值,比如LUCENE-5843
,極限值是2,147,483,519 (= Integer.MAX_VALUE - 128)
,另外,可以通過_cat/shards
介面監控碎片shards
的體積。Type
屬性在Elasticsearch 6.x.x
已經被棄用,所以這裡不提了。
3. 基本操作
Elasticsearch提供強大支援Rest風格的API(順便熟悉下),可以完成下面的一些操作(僅是部分):
- 檢查叢集
cluster
、節點node
和索引index
的執行狀態和統計資訊; - 管理叢集、節點和索引資料以及元資料;
- 執行CRUD以及針對索引的搜尋操作;
- 執行高階搜尋操作,例如分頁,排序,過濾,指令碼編寫,聚合等等;
3.1 Cluster Health
健康檢查可以讓給我們看到叢集當前正在做的事情,正常可以通過curl
來幹這事兒,當然可以使用任意工具(只要能執行HTTP/REST
請求即可),命令為curl -X GET "localhost:9200/_cat/health?v"
。使用_cat
API介面進行健康檢查,比如:
C:\Users\liuwg-a\Desktop\curl-7.61.0_4-win64-mingw\curl-7.61.0-win64-mingw\bin>curl -X GET "localhost: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
1535783031 14:23:51 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0%
【注意】這裡記錄一個坑,win10的powershell自帶的curl應該是閹割版的,只能進行很簡單的訪問,沒法帶參,所以還是自己去下載一個win平臺下的curl,配置教程這裡就不寫了,注意curl
命令是對大小寫敏感的!
上面可以看到,“elasticsearch”叢集目前是綠色狀態,節點node
總共有1個,碎片shards
0個。注意,由於我們使用的是預設群集名稱elasticsearch
,並且由於Elasticsearch預設使用單播網路發現來查詢同一臺計算機上的其他節點,因此可能會意外啟動計算機上的多個節點並擁有它們所有都加入一個叢集。使用curl -X GET "localhost:9200/_cat/nodes?v"
可以檢視所有節點的詳細資訊,我這裡只有一個V8JnETy
節點資訊,本地結果為:
C:\Users\liuwg-a>curl -X GET http://localhost:9200/_cat/nodes?v
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
127.0.0.1 30 36 8 mdi * V8JnETy
關於health查詢時的顏色說明如下:
- Green:叢集
cluster
功能齊全,一切都很好; - Yellow:所有資料都可用,但尚未分配一些副本(群集功能齊全,但節點沒有分配到具體合法節點上);
- Red:某些資料由於某種原因不可用(群集只有部分功能);
【注意】當群集cluster
為紅色時,它將繼續提供來自可用分片的搜尋請求,但您可能需要儘快修復它,因為存在未分配的分片。
3.2 索引index
3.2.1 查詢所有索引indices
利用curl -X GET "localhost:9200/_cat/indices?v"
可以列出所有的索引(indexes和indices都是index的複數),本次操作結果:
C:\Users\liuwg-a>curl -X GET "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
這表示叢集中還沒索引。
3.2.2 建立索引
注:從這兒開始,發現win中使用curl還是很蛋疼,直接用kibana(埠號為5601)或者postman去作下面所有的測試。
由於Elasticsearch所有操作都是搞了個REST,所以不難想到建立索引肯定是用PUT
請求,下面是建立一個名為customer
值為pretty
的索引過程(返回一個JSON結果):
C:\Users\liuwg-a>curl -X PUT http://localhost:9200/customer?pretty
// 返回的結果為
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "customer"
}
然後驗證索引建立成功,檢視一下:
C:\Users\liuwg-a>curl -X GET localhost:9200/_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open customer 3M7rfq18SeCEy8qLI3UVyQ 5 1 0 0 1.1kb 1.1kb
這時就不像剛剛空索引時查詢到的返回值了,可以看到索引名為customer
,它有5個主碎片primary shards
和1個副本replica
,內部的文件數document
為0,最後健康狀態居然是yellow
(所有資料都可用,但尚未分配一些副本),這是因為Elasticsearch預設在建立索引時會幫我們搞出5個主碎片和1個副本,雖然搞了1個副本,但是這個副本並沒有分配到某個節點上(replica
和源index
不能在同一個節點上),但現在我只有一個V8JnETy
節點,只能等稍後另一個節點加入elasticsearch
群集才能將這個replica
分配過去,然後再看狀態就會變成green
。
3.2.3 刪除索引
同樣依據REST的尿性,刪除用Delete
,指令如下(刪除名字為customer
的索引):
curl -X DELETE "localhost:9200/customer?pretty"
返回結果為:
{
"acknowledged": true
}
再次查詢所有索引:
curl -X GET localhost:9200/_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
此時已經不存在索引了,說明刪除成功。
3.3 文件document
3.3.1 建立文件
前面說過,index
是由無數的文件document
組成,所以完整的索引還要加一些document
,指令如下(在customer
索引中建立id
為1的文件,customer
是索引index
,_doc
是型別type
,內容是個json):
curl -X PUT "localhost:9200/customer/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
"name": "John Doe"
}
'
返回的結果:
{
"_index": "customer",
"_type": "_doc",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
當然如果再次執行上面的語句,也指定id
為1,如下:
curl -X PUT "localhost:9200/customer/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
"name": "John Doe2"
}
'
那麼此時John Doe2
文件物件將會取代之前的John Doe
的物件,當然在建立時id
屬性是非必需的,如果不顯式指定id
,那麼elasticsearch將會幫我們指定一個隨機之前沒有用過id
:
curl -X POST "localhost:9200/customer/_doc?pretty" -H 'Content-Type: application/json' -d'
{
"name": "Jane Doe"
}
'
請注意,在上述情況下,我們使用POST動詞而不是PUT,因為我們沒有指定ID
值得注意的是,Elasticsearch在將文件編入索引之前不需要硬性先顯式建立索引。在前面的示例中,如果客戶索引事先尚未存在,則Elasticsearch將自動建立客戶索引。
3.3.2 查詢文件
查詢customer
索引中id
為1的文件(注意REST的寫法):
curl -X GET "localhost:9200/customer/_doc/1?pretty"
結果:
{
"_index": "customer",
"_type": "_doc",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "John Doe"
}
}
_source
就是document
文件中的所有內容。
3.3.3 更新文件屬性
當前的customer
索引下的id
為1的文件document
為:
curl -X GET http://localhost:9200/customer/_doc/1?pretty
{
"_index": "customer",
"_type": "_doc",
"_id": "1",
"_version": 6,
"found": true,
"_source": {
"name": "Jane Doe",
"age": 21
}
}
更新(將原來的age
改為22):
curl -X POST "localhost:9200/customer/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"doc": {
"age": 22
}
}
'
在更新時,必須像上面那樣用doc
包起來(雖然PUT時沒有包,但是elasticsearch預設是加上這個東西的),否則報400,不能將內容直接簡單的寫為:
{"age": 22}
再次查詢:
{
"_index": "customer",
"_type": "_doc",
"_id": "1",
"_version": 8,
"found": true,
"_source": {
"name": "John Doe",
"age": 22
}
}
此外,還可以用指令碼語言去更新,如:
curl -X POST "localhost:9200/customer/_doc/1/_update?pretty" -H 'Content-Type:application/json' -d'
{
"script": "ctx._source.age += 5"
}
這裡應該多少有點眼熟,ctx
可以理解為當前的上下文,_source
是document
的完整內容,然後通過.age
的形式獲取內部的age
屬性,然後執行增加5歲的操作,再次檢視結果:
{
"_index": "customer",
"_type": "_doc",
"_id": "1",
"_version": 5,
"found": true,
"_source": {
"name": "John Doe",
"age": 27
}
}
上述操作全部成功,但是都是單個操作,Elasticsearch也提供了通過一個條件更新多個document
檔案的能力,暫先擱置,詳情見docs-update-by-query API。
【注意】
如果使用指令碼方式更新類似於name
這樣的字串變數,必須加上單引號'
,否則不能更新,必須像下面這樣:
curl -X POST "localhost:9200/customer/_doc/1/_update?pretty" -H 'Content-Type:application/json' -d'
{
"script": "ctx._source.name = 'jack'"
}
3.3.4 刪除文件
刪除document
文件可能不去看教程現在已經可以大概猜出來,下面是刪除對應索引下id
為2的document
的案例:
curl -X DELETE http://localhost:9200/customer/_doc/2?pretty
當然也可以刪除指定條件的所有document
,詳見_delete_by_query API,但是如果想刪除某個索引下的所有文件,還不如直接將對應的索引刪除。
4. 批處理和資料匯入
上一章節基本都是圍繞單個的索引或者文件進行操作,在ES中也支援批處理主要是依靠_bulk
,它提供了一種非常有效的機制,可以儘可能快地執行多個操作,並儘可能少地進行網路往返。下面是索引兩個文件的命令:
curl -X POST "localhost:9200/customer/_doc/_bulk?pretty" -H 'Content-Type: application/json' -d'
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
'
更新id
為1的document,然後同時刪除id
為2 的document:
curl -X POST "localhost:9200/customer/_doc/_bulk?pretty" -H 'Content-Type: application/json' -d'
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}
'
【注意】上述包含了更新和刪除2個動作,統一使用POST
方法,動作直接在json中表述,更新動作後面接了更新的內容doc
,但刪除動作只需要一個_id
刪除即可,不需要doc
內容,返回結果為:
{
"took": 61,
"errors": false,
"items": [
{
"update": {
"_index": "customer",
"_type": "_doc",
"_id": "1",
"_version": 7,
"result": "noop",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"status": 200
}
},
{
"delete": {
"_index": "customer",
"_type": "_doc",
"_id": "2",
"_version": 3,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 7,
"_primary_term": 2,
"status": 200
}
}
]
}
_bulk
API在執行批量操作時不會因為某個方法執行失敗而失敗,而是繼續執行剩下的其他方法,在返回json時,它會提供批操作中每個方法的執行結果。
官方提供了一個簡單的銀行賬戶json資料集,可以下載儲存到某個資料夾,然後dos先進入到該檔案目錄下,夾下匯入下載的資料:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"
然後一直在匯入,完成後檢視是否匯入成功:
D:\ElasticSearch\elasticsearch-6.4.0\testdateset>curl "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open bank oFrvmRD5TyOjRIWeb-89jg 5 1 1000 0 475.1kb 475.1kb
green open .monitoring-kibana-6-2018.09.05 IjOR5IMKSO-8q6P8gqeqhg 1 0 336 0 118.5kb 118.5kb
green open .monitoring-es-6-2018.09.05 ao1DOXomTBmutbDI-juldw 1 0 2862 115 1.3mb 1.3mb
green open .kibana y-h6y-sMTASw_hx_Ve6WNA 1 0 1 0 4kb 4kb
yellow open customer lFQka4g1QmiRUlbeicYtIA 5 1 3 0 10.9kb 10.9kb
看到bank
索引下document
的數量為1000,說明匯入資料成功,這裡注意如果不進入json資料集的所在目錄進行匯入操作將出現400錯誤:
Warning: Couldn't read data from file "accounts.json", this makes an empty
Warning: POST.
{
"error" : {
"root_cause" : [
{
"type" : "parse_exception",
"reason" : "request body is required"
}
],
"type" : "parse_exception",
"reason" : "request body is required"
},
"status" : 400
}
【問題】
批量匯入json資料時,沒有顯式的制定索引名字,怎麼會自動建立bank
索引的?