本文來自 《Elasticsearch: 權威指南》
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
ElasticSearch安裝與基本使用
安裝
去下載版本https://www.elastic.co/downloads/elasticsearch
解壓之後
cd elasticsearch-<version>
./bin/elasticsearch
curl 'http://localhost:9200/?pretty' 檢查是否成功
-d參數即可讓其運行於後臺進程
單個 節點 可以作爲一個運行中的 Elasticsearch 的實例。 而一個 集羣 是一組擁有相同 cluster.name 的節點, 他們能一起工作並共享數據,還提供容錯與可伸縮性。(當然,一個單獨的節點也可以組成一個集羣) 你可以在 elasticsearch.yml 配置文件中 修改 cluster.name ,該文件會在節點啓動時加載 (譯者注:這個重啓服務後纔會生效)
客戶端
https://www.elastic.co/guide/en/elasticsearch/client/index.html
Java客戶端
節點客戶端(Node client)
節點客戶端作爲一個非數據節點加入到本地集羣中。換句話說,它本身不保存任何數據,但是它知道數據在集羣中的哪個節點中,並且可以把請求轉發到正確的節點。
傳輸客戶端(Transport client)
輕量級的傳輸客戶端可以將請求發送到遠程集羣。它本身不加入集羣,但是它可以將請求轉發到集羣中的一個節點上。
兩個 Java 客戶端都是通過 9300 端口並使用 Elasticsearch 的原生 傳輸 協議和集羣交互。集羣中的節點通過端口 9300 彼此通信。如果這個端口沒有打開,節點將無法形成一個集羣。
RESTful API with JSON over HTTP
所有其他語言可以使用 RESTful API 通過端口 9200 和 Elasticsearch 進行通信,你可以用你最喜愛的 web 客戶端訪問 Elasticsearch 。事實上,正如你所看到的,你甚至可以使用 curl 命令來和 Elasticsearch 交互。
獲取文檔數量
curl -XGET 'http://localhost:9200/_count?pretty' -d '{
"query": {
"match_all": {}
}}'
面向文檔
使用關係型數據庫的行和列存儲,這相當於是把一個表現力豐富的對象塞到一個非常大的電子表格中:爲了適應表結構,你必須設法將這個對象扁平化--通常一個字段對應一列--而且每次查詢時又需要將其重新構造爲對象。
Elasticsearch 是 面向文檔 的,意味着它存儲整個對象或 文檔。並且基於JSON。Elasticsearch 不僅存儲文檔,而且 _索引 每個文檔的內容,使之可以被檢索。在 Elasticsearch 中,我們對文檔進行索引、檢索、排序和過濾--而不是對行列數據。這是一種完全不同的思考數據的方式,也是 Elasticsearch 能支持複雜全文檢索的原因。
一個 Elasticsearch 集羣可以 包含多個 索引(index) ,相應的每個索引可以包含多個 類型(type),這些不同的類型存儲着多個 文檔(document、實體) ,每個文檔又有 多個 屬性(field) 。
一個 索引 類似於傳統關係數據庫中的一個 數據庫 ,是一個存儲關係型文檔的地方。 索引 (index) 的複數詞爲 indices 或 indexes 。索引一個文檔 就是存儲一個文檔到一個 索引 (名詞)中以便被檢索和查詢。這非常類似於 SQL 語句中的 INSERT 關鍵詞,除了文檔已存在時,新文檔會替換舊文檔情況之外。關係型數據庫通過增加一個 索引 比如一個 B樹(B-tree)索引 到指定的列上,以便提升數據檢索速度。Elasticsearch 和 Lucene 使用了一個叫做 倒排索引 的結構來達到相同的目的。
PUT /megacorp(索引)/employee(類型)/1(文檔id){
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]}
GET /megacorp/employee/1
返回
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}}
將 HTTP 命令由 PUT 改爲 GET 可以用來檢索文檔,同樣的,可以使用 DELETE 命令來刪除文檔,以及使用 HEAD 指令來檢查文檔是否存在。如果想更新已存在的文檔,只需再次 PUT 。
搜索所有
GET /megacorp/employee/_search 搜索所有
簡單查詢字符串
GET /megacorp/employee/_search?q=last_name:Smith
DSL方式:
名字搜索
GET /megacorp/employee/_search{
"query" : {
"match" : {
"last_name" : "Smith"
}
}}
名字+age範圍
GET /megacorp/employee/_search{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}}
about屬性上搜索
GET /megacorp/employee/_search{
"query" : {
"match" : {
"about" : "rock climbing"
}
}}
會對搜索到的文檔進行打分,默認按照相關性得分排序,即每個文檔跟查詢的匹配程度。
短語搜索
GET /megacorp/employee/_search{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}}
搜索高亮,結果中還多了一個叫做 highlight 的部分
GET /megacorp/employee/_search{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}}
興趣愛好的統計
GET /megacorp/employee/_search{
"aggs": {
"all_interests": {
"terms": { "field": "interests" }
}
}}
某個員工的興趣愛好統計
GET /megacorp/employee/_search{
"query": {
"match": {
"last_name": "smith"
}
},
"aggs": {
"all_interests": {
"terms": {
"field": "interests"
}
}
}}
特定興趣愛好的員工的平均年齡
GET /megacorp/employee/_search{
"aggs" : {
"all_interests" : {
"terms" : { "field" : "interests" },
"aggs" : {
"avg_age" : {
"avg" : { "field" : "age" }
}
}
}
}}
ElasticSearch集羣原理
ElasticSearch屏蔽了分佈式系統的複雜性,自動做好了以下工作:
- 分配文檔到不同的容器 或 分片 中,文檔可以儲存在一個或多個節點中
- 按集羣節點來均衡分配這些分片,從而對索引和搜索過程進行負載均衡
- 複製每個分片以支持數據冗餘,從而防止硬件故障導致的數據丟失
- 將集羣中任一節點的請求路由到存有相關數據的節點
- 集羣擴容時無縫整合新節點,重新分配分片以便從離羣節點恢復
ElasticSearch 的主旨是隨時可用和按需擴容。 而擴容可以通過購買性能更強大( 垂直擴容 ,或 縱向擴容 ) 或者數量更多的服務器( 水平擴容 ,或 橫向擴容 )來實現。
雖然 Elasticsearch 可以獲益於更強大的硬件設備,但是垂直擴容是有極限的。 真正的擴容能力是來自於水平擴容--爲集羣添加更多的節點,並且將負載壓力和穩定性分散到這些節點中。
一個運行中的 Elasticsearch 實例稱爲一個 節點,而集羣是由一個或者多個擁有相同 cluster.name 配置的節點組成, 它們共同承擔數據和負載的壓力。當有節點加入集羣中或者從集羣中移除節點時,集羣將會重新平均分佈所有的數據。
Elasticsearch是去中心化的,當一個節點被選舉成爲 主 節點時, 它將負責管理集羣範圍內的所有變更,例如增加、刪除索引,或者增加、刪除節點等。 而主節點並不需要涉及到文檔級別的變更和搜索等操作,所以當集羣只擁有一個主節點的情況下,即使流量的增加它也不會成爲瓶頸。 任何節點都可以成爲主節點。
作爲用戶,我們可以將請求發送到 集羣中的任何節點 ,包括主節點。 每個節點都知道任意文檔所處的位置,並且能夠將我們的請求直接轉發到存儲我們所需文檔的節點。 無論我們將請求發送到哪個節點,它都能負責從各個包含我們所需文檔的節點收集回數據,並將最終結果返回給客戶端。 Elasticsearch 對這一切的管理都是透明的。
獲取集羣健康
GET /_cluster/health
GET _cluster/health?level=indices
GET _cluster/health?level=shards
返回字段中的status:green、yellow、red代表越來越不健康
green
所有的主分片和副本分片都正常運行。
yellow
所有的主分片都正常運行,但不是所有的副本分片都正常運行。
red
有主分片沒能正常運行。
Elasticsearch 是利用分片將數據分發到集羣內各處的。分片是數據的容器,文檔保存在分片內,分片又被分配到集羣內的各個節點裏。 當你的集羣規模擴大或者縮小時, Elasticsearch 會自動的在各節點中遷移分片,使得數據仍然均勻分佈在集羣裏。一個分片可以是 主 分片或者 副本 分片。 索引內任意一個文檔都歸屬於一個主分片,所以主分片的數目決定着索引能夠保存的最大數據量。一個主分片最大能夠存儲 Integer.MAX_VALUE - 128 個文檔,但是實際最大值還需要參考你的使用場景:包括你使用的硬件, 文檔的大小和複雜程度,索引和查詢文檔的方式以及你期望的響應時長。一個副本分片只是一個主分片的拷貝。 副本分片作爲硬件故障時保護數據不丟失的冗餘備份,併爲搜索和返回文檔等讀操作提供服務。在索引建立的時候就已經確定了主分片數,並且無法修改,但是副本分片數可以隨時修改。
新建一個索引blogs,主分片3個,每個1個副本分片。
PUT /blogs{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}}
只要cluster.name相同,那麼新增一個節點即可自動加入集羣中,最好配置單播模式而非組播,後續詳說。
分片是一個功能完整的搜索引擎,它擁有使用一個節點上的所有資源的能力。 我們這個擁有6個分片(3個主分片和3個副本分片)的索引可以最大擴容到6個節點,每個節點上存在一個分片,並且每個分片擁有所在節點的全部資源。所以我們在規劃數據主機的時候要預估某個索引的可能最大的數據量,以決定分片數量。
副本可以隨時按需伸縮。在相同節點增加副本量並不能提高性能,因爲每個分片獲取的資源變少了,需要增加更多的硬件資源(說白了就是主機)來提升吞吐量,但是更多的副本提高了數據冗餘。
PUT /blogs/_settings{
"number_of_replicas" : 2}
故障轉移
我們關閉的節點是一個主節點。而集羣必須擁有一個主節點來保證正常工作,所以發生的第一件事情就是選舉一個新的主節點: Node 2 。
在我們關閉 Node 1 的同時也失去了主分片 1 和 2 ,並且在缺失主分片的時候索引也不能正常工作。 如果此時來檢查集羣的狀況,我們看到的狀態將會爲 red :不是所有主分片都在正常工作。
幸運的是,在其它節點上存在着這兩個主分片的完整副本, 所以新的主節點立即將這些分片在 Node 2 和 Node 3 上對應的副本分片提升爲主分片, 此時集羣的狀態將會爲 yellow 。 這個提升主分片的過程是瞬間發生的,如同按下一個開關一般。
爲什麼我們集羣狀態是 yellow 而不是 green 呢? 雖然我們擁有所有的三個主分片,但是同時設置了每個主分片需要對應2份副本分片,而此時只存在一份副本分片。 所以集羣不能爲 green 的狀態,不過我們不必過於擔心:如果我們同樣關閉了 Node 2 ,我們的程序 依然 可以保持在不丟任何數據的情況下運行,因爲 Node 3 爲每一個分片都保留着一份副本。
如果我們重新啓動 Node 1 ,集羣可以將缺失的副本分片再次進行分配,那麼集羣的狀態也將如圖 5 “將參數 number_of_replicas 調大到 2”所示。 如果 Node 1 依然擁有着之前的分片,它將嘗試去重用它們,同時僅從主分片複製發生了修改的數據文件。
文檔就是一個被ES實時序列化的JSON串,並對所有字段進行了索引。
文檔包含三個元數據:
_index 文檔在哪存放
_type 文檔表示的對象類別
_id 文檔唯一標識,可以自己指定,讓ES自動生成【20位的URLSafe、Base64】
返回文檔的一部分
GET /website/blog/123?_source=title,text
不需要返回元數據
GET /website/blog/123/_source
刪除文檔
DELETE /website/blog/123
ES使用樂觀鎖(_version版本號)來控制併發修改。文檔也是不可更改的,對於API提供的更新操作實際上操作的是不同的版本的數據。
路由一個文檔到一個分片中
當索引一個文檔的時候,文檔會被存儲到一個主分片中。 Elasticsearch 如何知道一個文檔應該存放到哪個分片中呢?當我們創建文檔時,它如何決定這個文檔應當被存儲在分片 1 還是分片 2 中呢?
首先這肯定不會是隨機的,否則將來要獲取文檔的時候我們就不知道從何處尋找了。實際上,這個過程是根據下面這個公式決定的:
shard = hash(routing) % number_of_primary_shards
routing 是一個可變值,默認是文檔的 _id ,也可以設置成一個自定義的值。 routing 通過 hash 函數生成一個數字,然後這個數字再除以 number_of_primary_shards (主分片的數量)後得到 餘數 。這個分佈在 0 到 number_of_primary_shards-1 之間的餘數,就是我們所尋求的文檔所在分片的位置。
這就解釋了爲什麼我們要在創建索引的時候就確定好主分片的數量 並且永遠不會改變這個數量:因爲如果數量變化了,那麼所有之前路由的值都會無效,文檔也再也找不到了。
主分片和副本分片如何交互
我們可以發送請求到集羣中的任一節點。 每個節點都有能力處理任意請求。 每個節點都知道集羣中任一文檔位置,所以可以直接將請求轉發到需要的節點上。
寫或者刪除
- 客戶端向 Node 1 發送新建、索引或者刪除請求。
- 節點使用文檔的 _id 確定文檔屬於分片 0 。請求會被轉發到 Node 3`,因爲分片 0 的主分片目前被分配在 `Node 3 上。
- Node 3 在主分片上面執行請求。如果成功了,它將請求並行轉發到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都報告成功, Node 3 將向協調節點報告成功,協調節點向客戶端報告成功。
在客戶端收到成功響應時,文檔變更已經在主分片和所有副本分片執行完成,變更是安全的。
讀
- 客戶端向 Node 1 發送獲取請求。
- 節點使用文檔的 _id 來確定文檔屬於分片 0 。分片 0 的副本分片存在於所有的三個節點上。 在這種情況下,它將請求轉發到 Node 2 。
- Node 2 將文檔返回給 Node 1 ,然後將文檔返回給客戶端。
在處理讀取請求時,協調結點在每次請求的時候都會通過輪詢所有的副本分片來達到負載均衡。
在文檔被檢索時,已經被索引的文檔可能已經存在於主分片上但是還沒有複製到副本分片。 在這種情況下,副本分片可能會報告文檔不存在,但是主分片可能成功返回文檔。 一旦索引請求成功返回給用戶,文檔在主分片和副本分片都是可用的。
一些重要的配置項
elasticsearch.yaml
cluster.name: elasticsearch_production
node.name: elasticsearch_005_data
path.data: /path/to/data1,/path/to/data2
path.logs: /path/to/logs
path.plugins: /path/to/plugins
discovery.zen.minimum_master_nodes: 2 最小節點數
gateway.recover_after_nodes: 8
gateway.expected_nodes: 10
gateway.recover_after_time: 5m
discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]