es的索引搜索是以lucene為底層的,但是lucene是沒有實(shí)現(xiàn)分布式,lucene提供了核心的索引和搜索引擎,ES則提供分布式和高可用。
lucene的工作原理可以見下面的這一篇博文:
ElasticSearch工作原理與插件
ElasticSearch 是一個(gè)基于lucene構(gòu)建的開源的,分布式的,restFul全文本搜索引擎。
1. 基本概念
Index(索引)
ES中的索引類似于傳統(tǒng)數(shù)據(jù)庫中的數(shù)據(jù)庫,ES中數(shù)據(jù)存儲(chǔ)于索引中,索引是具有類似特性的文檔的集合。
Type(類型)
類型類似于傳統(tǒng)庫中的表,類型是索引內(nèi)部的邏輯分區(qū),類型就是為那些擁有相同的域的文檔做的預(yù)定義。
document(文檔)
文檔是Lucene索引和搜索的原子單位,它是包含了一個(gè)或多個(gè)域的容器,基于JSON格式進(jìn)行表示。
mapping(映射)
所有的文檔在存儲(chǔ)之前都要首先進(jìn)行分析。用戶可根據(jù)需要定義如何將文本分割成token,哪些token應(yīng)該被過濾掉,以及哪些文本需要進(jìn)行額外處理等等。
shard(分片),replica(副本)
將一個(gè)索引內(nèi)部的數(shù)據(jù)分布地存儲(chǔ)于多個(gè)節(jié)點(diǎn),它通過將一個(gè)索引切分為多個(gè)底層物理的Lucene索引完成索引數(shù)據(jù)的分割存儲(chǔ)功能,這每一個(gè)物理的Lucene索引稱為一個(gè)分片(shard)。
每個(gè)分片其內(nèi)部都是一個(gè)全功能且獨(dú)立的索引,因此可由集群中的任何主機(jī)存儲(chǔ)。創(chuàng)建索引時(shí),用戶可指定其分片的數(shù)量,默認(rèn)數(shù)量為5個(gè)。
Shard有兩種類型:primary和replica,即主shard及副本shard。
Primary shard用于文檔存儲(chǔ),每個(gè)新的索引會(huì)自動(dòng)創(chuàng)建5個(gè)Primary shard,當(dāng)然此數(shù)量可在索引創(chuàng)建之前通過配置自行定義,不過,一旦創(chuàng)建完成,其Primary shard的數(shù)量將不可更改。
Replica shard是Primary Shard的副本,用于冗余數(shù)據(jù)及提高搜索性能。
每個(gè)Primary shard默認(rèn)配置了一個(gè)Replica shard,但也可以配置多個(gè),且其數(shù)量可動(dòng)態(tài)更改。ES會(huì)根據(jù)需要自動(dòng)增加或減少這些Replica shard的數(shù)量。
ES集群可由多個(gè)節(jié)點(diǎn)組成,各Shard分布式地存儲(chǔ)于這些節(jié)點(diǎn)上。
ES可自動(dòng)在節(jié)點(diǎn)間按需要移動(dòng)shard,例如增加節(jié)點(diǎn)或節(jié)點(diǎn)故障時(shí)。簡(jiǎn)而言之,分片實(shí)現(xiàn)了集群的分布式存儲(chǔ),而副本實(shí)現(xiàn)了其分布式處理及冗余功能。
ES的restFul
ElasticSearch提供了易用但功能強(qiáng)大的RESTful API以用于與集群進(jìn)行交互,這些API大體可分為如下四類:
(1) 檢查集群、節(jié)點(diǎn)、索引等健康與否,以及獲取其相關(guān)狀態(tài)與統(tǒng)計(jì)信息;
(2) 管理集群、節(jié)點(diǎn)、索引數(shù)據(jù)及元數(shù)據(jù);
(3) 執(zhí)行CRUD操作及搜索操作;
(4) 執(zhí)行高級(jí)搜索操作,例如paging、filtering、scripting、faceting、aggregations及其它操作;
ES的讀寫原理

(1)es寫數(shù)據(jù)過程
1)客戶端隨機(jī)選擇一個(gè)node發(fā)送請(qǐng)求過去,這個(gè)node就是coordinating node(協(xié)調(diào)節(jié)點(diǎn))
2)coordinating node,對(duì)document進(jìn)行路由,將請(qǐng)求轉(zhuǎn)發(fā)給對(duì)應(yīng)的node(有primary shard)
3)實(shí)際的node上的primary shard處理請(qǐng)求,然后將數(shù)據(jù)同步到replica node
4)coordinating node,如果發(fā)現(xiàn)primary node和所有replica node都搞定之后,就返回響應(yīng)結(jié)果給客戶端
在寫primary的過程中同時(shí)還要持久到本地 :
1)先寫入buffer,在buffer里的時(shí)候數(shù)據(jù)是搜索不到的;同時(shí)將數(shù)據(jù)寫入translog日志文件
2)如果buffer快滿了,或者到一定時(shí)間,就會(huì)將buffer數(shù)據(jù)refresh到一個(gè)新的segment file中,但是此時(shí)數(shù)據(jù)不是直接進(jìn)入segment file的磁盤文件的,而是先進(jìn)入os cache的。這個(gè)過程就是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í)沒有數(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)緩存,就是說數(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秒之后才能被看到。
可以通過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)持久化到磁盤去一份了
3)只要數(shù)據(jù)進(jìn)入os cache,此時(shí)就可以讓這個(gè)segment file的數(shù)據(jù)對(duì)外提供搜索了
4)重復(fù)1~3步驟,新的數(shù)據(jù)不斷進(jìn)入buffer和translog,不斷將buffer數(shù)據(jù)寫入一個(gè)又一個(gè)新的segment file中去,每次refresh完buffer清空,translog保留。隨著這個(gè)過程推進(jìn),translog會(huì)變得越來越大。當(dāng)translog達(dá)到一定長(zhǎng)度的時(shí)候,就會(huì)觸發(fā)commit操作。
buffer中的數(shù)據(jù),倒是好,每隔1秒就被刷到os cache中去,然后這個(gè)buffer就被清空了。所以說這個(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操作。
5)commit操作發(fā)生第一步,就是將buffer中現(xiàn)有數(shù)據(jù)refresh到os cache中去,清空buffer
6)將一個(gè)commit point寫入磁盤文件,里面標(biāo)識(shí)著這個(gè)commit point對(duì)應(yīng)的所有segment file
7)強(qiáng)行將os cache中目前所有的數(shù)據(jù)都fsync到磁盤文件中去
translog日志文件的作用是什么?就是在你執(zhí)行commit操作之前,數(shù)據(jù)要么是停留在buffer中,要么是停留在os cache中,無論是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日志文件
8)將現(xiàn)有的translog清空,然后再次重啟啟用一個(gè)translog,此時(shí)commit操作完成。默認(rèn)每隔30分鐘會(huì)自動(dòng)執(zhí)行一次commit,但是如果translog過大,也會(huì)觸發(fā)commit。整個(gè)commit的過程,叫做flush操作。我們可以手動(dòng)執(zhí)行flush操作,就是將所有os cache數(shù)據(jù)刷到磁盤文件中去。
不叫做commit操作,flush操作。es中的flush操作,就對(duì)應(yīng)著commit的全過程。我們也可以通過es api,手動(dòng)執(zhí)行flush操作,手動(dòng)將os cache中的數(shù)據(jù)fsync強(qiáng)刷到磁盤上去,記錄一個(gè)commit point,清空translog日志文件。
9)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ì)差很多。
實(shí)際上你在這里,如果面試官?zèng)]有問你es丟數(shù)據(jù)的問題,你可以在這里給面試官炫一把,你說,其實(shí)es第一是準(zhǔn)實(shí)時(shí)的,數(shù)據(jù)寫入1秒后可以搜索到;可能會(huì)丟失數(shù)據(jù)的,你的數(shù)據(jù)有5秒的數(shù)據(jù),停留在buffer、translog os cache、segment file os cache中,有5秒的數(shù)據(jù)不在磁盤上,此時(shí)如果宕機(jī),會(huì)導(dǎo)致5秒的數(shù)據(jù)丟失。
如果你希望一定不能丟失數(shù)據(jù)的話,你可以設(shè)置個(gè)參數(shù),官方文檔,百度一下。每次寫入一條數(shù)據(jù),都是寫入buffer,同時(shí)寫入磁盤上的translog,但是這會(huì)導(dǎo)致寫性能、寫入吞吐量會(huì)下降一個(gè)數(shù)量級(jí)。本來一秒鐘可以寫2000條,現(xiàn)在你一秒鐘只能寫200條,都有可能。
10)如果是刪除操作,commit的時(shí)候會(huì)生成一個(gè).del文件,里面將某個(gè)doc標(biāo)識(shí)為deleted狀態(tài),那么搜索的時(shí)候根據(jù).del文件就知道這個(gè)doc被刪除了
11)如果是更新操作,就是將原來的doc標(biāo)識(shí)為deleted狀態(tài),然后新寫入一條數(shù)據(jù)
12)buffer每次refresh一次,就會(huì)產(chǎn)生一個(gè)segment file,所以默認(rèn)情況下是1秒鐘一個(gè)segment file,segment file會(huì)越來越多,此時(shí)會(huì)定期執(zhí)行merge
13)每次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。
(2)es讀數(shù)據(jù)過程
查詢,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。
你可以通過doc id來查詢,會(huì)根據(jù)doc id進(jìn)行hash,判斷出來當(dāng)時(shí)把doc id分配到了哪個(gè)shard上面去,從那個(gè)shard去查詢
1)客戶端發(fā)送請(qǐng)求到任意一個(gè)node,成為coordinate node
2)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ù)載均衡
3)接收請(qǐng)求的node返回document給coordinate node
4)coordinate node返回document給客戶端
(3)es搜索數(shù)據(jù)過程
1)客戶端發(fā)送請(qǐng)求到一個(gè)coordinate node
2)協(xié)調(diào)節(jié)點(diǎn)將搜索請(qǐng)求轉(zhuǎn)發(fā)到所有的shard對(duì)應(yīng)的primary shard或replica shard也可以
3)query phase:每個(gè)shard將自己的搜索結(jié)果(其實(shí)就是一些doc id),返回給協(xié)調(diào)節(jié)點(diǎn),由協(xié)調(diào)節(jié)點(diǎn)進(jìn)行數(shù)據(jù)的合并、排序、分頁等操作,產(chǎn)出最終結(jié)果
4)fetch phase:接著由協(xié)調(diào)節(jié)點(diǎn),根據(jù)doc id去各個(gè)節(jié)點(diǎn)上拉取實(shí)際的document數(shù)據(jù),最終返回給客戶端
(4)搜索的底層原理,倒排索引,畫圖說明傳統(tǒng)數(shù)據(jù)庫和倒排索引的區(qū)別