聲明:
本文轉(zhuǎn)自我的個(gè)人博客,有興趣的可以查看原文。
轉(zhuǎn)發(fā)請(qǐng)注明來(lái)源。
這是一篇科普文。
1. 背景
Elasticsearch 在公司的使用越來(lái)越廣,很多同事之前并沒(méi)有接觸過(guò) Elasticsearch,所以,最近在公司準(zhǔn)備了一次關(guān)于 Elasticsearch 的分享,整理成此文。此文面向 Elasticsearch 新手,老司機(jī)們可以撤了。
2. 倒排索引
先簡(jiǎn)單介紹下搜索引擎的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)倒排索引。
我們?cè)谄綍r(shí),會(huì)經(jīng)常使用各種各樣的索引,如我們根據(jù)鏈接,可以找到鏈接里的具體文本,這就是索引。反過(guò)來(lái),如果,如果我們能根據(jù)具體文本,找到文本存在的具體鏈接,這就是倒排索引,可簡(jiǎn)單理解為從文本到鏈接的映射。我們平時(shí)在使用Google、百度時(shí),就是根據(jù)具體文本去找鏈接,這就是以倒排索引為基礎(chǔ)的。
可參看維基百科 。
3. Elasticsearch 簡(jiǎn)介與基本概念
Elasticsearch is a real-time distributed search and analytics engine. It allows you to explore your data at a speed and at a scale never before possible. It is used for full-text search, structured search, analytics, and all three in combination.
在 《Elasticsearch : The Definitive Guide》里,這樣介紹Elasticsearch,總的來(lái)說(shuō),Elasticsearch 是一個(gè)分布式的搜索和分析引擎,可以用于全文檢索、結(jié)構(gòu)化檢索和分析,并能將這三者結(jié)合起來(lái)。Elasticsearch 基于 Lucene 開(kāi)發(fā),現(xiàn)在是使用最廣的開(kāi)源搜索引擎之一,Wikipedia、Stack Overflow、GitHub 等都基于 Elasticsearch 來(lái)構(gòu)建他們的搜索引擎。
先介紹下 Elasticsearch 里的基本概念,下圖是 Elasticsearch 插件 head 的一個(gè)截圖。

- node:即一個(gè) Elasticsearch 的運(yùn)行實(shí)例,使用多播或單播方式發(fā)現(xiàn) cluster 并加入。
- cluster:包含一個(gè)或多個(gè)擁有相同集群名稱(chēng)的 node,其中包含一個(gè)master node。
- index:類(lèi)比關(guān)系型數(shù)據(jù)庫(kù)里的DB,是一個(gè)邏輯命名空間。
- alias:可以給 index 添加零個(gè)或多個(gè)alias,通過(guò) alias 使用index 和根據(jù)index name 訪問(wèn)index一樣,但是,alias給我們提供了一種切換index的能力,比如重建了index,取名customer_online_v2,這時(shí),有了alias,我要訪問(wèn)新 index,只需要把 alias 添加到新 index 即可,并把a(bǔ)lias從舊的 index 刪除。不用修改代碼。
- type:類(lèi)比關(guān)系數(shù)據(jù)庫(kù)里的Table。其中,一個(gè)index可以定義多個(gè)type,但一般使用習(xí)慣僅配一個(gè)type。
- mapping:類(lèi)比關(guān)系型數(shù)據(jù)庫(kù)中的 schema 概念,mapping 定義了 index 中的 type。mapping 可以顯示的定義,也可以在 document 被索引時(shí)自動(dòng)生成,如果有新的 field,Elasticsearch 會(huì)自動(dòng)推測(cè)出 field 的type并加到mapping中。
- document:類(lèi)比關(guān)系數(shù)據(jù)庫(kù)里的一行記錄(record),document 是 Elasticsearch 里的一個(gè) JSON 對(duì)象,包括零個(gè)或多個(gè)field。
- field:類(lèi)比關(guān)系數(shù)據(jù)庫(kù)里的field,每個(gè)field 都有自己的字段類(lèi)型。
- shard:是一個(gè)Lucene 實(shí)例。Elasticsearch 基于 Lucene,shard 是一個(gè) Lucene 實(shí)例,被 Elasticsearch 自動(dòng)管理。之前提到,index 是一個(gè)邏輯命名空間,shard 是具體的物理概念,建索引、查詢等都是具體的shard在工作。shard 包括primary shard 和 replica shard,寫(xiě)數(shù)據(jù)時(shí),先寫(xiě)到primary shard,然后,同步到replica shard,查詢時(shí),primary 和 replica 充當(dāng)相同的作用。replica shard 可以有多份,也可以沒(méi)有,replica shard的存在有兩個(gè)作用,一是容災(zāi),如果primary shard 掛了,數(shù)據(jù)也不會(huì)丟失,集群仍然能正常工作;二是提高性能,因?yàn)閞eplica 和 primary shard 都能處理查詢。另外,如上圖右側(cè)紅框所示,shard數(shù)和replica數(shù)都可以設(shè)置,但是,shard 數(shù)只能在建立index 時(shí)設(shè)置,后期不能更改,但是,replica 數(shù)可以隨時(shí)更改。但是,由于 Elasticsearch 很友好的封裝了這部分,在使用Elasticsearch 的過(guò)程中,我們一般僅需要關(guān)注 index 即可,不需關(guān)注shard。
綜上所述,shard、node、cluster 在物理上構(gòu)成了 Elasticsearch 集群,field、type、index 在邏輯上構(gòu)成一個(gè)index的基本概念,在使用 Elasticsearch 過(guò)程中,我們一般關(guān)注到邏輯概念就好,就像我們?cè)谑褂肕ySQL 時(shí),我們一般就關(guān)注DB Name、Table和schema即可,而不會(huì)關(guān)注DBA維護(hù)了幾個(gè)MySQL實(shí)例、master 和 slave 等怎么部署的一樣。
下表用Elasticsearch 和 關(guān)系數(shù)據(jù)庫(kù)做了類(lèi)比:
- index => databases
- type => table
- field => field
- document => record
- mapping => schema
最后,來(lái)從 Elasticsearch 中取出一條數(shù)據(jù)(document)看看:

由index、type和id三者唯一確定一個(gè)document,_source 字段中是具體的document 值,是一個(gè)JSON 對(duì)象,有5個(gè)field組成。
4. Elasticsearch 基本使用
下面介紹下 Elasticsearch 的基本使用,這里僅介紹 Elasticsearch 能做什么,而不詳細(xì)介紹語(yǔ)法。
4.1 基礎(chǔ)操作
- index:寫(xiě) document 到 Elasticsearch 中,如果不存在,就創(chuàng)建,如果存在,就用新的取代舊的。
- create:寫(xiě) document 到 Elasticsearch 中,與 index 不同的是,如果存在,就拋出異常
DocumentAlreadyExistException。 - get:根據(jù)ID取出document。
- update:如果是更新整個(gè) document,可用index 操作。如果是部分更新,用update操作。在Elasticsearch中,更新document時(shí),是把舊數(shù)據(jù)取出來(lái),然后改寫(xiě)要更新的部分,刪除舊document,創(chuàng)建新document,而不是在原document上做修改。
- delete:刪除document。Elasticsearch 會(huì)標(biāo)記刪除document,然后,在Lucene 底層進(jìn)行merge時(shí),會(huì)刪除標(biāo)記刪除的document。
4.2 Filter 與 Query
Elasticsearch 使用 domain-specific language(DSL)進(jìn)行查詢,DSL 使用 JSON 進(jìn)行表示。
DSL 由一些子查詢組成,這些子查詢可應(yīng)用于兩類(lèi)查詢,分別是filter 和 query。
filter 正如其字面意思“過(guò)濾”所說(shuō)的,是起過(guò)濾的作用,任何一個(gè)document 對(duì) filter 來(lái)說(shuō),就是match 與否的問(wèn)題,是個(gè)二值問(wèn)題,0和1,沒(méi)有scoring的過(guò)程。
使用query的時(shí)候,是表示match 程度問(wèn)題,有scroing 過(guò)程。
另外,F(xiàn)ilter 和 Query 還有性能上的差異,Elasticsearch 底層對(duì)Filter做了很多優(yōu)化,會(huì)對(duì)過(guò)濾結(jié)果進(jìn)行緩存;同時(shí),F(xiàn)ilter 沒(méi)有相關(guān)性計(jì)算過(guò)程,所以,F(xiàn)ilter 比 Query 快。
所以,官網(wǎng)推薦,作為一條比較通用的規(guī)則,僅在全文檢索時(shí)使用Query,其它時(shí)候都用Filter。但是,根據(jù)我們的使用情況來(lái)看,在過(guò)濾條件不是很強(qiáng)的情況下,緩存可能會(huì)占用較多內(nèi)存,如果這些數(shù)據(jù)不是頻繁使用,用空間換時(shí)間不一定劃算。
4.3 一些重要的查詢
在Elasticsearch 中,有幾類(lèi)最重要的查詢子句,掌握了就可以覆蓋日常90%以上的需求。
4.3.1 match_all
{"match_all":{}}
表示取出所有documents,在與filter結(jié)合使用時(shí),會(huì)經(jīng)常使用match_all。
4.3.2 match
一般在全文檢索時(shí)使用,首先利用analyzer 對(duì)具體查詢字符串進(jìn)行分析,然后進(jìn)行查詢;如果是在數(shù)值型字段、日期類(lèi)型字段、布爾字段或not_analyzed 的字符串上進(jìn)行查詢時(shí),不對(duì)查詢字符串進(jìn)行分析,表示精確匹配,兩個(gè)簡(jiǎn)單的例子如:
{ "match": { "tweet": "About Search" }}
{ "match": { "age": 26 }}
4.3.3 term
term 用于精確查找,可用于數(shù)值、date、boolean值或not_analyzed string,當(dāng)使用term時(shí),不會(huì)對(duì)查詢字符串進(jìn)行分析,進(jìn)行的是精確查找。
{ "term": { "date": "2014-09-01" }}
4.3.4 terms
terms 和 term 類(lèi)似,但是,terms 里可以指定多個(gè)值,只要doc滿足terms 里的任意值,就是滿足查詢條件的。與term 相同,terms 也是用于精確查找。
{ "terms": { "tag": [ "search", "full_text", "nosql" ] }}
注意,terms 表示的是contains 關(guān)系,而不是 equals關(guān)系。
4.3.5 range
類(lèi)比數(shù)據(jù)庫(kù)查找的范圍查找,舉個(gè)簡(jiǎn)單的例子:
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
操作符可以是:
- gt:大于
- gte:大于等于
- lt:小于
- lte:小于等于
4.3.6 exists 和 missing
exists 用于查找字段含有一個(gè)或多個(gè)值的document,而missing用于查找某字段不存在值的document,可類(lèi)比關(guān)系數(shù)據(jù)庫(kù)里的 is not null (exists) 和 is null (missing).
{
"exists": {
"field": "title"
}
}
4.3.7 bool
前面講的都是些最原子的查詢子句,那么,怎么實(shí)現(xiàn)復(fù)合查詢呢?Elasticsearch 使用bool 子句來(lái)將各種子查詢關(guān)聯(lián)起來(lái),組成布爾表達(dá)式,bool 子句可以隨意組合、嵌套。
bool子句主要包括:
- must:表示必須匹配。
- must_not:表示一定不能匹配。
- should:表示可以匹配,類(lèi)似于布爾運(yùn)算里的"或"。如果bool 子句里,沒(méi)有must子句,那么,should子句里至少匹配一個(gè),如果有must子句,那么,should子句至少匹配零個(gè)。可以使用
minimum_should_match來(lái)對(duì)最小匹配數(shù)進(jìn)行設(shè)置。
{
"bool" : {
"must" : {
"term" : { "user" : "kimchy" }
},
"must_not" : {
"range" : {
"age" : { "from" : 10, "to" : 20 }
}
},
"should" : [
{
"term" : { "tag" : "wow" }
},
{
"term" : { "tag" : "elasticsearch" }
}
],
"minimum_should_match" : 1,
"boost" : 1.0
}
}
4.4 聚合功能
前面說(shuō)的都是 Elasticsearch 當(dāng)做搜索引擎使用,Elasticsearch 還可以作為分析引擎使用。
和 MySQL 等關(guān)系數(shù)據(jù)庫(kù)類(lèi)似,Elasticsearch 有聚合操作,而且,可作用于大量數(shù)據(jù),提供實(shí)時(shí)的分析結(jié)果,速度快;同時(shí),聚合操作可以與搜索結(jié)合使用,例如將聚合作用于搜索結(jié)果等。總之,Elasticsearch的聚合功能十分強(qiáng)大,有很多公司利用 Elasticsearch 來(lái)做分析,其中,廣泛使用的 ELK(Elasticsearch + Logstash + Kibana),Kibana的數(shù)據(jù)顯示和分析功能就是基于 Elasticsearch 的聚合功能做的。
具體可參看 Elasticsearch: The Definitive Guide
4.5 Geolocation
Elasticsearch 還提供了基于地理位置的搜索,而且能將地理位置與全文檢索、結(jié)構(gòu)化搜索、分析等結(jié)合起來(lái)使用,比如查找距離某點(diǎn)一定范圍內(nèi)的符合搜索條件的地點(diǎn)、計(jì)算兩點(diǎn)的距離、判斷兩個(gè)形狀是否相交或包含等。
具體參考 Elasticsearch: The Definitive Guide
5. Elasticsearch 使用時(shí)注意的幾個(gè)問(wèn)題
深度分頁(yè)問(wèn)題:Elasticsearch 作為一個(gè)分布式搜索與分析引擎,深度分頁(yè)問(wèn)題會(huì)帶來(lái)嚴(yán)重的問(wèn)題,給CPU、內(nèi)存、IO、網(wǎng)絡(luò)帶來(lái)巨大壓力,所以,在Elasticsearch 不建議使用深度分頁(yè),如果要遍歷數(shù)據(jù),可以采用 SCROLL的方式,可參考我另一篇博客。
排序問(wèn)題:根據(jù)某field排序時(shí),Elasticsearch 會(huì)將這個(gè) field 的所有值給加載到內(nèi)存,然后,這部分?jǐn)?shù)據(jù)會(huì)常駐內(nèi)存,如果數(shù)據(jù)量大或排序字段多,就會(huì)給系統(tǒng)帶來(lái)巨大壓力,所以,在使用 field 進(jìn)行排序時(shí),要慎重。不過(guò),在Elasticsearch 2.X版本,開(kāi)始使用 doc value 來(lái)優(yōu)化這部分。
terms 問(wèn)題: terms 里可以傳多個(gè)值,但是,量不能太多,搜索引擎的基本數(shù)據(jù)結(jié)構(gòu)是倒排索引,terms 里傳多個(gè)值,原理上來(lái)說(shuō)是查很多的倒排索引,量大了也會(huì)給系統(tǒng)帶來(lái)很大壓力。
6 總結(jié)
本文是一篇 Elasticsearch 的入門(mén)文章,涵蓋的是一些基本概念,篇幅有限,并不深入,如DSL的具體語(yǔ)法、聚合功能等都點(diǎn)到為止,希望大家知道的是Elasticsearch能干什么,具體要做的時(shí)候,再去詳查就好了。