相關文檔:
Gitbook[中文未完整]: http://learnes.net/
Gitbook[英文完整]:https://allen8807.gitbooks.io/elasticsearch-definitive-guide-en/content/
權威指南: http://es.xiaoleilu.com/
官網: https://www.elastic.co/guide/index.html
官網API:https://www.elastic.co/guide/en/elasticsearch/reference/1.7/index.html
一、ES介紹
1、什么是es?
Elasticsearch 是一個建立在全文搜索引擎 Apache Lucene(TM) 基礎上的搜索引擎,可以說 Lucene 是當今最先進,最高效的全功能開源搜索引擎框架。但是 Lucene 只是一個框架,要充分利用它的功能,你需要使用 JAVA,並且在你的程序中集成 Lucene。更糟的是,你需要做很多的學習了解,才能明白它是如何運行的,Lucene 確實非常復雜。
當然 Elasticsearch 並不僅僅是 Lucene 那么簡單,它不僅包括了全文搜索功能,還可以進行以下工作:
- 分布式實時存儲,並將每一個字段都編入索引,使其可以被搜索(准實時,有輕微延遲,1s左右)。
- 實時分析的分布式搜索引擎。
- 可以擴展到上百台服務器,處理PB級別的結構化或非結構化數據。
2、es特性:
- 支持Restful,大量的Restful api
- json風格
- 分布式索引和搜索
- 天然支持shard,replication
- 高性能,易擴展,使用簡單
3、ES不僅僅是搜索?
- es是否可以作為一個NOSQL,看上去並不可以,但是這是一個合理的場景。就像MongoDB在MapReduce的基礎上使用分片技術同樣可以完成部分Hadoop可以做的工作,也就是說一切都和具體場景有關。
- es已經超越了最初的設定角色:一個純搜索引擎。但這依舊是它的核心,如果你有數百萬的文檔需要通過關鍵詞進行定位時,Elasticsearch就是最好的選擇。如果你僅僅把它當做一個JSON文檔,那它就是一個NOSQL數據庫,同時支持輕度的數據分析功能。
- 而且,es可以很輕松和hadoop集群。
二、ES中的基礎概念
https://www.elastic.co/guide/en/elasticsearch/reference/current/_basic_concepts.html
2.1 Document:文檔
- 內容為json格式,它指的是在Elasticsearch中被存儲到唯一ID下的由最高級或者根對象 (root object )序列化而來的JSON.
- 由_index+_type+_id共同決定一個唯一的文檔
- _type是一個邏輯概念,並不是物理概念
下面是對三者的詳細解釋:
(1) _index
類似於傳統數據庫中的"庫"-存儲並且索引數據的地方
在ES中,我們的數據都在分片中被存儲以及索引,索引是一個邏輯命名空間,它可以將一個或者多個分片組合在一起。
(2) _type
在ES中我們使用同樣_type的文檔來代表同類,因為它們的數據結構是相同的
每一個類型都有自己的mapping或者結構定義,它們定義了當前類型的數據結構,類似於數據表中的列。
Documents of all types can be stored in the same index, but the mapping for type tell ES how the data in each document should be indexed.
(3) _id
id是一個字符串,當它與_index
以及_type
組合時,就可以來代表Elasticsearch中一個特定的文檔。我們創建了一個新的文檔時,你可以自己提供一個_id
,或者也可以讓Elasticsearch幫你生成一個
2.2 Index:索引
文檔通過索引
API被索引——存儲並使其可搜索。但是最開始我們需要決定我們將文檔存儲在哪里。正如之前提到的,一篇文檔通過_index
, _type
以及_id
來確定它的唯一性。
如果我們的索引叫做"website"
,我們的類型叫做 "blog"
,然后我們選擇"123"
作為ID的編號。這時,請求就是這樣的:
PUT /website/blog/123
{
"title": "My first blog entry",
"text": "Just trying this out...",
"date": "2014/01/01"
}
2.3 分片、高可用、集群、節點
- ES采用的是服務端分片技術,對客戶端透明且容易擴展。
- 多個node(es實例)通過cluster(集群名字)自動加入一個集群,每個cluseter有一個master node。
- master node管理集群的變更,例如新建或者刪除索引,增加或者移除節點等。master node不會參與文檔級別的變更或者搜索,這意味着在流量增長的時候,該主節點不會成為集群的瓶頸。
- 分片(shard) 是 工作單元(worker unit) 底層的一員,它只負責保存索引中所有數據的一小片。在 elasticsearch 中,分片用來分配集群中的數據。把分片想象成一個數據的容器。
- 數據被存儲在分片中,然后分片又被分配在集群的節點上。
- 當你的集群擴展或者縮小時,elasticsearch 會自動的在節點之間遷移分配分片,以便集群保持均衡。
- es通過在分片級別實現主從機制來保證高可用,同時服務於搜索和檢索這種只讀請求。
- 主分片primary shard就是數據的切片,由於是數據存儲而不是緩存,primary shard的數目才創建索引時指定且不可更改
- 從分片就是數據的副本,可以隨時更改數目。
下面是使用head插件截圖,有一個索引bank,下面有0,1,2,3,4共5個主分片,每個分片有1個從分片。
2.4 映射概念:
映射類似於數據庫中的表結構,每一個索引都有一個映射,它定義了每一個字段類型,以及一個索引范圍內的設置。一個映射可以被事先定義,如果沒有事先定義,也存儲文檔的時候ES也會嘗試進行自動識別。
mapping機制也被ES用於字段分析,每個字段都有一種確定的類型
field(字段)則非常好理解,類似於MySQL中列的概念,每個field都有一個類型,這個關系被定義在mapping中。重要的是,ES中,每一個字段都會被索引,因此其查詢能力非常的強。
source field:默認情況下,document的原始內容被存儲在_source中,查詢的時候返回的是一個精確的json字符串。
三、安裝
依賴於jdk,依賴於java的都好安裝,直接解壓或者rpm包都可以,略
集群搭建也很簡單,一般的方式,同步時間,無ssh密鑰通信等.
其配置文件解釋的非常詳細...
systemctl daemon-reload
systemctl start elasticsearch.service
如果出錯JAVA_HOME找不到,可以在/etc/sysconfig/elasticsearch中指定環境變量JAVA_HOME
最近發現2.3版本的集群配置,不僅僅是配置為同一個cluster name,還需要大致修改一下..
# 修改集群名稱 cluster.name: cluster_name # 修改節點名稱 node.name: node-01 # 修改綁定IP地址 network.host 0.0.0.0 # 自定義端口 http.port: 9200 # 自動探測同集群節點的Hosts列表 discovery.zen.ping.unicast.hosts: ["192.168.131.129"] # 組成集群的最少節點數 discovery.zen.minimum_master_nodes: 2
安裝插件:
插件集地址: http://www.kailing.pub/article/index/arcid/87.html
插件擴展的功能:
添加自定義的映射類型、自定義分詞器、本地腳本、自定義節點發現方式
安裝:
直接將plugin放置於插件目錄
使用命令plugin
先安裝一些常用的插件:
1. 安裝Head,免費
./plugin install mobz/elasticsearch-head
2. 安裝marvel,收費的,但是官方推薦...
3. 安裝分詞器ik
https://github.com/medcl/elasticsearch-analysis-ik上有源碼,因此可以使用源碼安裝
修改默認配置為:index.analysis.analyzer.default.type: ik
四、客戶端
1、java客戶端:
Elasticsearch為Java用戶提供了兩種內置客戶端:
節點客戶端(node client):
節點客戶端以無數據節點(none data node)身份加入集群,換言之,它自己不存儲任何數據,但是它知道數據在集群中的具體位置,並且能夠直接轉發請求到對應的節點上。
傳輸客戶端(Transport client):
這個更輕量的傳輸客戶端能夠發送請求到遠程集群。它自己不加入集群,只是簡單轉發請求給集群中的節點。
說明:兩個Java客戶端都通過9300端口與集群交互,使用Elasticsearch傳輸協議(Elasticsearch Transport Protocol)。集群中的節點之間也通過9300端口進行通信。如果此端口未開放,你的節點將不能組成集群。
Tips: Java客戶端所在的Elasticsearch版本必須與集群中其他節點一致,否則,它們可能互相無法識別。
2、基於HTTP協議,以JSON為數據交互格式的RESTful API
其他所有程序語言都可以使用RESTful API,通過9200端口的與Elasticsearch進行通信,你可以使用你喜歡的WEB客戶端,事實上,如你所見,你甚至可以通過curl
命令與Elasticsearch通信。
NOTE
Elasticsearch官方提供了多種程序語言的客戶端——Groovy,Javascript, .NET,PHP,Perl,Python,以及 Ruby——還有很多由社區提供的客戶端和插件,所有這些可以在文檔中找到
向Elasticsearch發出的請求的組成部分與其它普通的HTTP請求是一樣的,下面是curl命令說明:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
- VERB HTTP方法:
GET
,POST
,PUT
,HEAD
,DELETE
- PROTOCOL http或者https協議(只有在Elasticsearch前面有https代理的時候可用)
- HOST Elasticsearch集群中的任何一個節點的主機名,如果是在本地的節點,那么就叫localhost
- PORT Elasticsearch HTTP服務所在的端口,默認為9200
- PATH API路徑(例如_count將返回集群中文檔的數量),PATH可以包含多個組件,例如_cluster/stats或者_nodes/stats/jvm
- QUERY_STRING 一些可選的查詢請求參數,例如
?pretty
參數將使請求返回更加美觀易讀的JSON數據 - BODY 一個JSON格式的請求主體(如果請求需要的話)
五、索引
下面我們以一個實例來演示相關概念
假設我們剛好在Megacorp工作,這時人力資源部門出於某種目的需要讓我們創建一個員工目錄,這個目錄用於促進人文關懷和用於實時協同工作,所以它有以下不同的需求:
- 數據能夠包含多個值的標簽、數字和純文本。
- 檢索任何員工的所有信息。
- 支持結構化搜索,例如查找30歲以上的員工。
- 支持簡單的全文搜索和更復雜的短語(phrase)搜索
- 高亮搜索結果中的關鍵字
- 能夠利用圖表管理分析這些數據
索引員工文檔
我們首先要做的是存儲員工數據,每個文檔代表一個員工。在Elasticsearch中存儲數據的行為就叫做索引(indexing),不過在索引之前,我們需要明確數據應該存儲在哪里。
在Elasticsearch中,文檔歸屬於一種類型(type),而這些類型存在於索引(index)中,我們可以畫一些簡單的對比圖來類比傳統關系型數據庫:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
索引不同於數據庫的概念
- 索引(名詞) 如上文所述,一個索引(index)就像是傳統關系數據庫中的數據庫,它是相關文檔存儲的地方,index的復數是indices 或indexes。
- 索引(動詞) 「索引一個文檔」表示把一個文檔存儲到索引(名詞)里,以便它可以被檢索或者查詢。這很像SQL中的
INSERT
關鍵字,差別是,如果文檔已經存在,新的文檔將覆蓋舊的文檔。- 倒排索引 傳統數據庫為特定列增加一個索引,例如B-Tree索引來加速檢索。Elasticsearch和Lucene使用一種叫做倒排索引(inverted index)的數據結構來達到相同目的。
curl -XPUT 'http://localhost:9200/megacorp/employee/1' -d '
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
'
可以使用head插件查看:
下面使用類似的操作加入更多的信息:
curl -XPUT 'http://localhost:9200/megacorp/employee/2' -d '
{
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests": [ "music" ]
}
'
curl -XPUT 'http://localhost:9200/megacorp/employee/3' -d '
{
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about": "I like to build cabinets",
"interests": [ "forestry" ]
}
'
六、檢索文檔
直接使用GET請求,非常簡單
[root@ysz211 ~]# curl 'http://localhost:9200/megacorp/employee/1?pretty'
{
"_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" ]
}
}
Tips: RESTFUL風格的API
我們通過HTTP方法
GET
來檢索文檔,同樣的,我們可以使用DELETE
方法刪除文檔,使用HEAD
方法檢查某文檔是否存在。如果想更新已存在的文檔,我們只需再PUT
一次。
6.1 簡單搜索
GET
請求非常簡單——你能輕松獲取你想要的文檔。讓我們來進一步嘗試一些東西,比如簡單的搜索!
我們嘗試一個最簡單的搜索全部員工的請求:
curl 'http://localhost:9200/megacorp/employee/_search?pretty'
返回結果大致如下:
{
"took" : 12,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 3,
"max_score" : 1.0,
"hits" : [ {
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_score" : 1.0,
"_source" : {
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests" : [ "music" ]
}
}, {
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [ "sports", "music" ]
}
}, {
"_index" : "megacorp",
"_type" : "employee",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about" : "I like to build cabinets",
"interests" : [ "forestry" ]
}
} ]
}
}
接下來,讓我們搜索姓氏中包含“Smith”的員工。要做到這一點,我們將在命令行中使用輕量級的搜索方法。這種方法常被稱作查詢字符串(query string)搜索,因為我們像傳遞URL參數一樣去傳遞查詢語句:
curl 'http://localhost:9200/megacorp/employee/_search?q=last_name:Smith&pretty'
6.2 使用DSL語句查詢
查詢字符串搜索便於通過命令行完成特定(ad hoc)的搜索,但是它也有局限性(參閱簡單搜索章節)。Elasticsearch提供豐富且靈活的查詢語言叫做DSL查詢(Query DSL),它允許你構建更加復雜、強大的查詢。
DSL(Domain Specific Language特定領域語言)以JSON請求體的形式出現。我們可以這樣表示之前關於“Smith”的查詢:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
'
這會返回與之前查詢相同的結果。
更復雜的搜索
我們讓搜索稍微再變的復雜一些。我們依舊想要找到姓氏為“Smith”的員工,但是我們只想得到年齡大於30歲的員工。我們的語句將添加過濾器(filter),它使得我們高效率的執行一個結構化搜索:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"query" : {
"filtered" : {
"filter" : {
"range" : {
"age" : { "gt" : 30 }
}
},
"query" : {
"match" : {
"last_name" : "smith"
}
}
}
}
}
'
注意上面的例子同時使用了過濾器和match進行搜索。
全文搜索
到目前為止搜索都很簡單:搜索特定的名字,通過年齡篩選。讓我們嘗試一種更高級的搜索,全文搜索——一種傳統數據庫很難實現的功能。
我們將會搜索所有喜歡“rock climbing”的員工:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
'
這里返回的結果可能會讓人疑惑,首先我們來看一下結果:
{
"took" : 10,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 0.18985549,
"hits" : [ {
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_score" : 0.18985549,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [ "sports", "music" ]
}
}, {
"_index" : "megacorp",
"_type" : "employee",
"_id" : "2",
"_score" : 0.019691018,
"_source" : {
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests" : [ "music" ]
}
} ]
}
}
這里居然出現了2個結果,其中第二個只包含了rock,這就是結果相關性評分。
默認情況下,Elasticsearch根據結果相關性評分來對結果集進行排序,所謂的「結果相關性評分」就是文檔與查詢條件的匹配程度。很顯然,排名第一的
John Smith
的about
字段明確的寫到“rock climbing”。但是為什么
Jane Smith
也會出現在結果里呢?原因是“rock”在她的abuot
字段中被提及了。因為只有“rock”被提及而“climbing”沒有,所以她的_score
要低於John。
這個例子很好的解釋了Elasticsearch如何在各種文本字段中進行全文搜索,並且返回相關性最大的結果集。相關性(relevance)的概念在Elasticsearch中非常重要,而這個概念在傳統關系型數據庫中是不可想象的,因為傳統數據庫對記錄的查詢只有匹配或者不匹配。
短語搜索
目前我們可以在字段中搜索單獨的一個詞,這挺好的,但是有時候你想要確切的匹配若干個單詞或者短語(phrases)。例如我們想要查詢同時包含"rock"和"climbing"(並且是相鄰的)的員工記錄。
要做到這個,我們只要將match
查詢變更為match_phrase
查詢即可:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}
}
'
高亮我們的搜索
很多應用喜歡從每個搜索結果中高亮(highlight)匹配到的關鍵字,這樣用戶可以知道為什么這些文檔和查詢相匹配。在Elasticsearch中高亮片段是非常容易的。
讓我們在之前的語句上增加highlight
參數:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
'
當我們運行這個語句時,會命中與之前相同的結果,但是在返回結果中會有一個新的部分叫做highlight
,這里包含了來自about
字段中的文本,並且用<em></em>
來標識匹配到的單詞。
七、聚合
分析
最后,我們還有一個需求需要完成:允許管理者在職員目錄中進行一些分析。 Elasticsearch有一個功能叫做聚合(aggregations),它允許你在數據上生成復雜的分析統計。它很像SQL中的GROUP BY
但是功能更強大。
舉個例子,讓我們找到所有職員中最大的共同點(興趣愛好)是什么:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"aggs": {
"all_interests": {
"terms": { "field": "interests" }
}
}
}
'
暫時先忽略語法只看查詢結果:
{
...
"hits": { ... },
"aggregations": {
"all_interests": {
"buckets": [
{
"key": "music",
"doc_count": 2
},
{
"key": "forestry",
"doc_count": 1
},
{
"key": "sports",
"doc_count": 1
}
]
}
}
}
我們可以看到兩個職員對音樂有興趣,一個喜歡林學,一個喜歡運動。這些數據並沒有被預先計算好,它們是實時的從匹配查詢語句的文檔中動態計算生成的。如果我們想知道所有姓"Smith"的人最大的共同點(興趣愛好),我們只需要增加合適的語句既可:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"query": {
"match": {
"last_name": "smith"
}
},
"aggs": {
"all_interests": {
"terms": {
"field": "interests"
}
}
}
}
'
聚合也允許分級匯總。例如,讓我們統計每種興趣下職員的平均年齡:
curl 'http://localhost:9200/megacorp/employee/_search?pretty' -d '
{
"aggs" : {
"all_interests" : {
"terms" : { "field" : "interests" },
"aggs" : {
"avg_age" : {
"avg" : { "field" : "age" }
}
}
}
}
}
'
雖然這次返回的聚合結果有些復雜,但任然很容易理解:
...
"all_interests": {
"buckets": [
{
"key": "music",
"doc_count": 2,
"avg_age": {
"value": 28.5
}
},
{
"key": "forestry",
"doc_count": 1,
"avg_age": {
"value": 35
}
},
{
"key": "sports",
"doc_count": 1,
"avg_age": {
"value": 25
}
}
]
}