Elasticsearch 介紹
概要
Elasticsearch 是一個(gè)分布式搜索引擎,底層基于 Lucene 實(shí)現(xiàn)。Elasticsearch 屏蔽了 Lucene 的底層細(xì)節(jié),提供了分布式特性,同時(shí)對(duì)外提供了 Restful API。Elasticsearch 以其易用性迅速贏得了許多用戶,被用在網(wǎng)站搜索、日志分析等諸多方面。由于 ES 強(qiáng)大的橫向擴(kuò)展能力,甚至很多人也會(huì)直接把 ES 當(dāng)做 NoSQL 來(lái)用。
基本概念
全文搜索(Full-text Search)
全文檢索是指計(jì)算機(jī)索引程序通過(guò)掃描文章中的每一個(gè)詞,對(duì)每一個(gè)詞建立一個(gè)索引,指明該詞在文章中出現(xiàn)的次數(shù)和位置,當(dāng)用戶查詢時(shí),檢索程序就根據(jù)事先建立的索引進(jìn)行查找,并將查找的結(jié)果反饋給用戶的檢索方式。
?在全文搜索的世界中,存在著幾個(gè)龐大的帝國(guó),也就是主流工具,主要有:
- Apache Lucene
- Elasticsearch
- Solr
- Ferret
倒排索引(Inverted Index)
該索引表中的每一項(xiàng)都包括一個(gè)屬性值和具有該屬性值的各記錄的地址。由于不是由記錄來(lái)確定屬性值,而是由屬性值來(lái)確定記錄的位置,因而稱為倒排索引(inverted index)。Elasticsearch能夠?qū)崿F(xiàn)快速、高效的搜索功能,正是基于倒排索引原理。
節(jié)點(diǎn) & 集群(Node & Cluster)
Elasticsearch 本質(zhì)上是一個(gè)分布式數(shù)據(jù)庫(kù),允許多臺(tái)服務(wù)器協(xié)同工作,每臺(tái)服務(wù)器可以運(yùn)行多個(gè)Elasticsearch實(shí)例。單個(gè)Elasticsearch實(shí)例稱為一個(gè)節(jié)點(diǎn)(Node),一組節(jié)點(diǎn)構(gòu)成一個(gè)集群(Cluster)。
Document (文檔)
文檔指的是用戶提交給 ES 的一條數(shù)據(jù)。需要注意的是,這里的文檔并非指的是一個(gè)純字符串文本,在 ES 中文檔指的是一條 JSON 數(shù)據(jù)。如果對(duì) MongoDB 有了解的話,這里文檔的含義和 MongoDB 中的基本類似。
JSON 數(shù)據(jù)中可以包含多個(gè)字段,這些字段可以類比為 MySQL 中每個(gè)表的字段。
例如:
{
"message": "this is my blog",
"author": "cyhone"
}
這樣我們后期進(jìn)行搜索和查詢的時(shí)候,也可以分別針對(duì) message 字段和 author 字段進(jìn)行搜索。
Index (索引)
Index(索引) 可以理解為是文檔的集合,同在一個(gè)索引中的文檔共同建立倒排索引。
也有很多人會(huì)把索引類比于 MySQL 中 schema 的概念。但在 ES 中 Index 更加靈活,用起來(lái)也更加方便。
此外,提交給同一個(gè)索引中的文檔,最好擁有相同的結(jié)構(gòu)。這樣對(duì)于 ES 來(lái)說(shuō),不管是存儲(chǔ)還是查詢,都更容易優(yōu)化。
類型(Type)
Document 可以分組,比如employee這個(gè) Index 里面,可以按部門分組,也可以按職級(jí)分組。這種分組就叫做 Type,它是虛擬的邏輯分組,用來(lái)過(guò)濾 Document,類似關(guān)系型數(shù)據(jù)庫(kù)中的數(shù)據(jù)表。
?不同的 Type 應(yīng)該有相似的結(jié)構(gòu)(Schema),性質(zhì)完全不同的數(shù)據(jù)(比如 products 和 logs)應(yīng)該存成兩個(gè) Index,而不是一個(gè) Index 里面的兩個(gè) Type(雖然可以做到)。
文檔元數(shù)據(jù)(Document metadata)
文檔元數(shù)據(jù)為_index, _type, _id, 這三者可以唯一表示一個(gè)文檔,_index表示文檔在哪存放,_type表示文檔的對(duì)象類別,_id為文檔的唯一標(biāo)識(shí)。
字段(Fields)
每個(gè)Document都類似一個(gè)JSON結(jié)構(gòu),它包含了許多字段,每個(gè)字段都有其對(duì)應(yīng)的值,多個(gè)字段組成了一個(gè) Document,可以類比關(guān)系型數(shù)據(jù)庫(kù)數(shù)據(jù)表中的字段。
?在 Elasticsearch 中,文檔(Document)歸屬于一種類型(Type),而這些類型存在于索引(Index)中,下圖展示了Elasticsearch與傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)的類比:

整體結(jié)構(gòu)
下圖是ES的主要結(jié)構(gòu)圖:

包含了這些模塊:
Transport Client/Node Client/REST API:三種訪問(wèn)es集群的方式
Transport(Netty):通信模塊,數(shù)據(jù)傳輸,底層采用netty框架
Index、Search…:支持搜索,索引等常用操作
Discovery:節(jié)點(diǎn)發(fā)現(xiàn),集群之間通信的基石
Plugins:很多服務(wù)以插件形式提供,官方和社區(qū)支持的ik、head、river、discovery gce…
Script:提供腳本支持,內(nèi)置painless,groovy等,默認(rèn)painless
Store/Snapshot:文件存儲(chǔ)與訪問(wèn),快照創(chuàng)建和恢復(fù)
translog、cluster state、segments:es主要文件類型,其中translog、cluster state是es添加的數(shù)據(jù),多個(gè)segments段組成一個(gè)完整的lucene索引
Monitor:監(jiān)控模塊,監(jiān)控jvm,文件系統(tǒng),操作系統(tǒng)等運(yùn)行情況
File System:es支持可以在多種文件系統(tǒng)上運(yùn)行,本地、共享型、HDFS、亞馬遜云平臺(tái)等
相關(guān)原理
ES的RESTful
ElasticSearch提供了易用但功能強(qiáng)大的RESTful API以用于與集群進(jìn)行交互,這些API大體可分為如下四類:
- 檢查集群、節(jié)點(diǎn)、索引等健康與否,以及獲取其相關(guān)狀態(tài)與統(tǒng)計(jì)信息;
- 管理集群、節(jié)點(diǎn)、索引數(shù)據(jù)及元數(shù)據(jù);
- 執(zhí)行CRUD操作及搜索操作;
- 執(zhí)行高級(jí)搜索操作,例如paging、filtering、scripting、faceting、aggregations及其它操作;
ES的讀寫原理
下圖為ES的讀寫原理流程圖:

ES寫數(shù)據(jù)過(guò)程
- 客戶端隨機(jī)選擇一個(gè)node發(fā)送請(qǐng)求過(guò)去,這個(gè)node就是coordinating node(協(xié)調(diào)節(jié)點(diǎn))
- coordinating node,對(duì)document進(jìn)行路由,將請(qǐng)求轉(zhuǎn)發(fā)給對(duì)應(yīng)的node(有primary shard)
- 實(shí)際的node上的primary shard處理請(qǐng)求,然后將數(shù)據(jù)同步到replica node
- coordinating node,如果發(fā)現(xiàn)primary node和所有replica node都搞定之后,就返回響應(yīng)結(jié)果給客戶端
在寫primary的過(guò)程中同時(shí)還要持久到本地 :
先寫入buffer,在buffer里的時(shí)候數(shù)據(jù)是搜索不到的;同時(shí)將數(shù)據(jù)寫入translog日志文件
如果buffer快滿了,或者到一定時(shí)間,就會(huì)將buffer數(shù)據(jù)refresh到一個(gè)新的segment file中,但是此時(shí)數(shù)據(jù)不是直接進(jìn)入segment file的磁盤文件的,而是先進(jìn)入os cache的。這個(gè)過(guò)程就是refresh。每隔1秒鐘,es將buffer中的數(shù)據(jù)寫入一個(gè)新的segment file,每秒鐘會(huì)產(chǎn)生一個(gè)新的磁盤文件,segment file,這個(gè)segment file中就存儲(chǔ)最近1秒內(nèi)buffer中寫入的數(shù)據(jù)。但是如果buffer里面此時(shí)沒(méi)有數(shù)據(jù),那當(dāng)然不會(huì)執(zhí)行refresh操作咯,每秒創(chuàng)建換一個(gè)空的segment file,如果buffer里面有數(shù)據(jù),默認(rèn)1秒鐘執(zhí)行一次refresh操作,刷入一個(gè)新的segment file中。操作系統(tǒng)里面,磁盤文件其實(shí)都有一個(gè)東西,叫做os cache,操作系統(tǒng)緩存,就是說(shuō)數(shù)據(jù)寫入磁盤文件之前,會(huì)先進(jìn)入os cache,先進(jìn)入操作系統(tǒng)級(jí)別的一個(gè)內(nèi)存緩存中去。只要buffer中的數(shù)據(jù)被refresh操作,刷入os cache中,就代表這個(gè)數(shù)據(jù)就可以被搜索到了。為什么叫es是準(zhǔn)實(shí)時(shí)的?NRT,near real-time,準(zhǔn)實(shí)時(shí)。默認(rèn)是每隔1秒refresh一次的,所以es是準(zhǔn)實(shí)時(shí)的,因?yàn)閷懭氲臄?shù)據(jù)1秒之后才能被看到。可以通過(guò)es的restful api或者java api,手動(dòng)執(zhí)行一次refresh操作,就是手動(dòng)將buffer中的數(shù)據(jù)刷入os cache中,讓數(shù)據(jù)立馬就可以被搜索到。只要數(shù)據(jù)被輸入os cache中,buffer就會(huì)被清空了,因?yàn)椴恍枰A鬮uffer了,數(shù)據(jù)在translog里面已經(jīng)持久化到磁盤去一份了
只要數(shù)據(jù)進(jìn)入os cache,此時(shí)就可以讓這個(gè)segment file的數(shù)據(jù)對(duì)外提供搜索了
重復(fù)1~3步驟,新的數(shù)據(jù)不斷進(jìn)入buffer和translog,不斷將buffer數(shù)據(jù)寫入一個(gè)又一個(gè)新的segment file中去,每次refresh完buffer清空,translog保留。隨著這個(gè)過(guò)程推進(jìn),translog會(huì)變得越來(lái)越大。當(dāng)translog達(dá)到一定長(zhǎng)度的時(shí)候,就會(huì)觸發(fā)commit操作。buffer中的數(shù)據(jù),倒是好,每隔1秒就被刷到os cache中去,然后這個(gè)buffer就被清空了。所以說(shuō)這個(gè)buffer的數(shù)據(jù)始終是可以保持住不會(huì)填滿es進(jìn)程的內(nèi)存的。每次一條數(shù)據(jù)寫入buffer,同時(shí)會(huì)寫入一條日志到translog日志文件中去,所以這個(gè)translog日志文件是不斷變大的,當(dāng)translog日志文件大到一定程度的時(shí)候,就會(huì)執(zhí)行commit操作。
commit操作發(fā)生第一步,就是將buffer中現(xiàn)有數(shù)據(jù)refresh到os cache中去,清空buffer
將一個(gè)commit point寫入磁盤文件,里面標(biāo)識(shí)著這個(gè)commit point對(duì)應(yīng)的所有segment file
強(qiáng)行將os cache中目前所有的數(shù)據(jù)都fsync到磁盤文件中去,translog日志文件的作用是什么?就是在你執(zhí)行commit操作之前,數(shù)據(jù)要么是停留在buffer中,要么是停留在os cache中,無(wú)論是buffer還是os cache都是內(nèi)存,一旦這臺(tái)機(jī)器死了,內(nèi)存中的數(shù)據(jù)就全丟了。所以需要將數(shù)據(jù)對(duì)應(yīng)的操作寫入一個(gè)專門的日志文件,translog日志文件中,一旦此時(shí)機(jī)器宕機(jī),再次重啟的時(shí)候,es會(huì)自動(dòng)讀取translog日志文件中的數(shù)據(jù),恢復(fù)到內(nèi)存buffer和os cache中去。commit操作:1、寫commit point;2、將os cache數(shù)據(jù)fsync強(qiáng)刷到磁盤上去;3、清空translog日志文件
將現(xiàn)有的translog清空,然后再次重啟啟用一個(gè)translog,此時(shí)commit操作完成。默認(rèn)每隔30分鐘會(huì)自動(dòng)執(zhí)行一次commit,但是如果translog過(guò)大,也會(huì)觸發(fā)commit。整個(gè)commit的過(guò)程,叫做flush操作。我們可以手動(dòng)執(zhí)行flush操作,就是將所有os cache數(shù)據(jù)刷到磁盤文件中去。不叫做commit操作,flush操作。es中的flush操作,就對(duì)應(yīng)著commit的全過(guò)程。我們也可以通過(guò)es api,手動(dòng)執(zhí)行flush操作,手動(dòng)將os cache中的數(shù)據(jù)fsync強(qiáng)刷到磁盤上去,記錄一個(gè)commit point,清空translog日志文件。
translog其實(shí)也是先寫入os cache的,默認(rèn)每隔5秒刷一次到磁盤中去,所以默認(rèn)情況下,可能有5秒的數(shù)據(jù)會(huì)僅僅停留在buffer或者translog文件的os cache中,如果此時(shí)機(jī)器掛了,會(huì)丟失5秒鐘的數(shù)據(jù)。但是這樣性能比較好,最多丟5秒的數(shù)據(jù)。也可以將translog設(shè)置成每次寫操作必須是直接fsync到磁盤,但是性能會(huì)差很多。
如果是刪除操作,commit的時(shí)候會(huì)生成一個(gè).del文件,里面將某個(gè)doc標(biāo)識(shí)為deleted狀態(tài),那么搜索的時(shí)候根據(jù).del文件就知道這個(gè)doc被刪除了
如果是更新操作,就是將原來(lái)的doc標(biāo)識(shí)為deleted狀態(tài),然后新寫入一條數(shù)據(jù)
buffer每次refresh一次,就會(huì)產(chǎn)生一個(gè)segment file,所以默認(rèn)情況下是1秒鐘一個(gè)segment file,segment file會(huì)越來(lái)越多,此時(shí)會(huì)定期執(zhí)行merge
每次merge的時(shí)候,會(huì)將多個(gè)segment file合并成一個(gè),同時(shí)這里會(huì)將標(biāo)識(shí)為deleted的doc給物理刪除掉,然后將新的segment file寫入磁盤,這里會(huì)寫一個(gè)commit point,標(biāo)識(shí)所有新的segment file,然后打開segment file供搜索使用,同時(shí)刪除舊的segment file。es里的寫流程,有4個(gè)底層的核心概念,refresh、flush、translog、merge。當(dāng)segment file多到一定程度的時(shí)候,es就會(huì)自動(dòng)觸發(fā)merge操作,將多個(gè)segment file給merge成一個(gè)segment file。
ES讀數(shù)據(jù)過(guò)程
查詢,GET某一條數(shù)據(jù),寫入了某個(gè)document,這個(gè)document會(huì)自動(dòng)給你分配一個(gè)全局唯一的id,doc id,同時(shí)也是根據(jù)doc id進(jìn)行hash路由到對(duì)應(yīng)的primary shard上面去。也可以手動(dòng)指定doc id,比如用訂單id,用戶id。
你可以通過(guò)doc id來(lái)查詢,會(huì)根據(jù)doc id進(jìn)行hash,判斷出來(lái)當(dāng)時(shí)把doc id分配到了哪個(gè)shard上面去,從那個(gè)shard去查詢。
客戶端發(fā)送請(qǐng)求到任意一個(gè)node,成為coordinate node
coordinate node對(duì)document進(jìn)行路由,將請(qǐng)求轉(zhuǎn)發(fā)到對(duì)應(yīng)的node,此時(shí)會(huì)使用round-robin隨機(jī)輪詢算法,在primary shard以及其所有replica中隨機(jī)選擇一個(gè),讓讀請(qǐng)求負(fù)載均衡
接收請(qǐng)求的node返回document給coordinate node
coordinate node返回document給客戶端
ES搜索數(shù)據(jù)過(guò)程
- 客戶端發(fā)送請(qǐng)求到一個(gè)coordinate node
- 協(xié)調(diào)節(jié)點(diǎn)將搜索請(qǐng)求轉(zhuǎn)發(fā)到所有的shard對(duì)應(yīng)的primary shard或replica shard也可以
- query phase:每個(gè)shard將自己的搜索結(jié)果(其實(shí)就是一些doc id),返回給協(xié)調(diào)節(jié)點(diǎn),由協(xié)調(diào)節(jié)點(diǎn)進(jìn)行數(shù)據(jù)的合并、排序、分頁(yè)等操作,產(chǎn)出最終結(jié)果
- fetch phase:接著由協(xié)調(diào)節(jié)點(diǎn),根據(jù)doc id去各個(gè)節(jié)點(diǎn)上拉取實(shí)際的document數(shù)據(jù),最終返回給客戶端