原理
原文:https://www.cnblogs.com/dreamroute/p/8484457.html
簡(jiǎn)介
ElasticSearch:智能搜索,分布式的搜索引擎
Elasticsearch 是一個(gè)分布式可擴(kuò)展的實(shí)時(shí)搜索和分析引擎,一個(gè)建立在全文搜索引擎 Apache Lucene(TM) 基礎(chǔ)上的搜索引擎.當(dāng)然 Elasticsearch 并不僅僅是 Lucene 那么簡(jiǎn)單,它不僅包括了全文搜索功能,還可以進(jìn)行以下工作:
- 分布式實(shí)時(shí)文件存儲(chǔ),并將每一個(gè)字段都編入索引,使其可以被搜索。
- 實(shí)時(shí)分析的分布式搜索引擎。
- 可以擴(kuò)展到上百臺(tái)服務(wù)器,處理PB級(jí)別的結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)。
基本概念
全文檢索
全文檢索是指計(jì)算機(jī)索引程序通過(guò)掃描文章中的每一個(gè)詞,對(duì)每一個(gè)詞建立一個(gè)索引,指明該詞在文章中出現(xiàn)的次數(shù)和位置,當(dāng)用戶查詢時(shí),檢索程序就根據(jù)事先建立的索引進(jìn)行查找,并將查找的結(jié)果反饋給用戶的檢索方式。這個(gè)過(guò)程類似于通過(guò)字典中的檢索字表查字的過(guò)程。
全文檢索的方法主要分為按字檢索和按詞檢索兩種。按字檢索是指對(duì)于文章中的每一個(gè)字都建立索引,檢索時(shí)將詞分解為字的組合。對(duì)于各種不同的語(yǔ)言而言,字有不同的含義,比如英文中字與詞實(shí)際上是合一的,而中文中字與詞有很大分別。按詞檢索指對(duì)文章中的詞,即語(yǔ)義單位建立索引,檢索時(shí)按詞檢索,并且可以處理同義項(xiàng)等。英文等西方文字由于按照空白切分詞,因此實(shí)現(xiàn)上與按字處理類似,添加同義處理也很容易。中文等東方文字則需要切分字詞,以達(dá)到按詞索引的目的,關(guān)于這方面的問(wèn)題,是當(dāng)前全文檢索技術(shù)尤其是中文全文檢索技術(shù)中的難點(diǎn),在此不做詳述。
關(guān)系數(shù)據(jù)庫(kù) ? 數(shù)據(jù)庫(kù) ? 表 ? 行 ? 列(Columns)
Elasticsearch ? 索引(Index) ? 類型(type) ? 文檔(Docments) ? 字段(Fields)
index 索引(索引庫(kù))
我們?yōu)槭裁词褂肊S?因?yàn)橄氚褦?shù)據(jù)存進(jìn)去,然后再查詢出來(lái)。
我們?cè)谑褂肕ysql或者Oracle的時(shí)候,為了區(qū)分?jǐn)?shù)據(jù),我們會(huì)建立不同的數(shù)據(jù)庫(kù),庫(kù)下面還有表的。
其實(shí)ES功能就像一個(gè)關(guān)系型數(shù)據(jù)庫(kù),在這個(gè)數(shù)據(jù)庫(kù)我們可以往里面添加數(shù)據(jù),查詢數(shù)據(jù)。
ES中的索引非傳統(tǒng)索引的含義,ES中的索引是存放數(shù)據(jù)的地方,是ES中的一個(gè)概念詞匯
index類似于我們Mysql里面的一個(gè)數(shù)據(jù)庫(kù) create database user; 好比就是一個(gè)索引庫(kù)
type類型
類型是用來(lái)定義數(shù)據(jù)結(jié)構(gòu)的
在每一個(gè)index下面,可以有一個(gè)或者多個(gè)type,好比數(shù)據(jù)庫(kù)里面的一張表。
相當(dāng)于表結(jié)構(gòu)的描述,描述每個(gè)字段的類型。
document:文檔
文檔就是最終的數(shù)據(jù)了,可以認(rèn)為一個(gè)文檔就是一條記錄。
是ES里面最小的數(shù)據(jù)單元,就好比表里面的一條數(shù)據(jù)
Field 字段
好比關(guān)系型數(shù)據(jù)庫(kù)中列的概念,一個(gè)document有一個(gè)或者多個(gè)field組成。
一條數(shù)據(jù)
{
"name" : "John",
"sex" : "Male",
"age" : 25,
"birthDate": "1990/05/01",
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
Elasticsearch的文件存儲(chǔ),Elasticsearch是面向文檔型數(shù)據(jù)庫(kù),一條數(shù)據(jù)在這里就是一個(gè)文檔,用JSON作為文檔序列化的格式
索引
Elasticsearch索引的精髓:
一切設(shè)計(jì)都是為了提高搜索的性能
Elasticsearch是如何做到快速索引的
InfoQ那篇文章里說(shuō)Elasticsearch使用的倒排索引比關(guān)系型數(shù)據(jù)庫(kù)的B-Tree索引快,為什么呢?
什么是B-Tree索引?
上大學(xué)讀書(shū)時(shí)老師教過(guò)我們,二叉樹(shù)查找效率是logN,同時(shí)插入新的節(jié)點(diǎn)不必移動(dòng)全部節(jié)點(diǎn),所以用樹(shù)型結(jié)構(gòu)存儲(chǔ)索引,能同時(shí)兼顧插入和查詢的性能。因此在這個(gè)基礎(chǔ)上,再結(jié)合磁盤的讀取特性(順序讀/隨機(jī)讀),傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)采用了B-Tree/B+Tree這樣的數(shù)據(jù)結(jié)構(gòu):

什么是倒排索引?
典型的用空間換時(shí)間
一般數(shù)據(jù)庫(kù)

ES上的


繼續(xù)上面的例子,假設(shè)有這么幾條數(shù)據(jù)(為了簡(jiǎn)單,去掉about, interests這兩個(gè)field):
| ID | Name | Age | Sex |
| -- |:------------:| -----:| -----:|
| 1 | Kate | 24 | Female
| 2 | John | 24 | Male
| 3 | Bill | 29 | Male```
ID是Elasticsearch自建的文檔id,那么Elasticsearch建立的索引如下:
Name:
| Term | Posting List |
|---|---|
| Kate | 1 |
| John | 2 |
| Bill | 3 |
Age:
| Term | Posting List |
|---|---|
| 24 | [1,2] |
| 29 | 3 |
Sex:
| Term | Posting List |
|---|---|
| Female | 1 |
| Male | [2,3] ``` |
Posting List
Elasticsearch分別為每個(gè)field都建立了一個(gè)倒排索引,Kate, John, 24, Female這些叫term,而[1,2]就是Posting List。Posting list就是一個(gè)int的數(shù)組,存儲(chǔ)了所有符合某個(gè)term的文檔id。
看到這里,不要認(rèn)為就結(jié)束了,精彩的部分才剛開(kāi)始...
通過(guò)posting list這種索引方式似乎可以很快進(jìn)行查找,比如要找age=24的同學(xué),愛(ài)回答問(wèn)題的小明馬上就舉手回答:我知道,id是1,2的同學(xué)。但是,如果這里有上千萬(wàn)的記錄呢?如果是想通過(guò)name來(lái)查找呢?
Term Dictionary
Elasticsearch為了能快速找到某個(gè)term,將所有的term排個(gè)序,二分法查找term,logN的查找效率,就像通過(guò)字典查找一樣,這就是Term Dictionary?,F(xiàn)在再看起來(lái),似乎和傳統(tǒng)數(shù)據(jù)庫(kù)通過(guò)B-Tree的方式類似啊,為什么說(shuō)比B-Tree的查詢快呢?
Term Index
B-Tree通過(guò)減少磁盤尋道次數(shù)來(lái)提高查詢性能,Elasticsearch也是采用同樣的思路,直接通過(guò)內(nèi)存查找term,不讀磁盤,但是如果term太多,term dictionary也會(huì)很大,放內(nèi)存不現(xiàn)實(shí),于是有了Term Index,就像字典里的索引頁(yè)一樣,A開(kāi)頭的有哪些term,分別在哪頁(yè),可以理解term index是一顆樹(shù):

這棵樹(shù)不會(huì)包含所有的term,它包含的是term的一些前綴。通過(guò)term index可以快速地定位到term dictionary的某個(gè)offset,然后從這個(gè)位置再往后順序查找。

所以term index不需要存下所有的term,而僅僅是他們的一些前綴與Term Dictionary的block之間的映射關(guān)系,再結(jié)合FST(Finite State Transducers)的壓縮技術(shù),可以使term index緩存到內(nèi)存中。從term index查到對(duì)應(yīng)的term dictionary的block位置之后,再去磁盤上找term,大大減少了磁盤隨機(jī)讀的次數(shù)。
假設(shè)我們現(xiàn)在要將mop, moth, pop, star, stop and top(term index里的term前綴)映射到序號(hào):0,1,2,3,4,5(term dictionary的block位置)。最簡(jiǎn)單的做法就是定義個(gè)Map<string, integer="">,大家找到自己的位置對(duì)應(yīng)入座就好了,但從內(nèi)存占用少的角度想想,有沒(méi)有更優(yōu)的辦法呢?答案就是:FST

??表示一種狀態(tài)
-->表示狀態(tài)的變化過(guò)程,上面的字母/數(shù)字表示狀態(tài)變化和權(quán)重
將單詞分成單個(gè)字母通過(guò)??和-->表示出來(lái),0權(quán)重不顯示。如果??后面出現(xiàn)分支,就標(biāo)記權(quán)重,最后整條路徑上的權(quán)重加起來(lái)就是這個(gè)單詞對(duì)應(yīng)的序號(hào)
壓縮技巧
Elasticsearch里除了上面說(shuō)到用FST壓縮term index外,對(duì)posting list也有壓縮技巧。
增量編碼壓縮,將大數(shù)變小數(shù),按字節(jié)存儲(chǔ)

如果數(shù)學(xué)不是體育老師教的話,還是比較容易看出來(lái)這種壓縮技巧的。
原理就是通過(guò)增量,將原來(lái)的大數(shù)變成小數(shù)僅存儲(chǔ)增量值,再精打細(xì)算按bit排好隊(duì),最后通過(guò)字節(jié)存儲(chǔ),而不是大大咧咧的盡管是2也是用int(4個(gè)字節(jié))來(lái)存儲(chǔ)。
Roaring bitmaps
說(shuō)到Roaring bitmaps,就必須先從bitmap說(shuō)起。Bitmap是一種數(shù)據(jù)結(jié)構(gòu),假設(shè)有某個(gè)posting list:
[1,3,4,7,10]
對(duì)應(yīng)的bitmap就是:
[1,0,1,1,0,0,1,0,0,1]
非常直觀,用0/1表示某個(gè)值是否存在,比如10這個(gè)值就對(duì)應(yīng)第10位,對(duì)應(yīng)的bit值是1,這樣用一個(gè)字節(jié)就可以代表8個(gè)文檔id,舊版本(5.0之前)的Lucene就是用這樣的方式來(lái)壓縮的,但這樣的壓縮方式仍然不夠高效,如果有1億個(gè)文檔,那么需要12.5MB的存儲(chǔ)空間,這僅僅是對(duì)應(yīng)一個(gè)索引字段(我們往往會(huì)有很多個(gè)索引字段)。于是有人想出了Roaring bitmaps這樣更高效的數(shù)據(jù)結(jié)構(gòu)。
Bitmap的缺點(diǎn)是存儲(chǔ)空間隨著文檔個(gè)數(shù)線性增長(zhǎng),Roaring bitmaps需要打破這個(gè)魔咒就一定要用到某些指數(shù)特性:
將posting list按照65535為界限分塊,比如第一塊所包含的文檔id范圍在065535之間,第二塊的id范圍是65536131071,以此類推。再用<商,余數(shù)>的組合表示每一組id,這樣每組里的id范圍都在0~65535內(nèi)了,剩下的就好辦了,既然每組id不會(huì)變得無(wú)限大,那么我們就可以通過(guò)最有效的方式對(duì)這里的id存儲(chǔ)。

細(xì)心的小明這時(shí)候又舉手了:"為什么是以65535為界限?"
程序員的世界里除了1024外,65535也是一個(gè)經(jīng)典值,因?yàn)樗?2^16-1,正好是用2個(gè)字節(jié)能表示的最大數(shù),一個(gè)short的存儲(chǔ)單位,注意到上圖里的最后一行“If a block has more than 4096 values, encode as a bit set, and otherwise as a simple array using 2 bytes per value”,如果是大塊,用節(jié)省點(diǎn)用bitset存,小塊就豪爽點(diǎn),2個(gè)字節(jié)我也不計(jì)較了,用一個(gè)short[]存著方便。
那為什么用4096來(lái)區(qū)分大塊還是小塊呢?
個(gè)人理解:都說(shuō)程序員的世界是二進(jìn)制的,4096*2bytes = 8192bytes < 1KB, 磁盤一次尋道可以順序把一個(gè)小塊的內(nèi)容都讀出來(lái),再大一位就超過(guò)1KB了,需要兩次讀。
聯(lián)合索引
上面說(shuō)了半天都是單field索引,如果多個(gè)field索引的聯(lián)合查詢,倒排索引如何滿足快速查詢的要求呢?
利用跳表(Skip list)的數(shù)據(jù)結(jié)構(gòu)快速做“與”運(yùn)算,或者
利用上面提到的bitset按位“與”
先看看跳表的數(shù)據(jù)結(jié)構(gòu):

將一個(gè)有序鏈表level0,挑出其中幾個(gè)元素到level1及l(fā)evel2,每個(gè)level越往上,選出來(lái)的指針元素越少,查找時(shí)依次從高level往低查找,比如55,先找到level2的31,再找到level1的47,最后找到55,一共3次查找,查找效率和2叉樹(shù)的效率相當(dāng),但也是用了一定的空間冗余來(lái)?yè)Q取的。
假設(shè)有下面三個(gè)posting list需要聯(lián)合索引:

如果使用跳表,對(duì)最短的posting list中的每個(gè)id,逐個(gè)在另外兩個(gè)posting list中查找看是否存在,最后得到交集的結(jié)果。
如果使用bitset,就很直觀了,直接按位與,得到的結(jié)果就是最后的交集。
總結(jié)和思考
Elasticsearch的索引思路:
將磁盤里的東西盡量搬進(jìn)內(nèi)存,減少磁盤隨機(jī)讀取次數(shù)(同時(shí)也利用磁盤順序讀特性),結(jié)合各種奇技淫巧的壓縮算法,用及其苛刻的態(tài)度使用內(nèi)存。
所以,對(duì)于使用Elasticsearch進(jìn)行索引時(shí)需要注意:
- 不需要索引的字段,一定要明確定義出來(lái),因?yàn)?strong>默認(rèn)是自動(dòng)建索引的
- 同樣的道理,對(duì)于String類型的字段,不需要analysis的也需要明確定義出來(lái),因?yàn)槟J(rèn)也是會(huì)analysis的
- 選擇有規(guī)律的ID很重要,隨機(jī)性太大的ID(比如java的UUID)不利于查詢
語(yǔ)法
數(shù)據(jù)類型
原文:http://www.itdecent.cn/p/01f489c46c38
字段類型概述

整數(shù)類型

浮點(diǎn)類型

date類型
日期類型表示格式可以是以下幾種:
(1)日期格式的字符串,比如 “2018-01-13” 或 “2018-01-13 12:10:30”
(2)long類型的毫秒數(shù)( milliseconds-since-the-epoch,epoch就是指UNIX誕生的UTC時(shí)間1970年1月1日0時(shí)0分0秒)
(3)integer的秒數(shù)(seconds-since-the-epoch)
boolean類型
true和false
binary類型
進(jìn)制字段是指用base64來(lái)表示索引中存儲(chǔ)的二進(jìn)制數(shù)據(jù),可用來(lái)存儲(chǔ)二進(jìn)制形式的數(shù)據(jù),例如圖像。默認(rèn)情況下,該類型的字段只存儲(chǔ)不索引。二進(jìn)制類型只支持index_name屬性。
array類型
(1)字符數(shù)組: [ “one”, “two” ]
(2)整數(shù)數(shù)組: productid:[ 1, 2 ]
(3)對(duì)象(文檔)數(shù)組: “user”:[ { “name”: “Mary”, “age”: 12 }, { “name”: “John”, “age”: 10 }],
注意:lasticSearch不支持元素為多個(gè)數(shù)據(jù)類型:[ 10, “some string” ]
object類型
屬組類型:array
對(duì)象類型:object
嵌套類型:nested object
專用類型:
記錄IP地址 ip
實(shí)現(xiàn)自動(dòng)補(bǔ)全 completion
記錄分詞數(shù) token_count
記錄字符串hash值 murmur3
percolator
join
Mapping
1、enabled:僅存儲(chǔ)、不做搜索和聚合分析
"enabled":true (缺省)| false
2、index:是否構(gòu)建倒排索引(即是否分詞,設(shè)置false,字段將不會(huì)被索引)
"index": true(缺?。﹟ false
3、coerce:是否開(kāi)啟自動(dòng)數(shù)據(jù)類型轉(zhuǎn)換功能,比如:字符串轉(zhuǎn)數(shù)字,浮點(diǎn)轉(zhuǎn)整型
"coerce: true(缺?。﹟ false"
12、data_detection:是否自動(dòng)識(shí)別日期類型
"data_detection":true(缺省)| false
14、analyzer:指定分詞器,默認(rèn)分詞器為standard analyzer
"analyzer": "ik"
16、ignore_above:超過(guò)100個(gè)字符的文本,將會(huì)被忽略,不被索引
"ignore_above": 100
18、null_value:設(shè)置一些缺失字段的初始化,只有string可以使用,分詞字段的null值也會(huì)被分詞
"null_value": "NULL"
20、search_analyzer:設(shè)置搜索時(shí)的分詞器,默認(rèn)跟analyzer是一致的,比如index時(shí)用standard+ngram,搜索時(shí)用standard用來(lái)完成自動(dòng)提示功能
"search_analyzer": "ik"
Mapping 字段設(shè)置流程

自定義Mapping
自定義mapping的操作步驟如下:
- 寫入一條文檔到es的臨時(shí)索引中,獲取es自動(dòng)生成的mapping
- 修改步驟1得到的mapping,自定義相關(guān)配置
- 使用步驟2的mapping創(chuàng)建實(shí)際所需索引

mapping中的字段類型一旦設(shè)置,禁止直接修改,因?yàn)?lucene實(shí)現(xiàn)的倒排索引生成后不允許修改,應(yīng)該重新建立新的索引,然后做reindex操作。
但是可以新增字段,通過(guò) dynamic 參數(shù)來(lái)控制字段的新增,這個(gè)參數(shù)的值如下:
true:默認(rèn)值,表示允許選自動(dòng)新增字段
false:不允許自動(dòng)新增字段,但是文檔可以正常寫入,但無(wú)法對(duì)字段進(jìn)行查詢等操作
strict:嚴(yán)格模式,文檔不能寫入,報(bào)錯(cuò)
例子
#創(chuàng)建索引(設(shè)置字段類型)
PUT jobbole #創(chuàng)建索引設(shè)置索引名稱
{
"mappings": { #設(shè)置mappings映射字段類型
"job": { #表名稱
"properties": { #設(shè)置字段類型
"title":{ #title字段
"type": "text", #text類型,text類型分詞,建立倒排索
"fields": { #fields 來(lái)指定 一個(gè) 不分詞的原文
"keyword": {
"type": "keyword" # 用于 排序聚合
}
}
},
"salary_min":{ #salary_min字段
"type": "integer" #integer數(shù)字類型
},
"city":{ #city字段
"type": "keyword" #keyword普通字符串類型
},
"company":{ #company字段,是嵌套字段
"properties":{ #設(shè)置嵌套字段類型
"name":{ #name字段
"type":"text" #text類型
},
"company_addr":{ #company_addr字段
"type":"text" #text類型
},
"employee_count":{ #employee_count字段
"type":"integer" #integer數(shù)字類型
}
}
},
"publish_date":{ #publish_date字段
"type": "date", #date時(shí)間類型
"format":"yyyy-MM-dd" #yyyy-MM-dd格式化時(shí)間樣式
},
"comments":{ #comments字段
"type": "integer" #integer數(shù)字類型
}
}
}
}
}
index
控制當(dāng)前字段是否索引,默認(rèn)為true,即記錄索引,false不記錄,即不可搜索
PUT myindex
{
"mappings": {
"doc": {
"properties": {
"cookie": {
"type": "text",
"index":false
}
}
}
}
}
index_options
用于控制倒排索引記錄的內(nèi)容,有如下4種配置
docs: 只記錄doc id
freqs: 記錄doc id 和term frequencies
positions: 記錄doc id、term frequencies和term position
offsets: 記錄doc id、term frequencies、term position和character offsets
PUT myindex1
{
"mappings": {
"doc": {
"properties": {
"cookie": {
"type": "text",
"index_options": "offsets"
}
}
}
}
}
null_value
當(dāng)字段遇到null值時(shí)的處理策略,默認(rèn)為null,即空值,此時(shí)es會(huì)忽略該值??梢酝ㄟ^(guò)設(shè)定該值設(shè)定字段的默認(rèn)值
PUT myindex1
{
"mappings": {
"doc": {
"properties": {
"status_code": {
"type": "keyword",
"null_value": "NULL"
}
}
}
}
}
中文分詞
https://blog.csdn.net/kkkloveyou/article/details/78210139
{
"properties":{
"answer":{
"type":"text",
"analyzer":"ik_max_word",
"search_analyzer":"ik_smart"
},
"question":{
"type":"text",
"analyzer":"ik_max_word",
"search_analyzer":"ik_smart"
},
}
}
查看mapping
news/_mapping/foshan # index/_mapping/type
操作
GET _cat/health 查看集群的健康狀況
GET _all
PUT 類似于SQL中的增
DELETE 類似于SQL中的刪
POST 類似于SQL中的改
GET 類似于SQL中的查
返回結(jié)果

took:耗費(fèi)了6毫秒
shards:分片的情況
hits:獲取到的數(shù)據(jù)的情況
total:3 總的數(shù)據(jù)條數(shù)
max_score:1 所有數(shù)據(jù)里面打分最高的分?jǐn)?shù)
hits里面:
_index:"ecommerce" index名稱
_type:"product" type的名稱
_id:"2" id號(hào)
_score:1 分?jǐn)?shù),這個(gè)分?jǐn)?shù)越大越靠前出來(lái),百度也是這樣。除非是花錢。否則匹配度越高越靠前
