只列出了感覺重要的部分,完整內(nèi)容請查看這里。
適用于Elasticsearch 2.x版本
添加故障轉(zhuǎn)移
當(dāng)集群中只有一個(gè)節(jié)點(diǎn)在運(yùn)行時(shí),意味著會有一個(gè)單點(diǎn)故障問題——沒有冗余。 幸運(yùn)的是,我們只需再啟動一個(gè)節(jié)點(diǎn)即可防止數(shù)據(jù)丟失。
當(dāng)你在同一臺機(jī)器上啟動了第二個(gè)節(jié)點(diǎn)時(shí),只要它和第一個(gè)節(jié)點(diǎn)有同樣的 cluster.name 配置,它就會自動發(fā)現(xiàn)集群并加入到其中。 但是在不同機(jī)器上啟動節(jié)點(diǎn)的時(shí)候,為了加入到同一集群,你需要配置一個(gè)可連接到的單播主機(jī)列表。
當(dāng)?shù)诙€(gè)節(jié)點(diǎn)加入到集群后,3個(gè)副本分片將會分配到這個(gè)節(jié)點(diǎn)上——每個(gè)主分片對應(yīng)一個(gè)副本分片。 這意味著當(dāng)集群內(nèi)任何一個(gè)節(jié)點(diǎn)出現(xiàn)問題時(shí),我們的數(shù)據(jù)都完好無損。
所有新近被索引的文檔都將會保存在主分片上,然后被并行的復(fù)制到對應(yīng)的副本分片上。這就保證了我們既可以從主分片又可以從副本分片上獲得文檔。
搜索——最基本的工具
文檔中的每個(gè)字段都將被索引并且可以被查詢 。
搜索(search)可以做到:
- 在類似于 gender 或者 age 這樣的字段上使用結(jié)構(gòu)化查詢,join_date 這樣的字段上使用排序,就像SQL的結(jié)構(gòu)化查詢一樣。
- 全文檢索,找出所有匹配關(guān)鍵字的文檔并按照相關(guān)性(relevance)排序后返回結(jié)果。
- 以上二者兼而有之。
很多搜索都是開箱即用的,為了充分挖掘Elasticsearch的潛力,你需要理解以下三個(gè)概念:
映射(Mapping)
- 描述數(shù)據(jù)在每個(gè)字段內(nèi)如何存儲
分析(Analysis)
- 全文是如何處理使之可以被搜索的
領(lǐng)域特定查詢語言(Query DSL)
- Elasticsearch中強(qiáng)大靈活的查詢語言
timeout
timed_out 值告訴我們查詢是否超時(shí)。默認(rèn)情況下,搜索請求不會超時(shí)。 如果低響應(yīng)時(shí)間比完成結(jié)果更重要,你可以指定 timeout 為 10 或者 10ms(10毫秒),或者 1s(1秒):
GET /_search?timeout=10ms
在請求超時(shí)之前,Elasticsearch 將會返回已經(jīng)成功從每個(gè)分片獲取的結(jié)果。
應(yīng)當(dāng)注意的是 timeout 不是停止執(zhí)行查詢,它僅僅是告知正在協(xié)調(diào)的節(jié)點(diǎn)返回到目前為止收集的結(jié)果并且關(guān)閉連接。在后臺,其他的分片可能仍在執(zhí)行查詢即使是結(jié)果已經(jīng)被發(fā)送了。
使用超時(shí)是因?yàn)镾LA(服務(wù)等級協(xié)議)對你是很重要的,而不是因?yàn)橄肴ブ兄归L時(shí)間運(yùn)行的查詢。
多索引,多類型
如果不對某一特殊的索引或者類型做限制,就會搜索集群中的所有文檔。Elasticsearch 轉(zhuǎn)發(fā)搜索請求到每一個(gè)主分片或者副本分片,匯集查詢出的前10個(gè)結(jié)果,并且返回給我們。
/_search 在所有的索引中搜索所有的類型
/gb/_search 在 gb 索引中搜索所有的類型
/gb,us/_search 在 gb 和 us 索引中搜索所有的文檔
/g*,u*/_search 在任何以 g 或者 u 開頭的索引中搜索所有的類型
分頁
和 SQL 使用 LIMIT 關(guān)鍵字返回單個(gè) page 結(jié)果的方法相同,Elasticsearch 接受 from 和 size 參數(shù):
size
- 顯示應(yīng)該返回的結(jié)果數(shù)量,默認(rèn)是 10
from
- 顯示應(yīng)該跳過的初始結(jié)果數(shù)量,默認(rèn)是 0
如果每頁展示 5 條結(jié)果,可以用下面方式請求得到 1 到 3 頁的結(jié)果:
GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
考慮到分頁過深以及一次請求太多結(jié)果的情況,結(jié)果集在返回之前先進(jìn)行排序。 但請記住一個(gè)請求經(jīng)??缭蕉鄠€(gè)分片,每個(gè)分片都產(chǎn)生自己的排序結(jié)果,這些結(jié)果需要進(jìn)行集中排序以保證整體順序是正確的。
在分布式系統(tǒng)中深度分頁
理解為什么深度分頁是有問題的,我們可以假設(shè)在一個(gè)有 5 個(gè)主分片的索引中搜索。 當(dāng)我們請求結(jié)果的第一頁(結(jié)果從 1 到 10 ),每一個(gè)分片產(chǎn)生前 10 的結(jié)果,并且返回給協(xié)調(diào)節(jié)點(diǎn) ,協(xié)調(diào)節(jié)點(diǎn)對 50 個(gè)結(jié)果排序得到全部結(jié)果的前 10 個(gè)。
現(xiàn)在假設(shè)我們請求第 1000 頁--結(jié)果從 10001 到 10010 。所有都以相同的方式工作除了每個(gè)分片不得不產(chǎn)生前10010個(gè)結(jié)果以外。然后協(xié)調(diào)節(jié)點(diǎn)對全部 50050 個(gè)結(jié)果排序最后丟棄掉這些結(jié)果中的 50040 個(gè)結(jié)果。
可以看到,在分布式系統(tǒng)中,對結(jié)果排序的成本隨分頁的深度成指數(shù)上升。這就是 web 搜索引擎對任何查詢都不要返回超過 1000 個(gè)結(jié)果的原因。
“輕量”搜索
有兩種形式的 搜索 API:一種是 “輕量的” 查詢字符串 版本,要求在查詢字符串中傳遞所有的 參數(shù),另一種是更完整的請求體版本,要求使用 JSON 格式和更豐富的查詢表達(dá)式作為搜索語言。
查詢字符串搜索非常適用于通過命令行做即席查詢。例如,查詢在 tweet 類型中 tweet 字段包含 elasticsearch 單詞的所有文檔:
GET /_all/tweet/_search?q=tweet:elasticsearch
但是查詢字符串參數(shù)所需要的百分比編碼(譯者注:URL編碼)實(shí)際上更加難懂:
GET /_search?q=%2Bname%3Ajohn+%2Btweet%3Amary
查詢字符串搜索允許任何用戶在索引的任意字段上執(zhí)行可能較慢且重量級的查詢,這可能會暴露隱私信息,甚至將集群拖垮。因?yàn)檫@些原因,不推薦直接向用戶暴露查詢字符串搜索功能,除非對于集群和數(shù)據(jù)來說非常信任他們。
相反,我們經(jīng)常在生產(chǎn)環(huán)境中更多地使用功能全面的 request body 查詢API,除了能完成以上所有功能,還有一些附加功能。
映射和分析
讓我們看一看 Elasticsearch 是如何解釋我們文檔結(jié)構(gòu)的:
{
"gb": {
"mappings": {
"tweet": {
"properties": {
"date": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
},
"name": {
"type": "string"
},
"tweet": {
"type": "string"
},
"user_id": {
"type": "long"
}
}
}
}
}
}
基于對字段類型的猜測, Elasticsearch 動態(tài)為我們產(chǎn)生了一個(gè)映射。這個(gè)響應(yīng)告訴我們 date 字段被認(rèn)為是 date 類型的。由于 _all 是默認(rèn)字段,所以沒有提及它。但是我們知道 _all 字段是 string 類型的。
所以 date 字段和 string 字段 索引方式不同,因此搜索結(jié)果也不一樣。這完全不令人吃驚。你可能會認(rèn)為核心數(shù)據(jù)類型 strings、numbers、Booleans 和 dates 的索引方式有稍許不同。沒錯(cuò),他們確實(shí)稍有不同。
但是,到目前為止,最大的差異在于 代表 精確值 (它包括 string 字段)的字段和代表 全文 的字段。這個(gè)區(qū)別非常重要——它將搜索引擎和所有其他數(shù)據(jù)庫區(qū)別開來。
在Elasticsearch 5.X版本中有變化。
精確值 VS 全文
Elasticsearch 中的數(shù)據(jù)可以概括的分為兩類:精確值和全文。
精確值如它們聽起來那樣精確。例如日期或者用戶 ID,但字符串也可以表示精確值,例如用戶名或郵箱地址。對于精確值來講,F(xiàn)oo 和 foo 是不同的,2014 和 2014-09-15 也是不同的。
另一方面,全文是指文本數(shù)據(jù)(通常以人類容易識別的語言書寫),例如一個(gè)推文的內(nèi)容或一封郵件的內(nèi)容。
精確值很容易查詢。結(jié)果是二進(jìn)制的:要么匹配查詢,要么不匹配。這種查詢很容易用 SQL 表示:
WHERE name = "John Smith"
AND user_id = 2
AND date > "2014-09-15"
查詢?nèi)臄?shù)據(jù)要微妙的多。我們問的不只是“這個(gè)文檔匹配查詢嗎”,而是“該文檔匹配查詢的程度有多大?”換句話說,該文檔與給定查詢的相關(guān)性如何?
我們很少對全文類型的域做精確匹配。相反,我們希望在文本類型的域中搜索。不僅如此,我們還希望搜索能夠理解我們的意圖 :
- 搜索 UK ,會返回包含 United Kindom 的文檔。
- 搜索 jump ,會匹配 jumped , jumps , jumping ,甚至是 leap 。
- 搜索 johnny walker 會匹配 Johnnie Walker , johnnie depp 應(yīng)該匹配 Johnny Depp 。
- fox news hunting 應(yīng)該返回福克斯新聞( Foxs News )中關(guān)于狩獵的故事,同時(shí), fox hunting news 應(yīng)該返回關(guān)于獵狐的故事。
為了促進(jìn)這類在全文域中的查詢,Elasticsearch首先分析文檔,之后根據(jù)結(jié)果創(chuàng)建 倒排索引 。
倒排索引
Elasticsearch 使用一種稱為倒排索引 的結(jié)構(gòu),它適用于快速的全文搜索。一個(gè)倒排索引由文檔中所有不重復(fù)詞的列表構(gòu)成,對于其中每個(gè)詞,有一個(gè)包含它的文檔列表。
例如,假設(shè)我們有兩個(gè)文檔,每個(gè)文檔的 content 域包含如下內(nèi)容:
- The quick brown fox jumped over the lazy dog
- Quick brown foxes leap over lazy dogs in summer
為了創(chuàng)建倒排索引,我們首先將每個(gè)文檔的 content 域拆分成單獨(dú)的詞(我們稱它為詞條或tokens ),創(chuàng)建一個(gè)包含所有不重復(fù)詞條的排序列表,然后列出每個(gè)詞條出現(xiàn)在哪個(gè)文檔。結(jié)果如下所示:
Term Doc_1 Doc_2
-------------------------
Quick | | X
The | X |
brown | X | X
dog | X |
dogs | | X
fox | X |
foxes | | X
in | | X
jumped | X |
lazy | X | X
leap | | X
over | X | X
quick | X |
summer | | X
the | X |
------------------------
現(xiàn)在,如果我們想搜索 quick brown ,我們只需要查找包含每個(gè)詞條的文檔:
Term Doc_1 Doc_2
-------------------------
brown | X | X
quick | X |
------------------------
Total | 2 | 1
兩個(gè)文檔都匹配,但是第一個(gè)文檔比第二個(gè)匹配度更高。如果我們使用僅計(jì)算匹配詞條數(shù)量的簡單 相似性算法,那么我們可以說,對于我們查詢的相關(guān)性來講,第一個(gè)文檔比第二個(gè)文檔更佳。
分析與分析器
分析包含下面的過程:
- 首先,將一塊文本分成適合于倒排索引的獨(dú)立的詞條
- 之后,將這些詞條統(tǒng)一化為標(biāo)準(zhǔn)格式以提高它們的“可搜索性”,或者
recall
分析器執(zhí)行上面的工作。 分析器實(shí)際上是將三個(gè)功能封裝到了一個(gè)包里:
字符過濾器
- 首先,字符串按順序通過每個(gè)字符過濾器 。他們的任務(wù)是在分詞前整理字符串。一個(gè)字符過濾器可以用來去掉HTML,或者將
&轉(zhuǎn)化成and。
分詞器
- 其次,字符串被分詞器分為單個(gè)的詞條。一個(gè)簡單的分詞器遇到空格和標(biāo)點(diǎn)的時(shí)候,可能會將文本拆分成詞條。
Token 過濾器
- 最后,詞條按順序通過每個(gè) token 過濾器 。這個(gè)過程可能會改變詞條(例如,小寫化 Quick ),刪除詞條(例如, 像
a,and,the等無用詞),或者增加詞條(例如,像 jump 和 leap 這種同義詞)。
Elasticsearch提供了開箱即用的字符過濾器、分詞器和token 過濾器。 這些可以組合起來形成自定義的分析器以用于不同的目的。
內(nèi)置分析器
但是, Elasticsearch還附帶了可以直接使用的預(yù)包裝的分析器。 接下來我們會列出最重要的分析器。為了證明它們的差異,我們看看每個(gè)分析器會從下面的字符串得到哪些詞條:
"Set the shape to semi-transparent by calling set_trans(5)"
1、標(biāo)準(zhǔn)分析器
標(biāo)準(zhǔn)分析器是Elasticsearch默認(rèn)使用的分析器。它是分析各種語言文本最常用的選擇。它根據(jù) Unicode 聯(lián)盟定義的單詞邊界劃分文本。刪除絕大部分標(biāo)點(diǎn)。最后,將詞條小寫。它會產(chǎn)生:
set, the, shape, to, semi, transparent, by, calling, set_trans, 5
2、簡單分析器
簡單分析器在任何不是字母的地方分隔文本,將詞條小寫。它會產(chǎn)生
set, the, shape, to, semi, transparent, by, calling, set, trans
3、空格分析器
空格分析器在空格的地方劃分文本。它會產(chǎn)生
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
4、語言分析器
特定語言分析器可用于很多語言。它們可以考慮指定語言的特點(diǎn)。例如,英語 分析器附帶了一組英語無用詞(常用單詞,例如 and 或者 the ,它們對相關(guān)性沒有多少影響),它們會被刪除。 由于理解英語語法的規(guī)則,這個(gè)分詞器可以提取英語單詞的詞干。
英語 分詞器會產(chǎn)生下面的詞條:
set, shape, semi, transpar, call, set_tran, 5
注意看 transparent、 calling 和 set_trans 已經(jīng)變?yōu)樵~根格式。
什么時(shí)候使用分析器
當(dāng)我們 索引 一個(gè)文檔,它的全文域被分析成詞條以用來創(chuàng)建倒排索引。 但是,當(dāng)我們在全文域搜索的時(shí)候,我們需要將查詢字符串通過相同的分析過程 ,以保證我們搜索的詞條格式與索引中的詞條格式一致。
全文查詢理解每個(gè)域是如何定義的,因此它們可以做正確的事:
- 當(dāng)你查詢一個(gè) 全文 域時(shí),會對查詢字符串應(yīng)用相同的分析器,以產(chǎn)生正確的搜索詞條列表。
- 當(dāng)你查詢一個(gè) 精確值 域時(shí),不會分析查詢字符串,而是搜索你指定的精確值。
現(xiàn)在你可以理解在開始章節(jié)的查詢?yōu)槭裁捶祷啬菢拥慕Y(jié)果:
date 域包含一個(gè)精確值:單獨(dú)的詞條 2014-09-15。
_all 域是一個(gè)全文域,所以分詞進(jìn)程將日期轉(zhuǎn)化為三個(gè)詞條: 2014, 09, 和 15。
當(dāng)我們在 _all 域查詢 2014,它匹配所有的12條推文,因?yàn)樗鼈兌己?2014。
映射
為了能夠?qū)r(shí)間域視為時(shí)間,數(shù)字域視為數(shù)字,字符串域視為全文或精確值字符串, Elasticsearch 需要知道每個(gè)域中數(shù)據(jù)的類型。這個(gè)信息包含在映射中。
Elasticsearch 支持 如下簡單域類型:
- 字符串:
string - 整數(shù) :
byte,short,integer,long - 浮點(diǎn)數(shù):
float,double - 布爾型:
boolean - 日期:
date
當(dāng)你索引一個(gè)包含新域的文檔--之前未曾出現(xiàn)-- Elasticsearch 會使用 動態(tài)映射 ,通過JSON中基本數(shù)據(jù)類型,嘗試猜測域類型,使用如下規(guī)則:
| JSON類型 | 域類型 |
|---|---|
布爾型: true 或者 false
|
boolean |
| 整數(shù): 123 | long |
| 浮點(diǎn)數(shù): 123.45 | double |
| 字符串,有效日期: 2014-09-15 | date |
| 字符串: foo bar | string |
注意:這些類型在Elasticsearch 5.X版本中有變化,以后會更新。
這意味著如果你通過引號( "123" )索引一個(gè)數(shù)字,它會被映射為 string 類型,而不是 long 。但是,如果這個(gè)域已經(jīng)映射為 long ,那么 Elasticsearch 會嘗試將這個(gè)字符串轉(zhuǎn)化為 long ,如果無法轉(zhuǎn)化,則拋出一個(gè)異常。
查看映射
通過 /_mapping ,我們可以查看 Elasticsearch 在一個(gè)或多個(gè)索引中的一個(gè)或多個(gè)類型的映射 。取得索引 gb 中類型 tweet 的映射:
GET /gb/_mapping/tweet
返回結(jié)果:
{
"gb": {
"mappings": {
"tweet": {
"properties": {
"date": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
},
"name": {
"type": "string"
},
"tweet": {
"type": "string"
},
"user_id": {
"type": "long"
}
}
}
}
}
}
錯(cuò)誤的映射,例如 將 age 域映射為 string 類型,而不是 integer ,會導(dǎo)致查詢出現(xiàn)令人困惑的結(jié)果。
自定義域映射
自定義映射允許你執(zhí)行下面的操作:
- 全文字符串域和精確值字符串域的區(qū)別
- 使用特定語言分析器
- 優(yōu)化域以適應(yīng)部分匹配
- 指定自定義數(shù)據(jù)格式
- 還有更多
域最重要的屬性是 type 。對于不是 string 的域,你一般只需要設(shè)置 type :
{
"number_of_clicks": {
"type": "integer"
}
}
默認(rèn), string 類型域會被認(rèn)為包含全文。也就是說它們的值在索引前會通過 一個(gè)分析器,針對于這個(gè)域的查詢在搜索前也會經(jīng)過一個(gè)分析器。
string 域映射的兩個(gè)最重要的屬性是 index 和 analyzer。
index
index屬性控制怎樣索引字符串。它可以是下面三個(gè)值:
analyzed(默認(rèn))
- 首先分析字符串,然后索引它。換句話說,以全文索引這個(gè)域。
not_analyzed
- 索引這個(gè)域,所以可以搜索到它,但索引指定的精確值。不對它進(jìn)行分析。
no
- 不索引這個(gè)域。這個(gè)域不會被搜索到。
如果我們想映射string 類型域?yàn)橐粋€(gè)精確值,我們需要設(shè)置它的index屬性為 not_analyzed :
{
"tag": {
"type": "string",
"index": "not_analyzed"
}
}
其他簡單類型(例如 long , double , date 等)也接受 index 參數(shù),但有意義的值只有 no 和 not_analyzed , 因?yàn)樗鼈冇肋h(yuǎn)不會被分析。
analyzer
對于index被設(shè)置為analyzed的字符串域,用analyzer 屬性指定在搜索和索引時(shí)使用的分析器。默認(rèn), Elasticsearch 使用 standard 分析器, 但你可以指定一個(gè)內(nèi)置的分析器替代它,例如 whitespace 、 simple 和 english:
{
"tweet": {
"type": "string",
"analyzer": "english"
}
}
更新映射
當(dāng)你首次創(chuàng)建一個(gè)索引的時(shí)候,可以指定類型的映射。你也可以使用 /_mapping 為新類型增加映射。
盡管你可以增加一個(gè)映射,你不能修改 存在的域映射。如果一個(gè)域的映射已經(jīng)存在,那么該域的數(shù)據(jù)可能已經(jīng)被索引。如果你意圖修改這個(gè)域的映射,索引的數(shù)據(jù)可能會出錯(cuò),不能被正常的搜索。
我們決定在 tweet 映射增加一個(gè)新的名為 tag 的 not_analyzed 的文本域,使用 _mapping :
PUT /gb/_mapping/tweet
{
"properties" : {
"tag" : {
"type" : "string",
"index": "not_analyzed"
}
}
}
注意,我們不需要再次列出所有已存在的域,因?yàn)闊o論如何我們都無法改變它們。新域已經(jīng)被合并到存在的映射中。
復(fù)雜類型
除了我們提到的簡單標(biāo)量數(shù)據(jù)類型, JSON 還有 null 值,數(shù)組,和對象,這些 Elasticsearch 都是支持的。
數(shù)組
很有可能,我們希望 tag 域包含多個(gè)標(biāo)簽。我們可以以數(shù)組的形式索引標(biāo)簽:
{ "tag": [ "search", "nosql" ]}
對于數(shù)組,沒有特殊的映射需求。任何域都可以包含0、1或者多個(gè)值。
這暗示數(shù)組中所有的值必須是相同數(shù)據(jù)類型的 。你不能將日期和字符串混在一起。如果你通過索引數(shù)組來創(chuàng)建新的域,Elasticsearch 會用數(shù)組中第一個(gè)值的數(shù)據(jù)類型作為這個(gè)域的類型。
未完,待續(xù)...