前言
文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是現(xiàn)在
Tips
面試指南系列,很多情況下不會去深挖細(xì)節(jié),是小六六以被面試者的角色去回顧知識的一種方式,所以我默認(rèn)大部分的東西,作為面試官的你,肯定是懂的。
https://www.processon.com/view/link/600ed9e9637689349038b0e4
上面的是腦圖地址
叨絮
今天來看看Elasticsearch
然后下面是前面的文章匯總
- 2021-Java后端工程師面試指南-(引言)
- 2021-Java后端工程師面試指南-(Java基礎(chǔ)篇)
- 2021-Java后端工程師面試指南-(并發(fā)-多線程)
- 2021-Java后端工程師面試指南-(JVM)
- 2021-Java后端工程師面試指南-(MySQL)
- 2021-Java后端工程師面試指南-(Redis)
Es其實用的很多,而且如果體量大點的話,基本上都需要使用到它,所以掌握它還是很有必要的,那么我們來一起看看吧
說說什么是Elasticsearch
- Elasticsearch,基于lucene.分布式的Restful實時搜索和分析引擎(實時)
- 分布式的實時文件存儲,每個字段都被索引并可被搜索
- 高擴(kuò)展性,可擴(kuò)展至上百臺服務(wù)器,處理PB級結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)
- Elasticsearch用于全文檢索,結(jié)構(gòu)化搜索,分析/合并使用
聊聊Elasticsearch的特性:
- Elasticsearch沒有典型意義的事務(wù)(無事務(wù)性)
- Elasticsearch是一種面向文檔的數(shù)據(jù)庫
- Elasticsearch沒有提供授權(quán)和認(rèn)證特性
什么是全文檢索和Lucene?
全文檢索,倒排索引
全文檢索是指計算機(jī)索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現(xiàn)的次數(shù)和位置,當(dāng)用戶查詢時,檢索程序就根據(jù)事先建立的索引進(jìn)行查找,并將查找的結(jié)果反饋給用戶的檢索方式。這個過程類似于通過字典中的檢索字表查字的過程。全文搜索搜索引擎數(shù)據(jù)庫中的數(shù)據(jù)。
lucene
lucene,就是一個jar包,里面包含了封裝好的各種建立倒排索引,以及進(jìn)行搜索的代碼,包括各種算法。我們就用java開發(fā)的時候,引入lucene jar,然后基于lucene的api進(jìn)行去進(jìn)行開發(fā)就可以了。
那你聊聊Elasticsearch的核心概念,就是我們經(jīng)常用的那些。
近實時
近實時,兩個意思,從寫入數(shù)據(jù)到數(shù)據(jù)可以被搜索到有一個小延遲(大概1秒);基于es執(zhí)行搜索和分析可以達(dá)到秒級。
Cluster(集群)
集群包含多個節(jié)點,每個節(jié)點屬于哪個集群是通過一個配置(集群名稱,默認(rèn)是elasticsearch)來決定的,對于中小型應(yīng)用來說,剛開始一個集群就一個節(jié)點很正常
Node(節(jié)點)
集群中的一個節(jié)點,節(jié)點也有一個名稱(默認(rèn)是隨機(jī)分配的),節(jié)點名稱很重要(在執(zhí)行運維管理操作的時候),默認(rèn)節(jié)點會去加入一個名稱為“elasticsearch”的集群,如果直接啟動一堆節(jié)點,那么它們會自動組成一個elasticsearch集群,當(dāng)然一個節(jié)點也可以組成一個elasticsearch集群。
Index(索引-數(shù)據(jù)庫)
索引包含一堆有相似結(jié)構(gòu)的文檔數(shù)據(jù),比如可以有一個客戶索引,商品分類索引,訂單索引,索引有一個名稱。一個index包含很多document,一個index就代表了一類類似的或者相同的document。比如說建立一個product index,商品索引,里面可能就存放了所有的商品數(shù)據(jù),所有的商品document。
Type(類型-表)
每個索引里都可以有一個或多個type,type是index中的一個邏輯數(shù)據(jù)分類,一個type下的document,都有相同的field,比如博客系統(tǒng),有一個索引,可以定義用戶數(shù)據(jù)type,博客數(shù)據(jù)type,評論數(shù)據(jù)type。
Document(文檔-行)
文檔是es中的最小數(shù)據(jù)單元,一個document可以是一條客戶數(shù)據(jù),一條商品分類數(shù)據(jù),一條訂單數(shù)據(jù),通常用JSON數(shù)據(jù)結(jié)構(gòu)表示,每個index下的type中,都可以去存儲多個document。
Field(字段-列)
Field是Elasticsearch的最小單位。一個document里面有多個field,每個field就是一個數(shù)據(jù)字段。
shard
單臺機(jī)器無法存儲大量數(shù)據(jù),es可以將一個索引中的數(shù)據(jù)切分為多個shard,分布在多臺服務(wù)器上存儲。有了shard就可以橫向擴(kuò)展,存儲更多數(shù)據(jù),讓搜索和分析等操作分布到多臺服務(wù)器上去執(zhí)行,提升吞吐量和性能。每個shard都是一個lucene index。
replica
任何一個服務(wù)器隨時可能故障或宕機(jī),此時shard可能就會丟失,因此可以為每個shard創(chuàng)建多個replica副本。replica可以在shard故障時提供備用服務(wù),保證數(shù)據(jù)不丟失,多個replica還可以提升搜索操作的吞吐量和性能。primary shard(建立索引時一次設(shè)置,不能修改,默認(rèn)5個),replica shard(隨時修改數(shù)量,默認(rèn)1個),默認(rèn)每個索引10個shard,5個primary shard,5個replica shard,最小的高可用配置,是2臺服務(wù)器。
說說Elasticsearch樂觀并發(fā)控制
Elasticsearch是分布式的。當(dāng)文檔被創(chuàng)建、更新或刪除,文檔的新版本會被復(fù)制到集群的其它節(jié)點。Elasticsearch即是同步的又是異步的,意思是這些復(fù)制請求都是平行發(fā)送的,并無序(out of sequence)的到達(dá)目的地。這就需要一種方法確保老版本的文檔永遠(yuǎn)不會覆蓋新的版本。
上文我們提到index、get、delete請求時,我們指出每個文檔都有一個_version號碼,這個號碼在文檔被改變時加一。Elasticsearch使用這個_version保證所有修改都被正確排序。當(dāng)一個舊版本出現(xiàn)在新版本之后,它會被簡單的忽略。
我們利用_version的這一優(yōu)點確保數(shù)據(jù)不會因為修改沖突而丟失。我們可以指定文檔的version來做想要的更改。如果那個版本號不是現(xiàn)在的,我們的請求就失敗了。
用version 來保證并發(fā)的順序一致性
聊聊text,keyword類型的區(qū)別
- text:當(dāng)一個字段是要被全文搜索的,比如Email內(nèi)容、產(chǎn)品描述,應(yīng)該使用text類型。設(shè)置text類型以后,字段內(nèi)容會被分析,在生成倒排索引以前,字符串會被分析器分成一個一個詞項。text類型的字段不用于排序,很少用于聚合。
- keyword:keyword類型適用于索引結(jié)構(gòu)化的字段,比如email地址、主機(jī)名、狀態(tài)碼和標(biāo)簽。如果字段需要進(jìn)行過濾(比如查找已發(fā)布博客中status屬性為published的文章)、排序、聚合。keyword類型的字段只能通過精確值搜索到。
那你說說查詢api返回的主要包含什么東西
hits
響應(yīng)中最重要的部分是hits,它包含了total字段來表示匹配到的文檔總數(shù),hits數(shù)組還包含了匹配到的前10條數(shù)據(jù)。
hits數(shù)組中的每個結(jié)果都包含_index、_type和文檔的_id字段,被加入到_source字段中這意味著在搜索結(jié)果中我們將可以直接使用全部文檔。這不像其他搜索引擎只返回文檔ID,需要你單獨去獲取文檔。
每個節(jié)點都有一個_score字段,這是相關(guān)性得分(relevance score),它衡量了文檔與查詢的匹配程度。默認(rèn)的,返回的結(jié)果中關(guān)聯(lián)性最大的文檔排在首位;這意味著,它是按照_score降序排列的。這種情況下,我們沒有指定任何查詢,所以所有文檔的相關(guān)性是一樣的,因此所有結(jié)果的_score都是取得一個中間值1
max_score指的是所有文檔匹配查詢中_score的最大值。
took
took告訴我們整個搜索請求花費的毫秒數(shù)。
shards
_shards節(jié)點告訴我們參與查詢的分片數(shù)(total字段),有多少是成功的(successful字段),有多少的是失敗的(failed字段)。通常我們不希望分片失敗,不過這個有可能發(fā)生。如果我們遭受一些重大的故障導(dǎo)致主分片和復(fù)制分片都故障,那這個分片的數(shù)據(jù)將無法響應(yīng)給搜索請求。這種情況下,Elasticsearch將報告分片failed,但仍將繼續(xù)返回剩余分片上的結(jié)果。
timeout
time_out值告訴我們查詢超時與否。一般的,搜索請求不會超時。如果響應(yīng)速度比完整的結(jié)果更重要,你可以定義timeout參數(shù)為10或者10ms(10毫秒),或者1s(1秒)
聊聊shard&replica機(jī)制
- index包含多個shard
- 每個shard都是一個最小工作單元,承載部分?jǐn)?shù)據(jù),lucene實例,完整的建立索引和處理請求的能力
- 增減節(jié)點時,shard會自動在nodes中負(fù)載均衡
- primary shard和replica shard,每個document肯定只存在于某一個primary shard以及其對應(yīng)的replica shard中,不可能存在于多個primary shard
- replica shard是primary shard的副本,負(fù)責(zé)容錯,以及承擔(dān)讀請求負(fù)載
- primary shard的數(shù)量在創(chuàng)建索引的時候就固定了,replica shard的數(shù)量可以隨時修改
- primary shard的默認(rèn)數(shù)量是5,replica默認(rèn)是1,默認(rèn)有10個shard,5個primary shard,5個replica shard
- primary shard不能和自己的replica shard放在同一個節(jié)點上(否則節(jié)點宕機(jī),primary shard和副本都丟失,起不到容錯的作用),但是可以和其他primary shard的replica shard放在同一個節(jié)點上
ES是如何實現(xiàn)master選舉的?
前置條件:
- 只有是候選主節(jié)點(master:true)的節(jié)點才能成為主節(jié)點。
- 最小主節(jié)點數(shù)(min_master_nodes)的目的是防止腦裂。
Elasticsearch 的選主是 ZenDiscovery 模塊負(fù)責(zé)的,主要包含 Ping(節(jié)點之間通過這個RPC來發(fā)現(xiàn)彼此)和 Unicast(單播模塊包含一個主機(jī)列表以控制哪些節(jié)點需要 ping 通)這兩部分;
獲取主節(jié)點的核心入口為 findMaster,選擇主節(jié)點成功返回對應(yīng) Master,否則返回 null。
選舉流程大致描述如下:
- 第一步:確認(rèn)候選主節(jié)點數(shù)達(dá)標(biāo),elasticsearch.yml 設(shè)置的值 discovery.zen.minimum_master_nodes;
- 第二步:對所有候選主節(jié)點根據(jù)nodeId字典排序,每次選舉每個節(jié)點都把自己所知道節(jié)點排一次序,然后選出第一個(第0位)節(jié)點,暫且認(rèn)為它是master節(jié)點。
- 第三步:如果對某個節(jié)點的投票數(shù)達(dá)到一定的值(候選主節(jié)點數(shù)n/2+1)并且該節(jié)點自己也選舉自己,那這個節(jié)點就是master。否則重新選舉一直到滿足上述條件。
如何解決ES集群的腦裂問題
所謂集群腦裂,是指 Elasticsearch 集群中的節(jié)點(比如共 20 個),其中的 10 個選了一個 master,另外 10 個選了另一個 master 的情況。
當(dāng)集群 master 候選數(shù)量不小于 3 個時,可以通過設(shè)置最少投票通過數(shù)量(discovery.zen.minimum_master_nodes)超過所有候選節(jié)點一半以上來解決腦裂問題;
當(dāng)候選數(shù)量為兩個時,只能修改為唯一的一個 master 候選,其他作為 data 節(jié)點,避免腦裂問題。
聊聊es的寫入流程
Elasticsearch采用多Shard方式,通過配置routing規(guī)則將數(shù)據(jù)分成多個數(shù)據(jù)子集,每個數(shù)據(jù)子集提供獨立的索引和搜索功能。當(dāng)寫入文檔的時候,根據(jù)routing規(guī)則,將文檔發(fā)送給特定Shard中建立索引。這樣就能實現(xiàn)分布式了。
每個Index由多個Shard組成(默認(rèn)是5個),每個Shard有一個主節(jié)點和多個副本節(jié)點,副本個數(shù)可配。但每次寫入的時候,寫入請求會先根據(jù)_routing規(guī)則選擇發(fā)給哪個Shard,Index Request中可以設(shè)置使用哪個Filed的值作為路由參數(shù),如果沒有設(shè)置,則使用Mapping中的配置,如果mapping中也沒有配置,則使用_id作為路由參數(shù),然后通過_routing的Hash值選擇出Shard(在OperationRouting類中),最后從集群的Meta中找出出該Shard的Primary節(jié)點。
請求接著會發(fā)送給Primary Shard,在Primary Shard上執(zhí)行成功后,再從Primary Shard上將請求同時發(fā)送給多個Replica Shard,請求在多個Replica Shard上執(zhí)行成功并返回給Primary Shard后,寫入請求執(zhí)行成功,返回結(jié)果給客戶端。
那你說說具體在 shard上的寫入流程唄
在每一個Shard中,寫入流程分為兩部分,先寫入Lucene,再寫入TransLog。
寫入請求到達(dá)Shard后,先寫Lucene文件,創(chuàng)建好索引,此時索引還在內(nèi)存里面,接著去寫TransLog,寫完TransLog后,刷新TransLog數(shù)據(jù)到磁盤上,寫磁盤成功后,請求返回給用戶。這里有幾個關(guān)鍵點:
和數(shù)據(jù)庫不同,數(shù)據(jù)庫是先寫CommitLog,然后再寫內(nèi)存,而Elasticsearch是先寫內(nèi)存,最后才寫TransLog,一種可能的原因是Lucene的內(nèi)存寫入會有很復(fù)雜的邏輯,很容易失敗,比如分詞,字段長度超過限制等,比較重,為了避免TransLog中有大量無效記錄,減少recover的復(fù)雜度和提高速度,所以就把寫Lucene放在了最前面。
寫Lucene內(nèi)存后,并不是可被搜索的,需要通過Refresh把內(nèi)存的對象轉(zhuǎn)成完整的Segment后,然后再次reopen后才能被搜索,一般這個時間設(shè)置為1秒鐘,導(dǎo)致寫入Elasticsearch的文檔,最快要1秒鐘才可被從搜索到,所以Elasticsearch在搜索方面是NRT(Near Real Time)近實時的系統(tǒng)。
每隔一段比較長的時間,比如30分鐘后,Lucene會把內(nèi)存中生成的新Segment刷新到磁盤上,刷新后索引文件已經(jīng)持久化了,歷史的TransLog就沒用了,會清空掉舊的TransLog。
Lucene緩存中的數(shù)據(jù)默認(rèn)1秒之后才生成segment文件,即使是生成了segment文件,這個segment是寫到頁面緩存中的,并不是實時的寫到磁盤,只有達(dá)到一定時間或者達(dá)到一定的量才會強(qiáng)制flush磁盤。如果這期間機(jī)器宕掉,內(nèi)存中的數(shù)據(jù)就丟了。如果發(fā)生這種情況,內(nèi)存中的數(shù)據(jù)是可以從TransLog中進(jìn)行恢復(fù)的,TransLog默認(rèn)是每5秒都會刷新一次磁盤。但這依然不能保證數(shù)據(jù)安全,因為仍然有可能最多丟失TransLog中5秒的數(shù)據(jù)。這里可以通過配置增加TransLog刷磁盤的頻率來增加數(shù)據(jù)可靠性,最小可配置100ms,但不建議這么做,因為這會對性能有非常大的影響。一般情況下,Elasticsearch是通過副本機(jī)制來解決這一問題的。即使主分片所在節(jié)點宕機(jī),丟失了5秒數(shù)據(jù),依然是可以通過副本來進(jìn)行恢復(fù)的。
總結(jié)一下,數(shù)據(jù)先寫入內(nèi)存 buffer,然后每隔 1s,將數(shù)據(jù) refresh 到 os cache,到了 os cache 數(shù)據(jù)就能被搜索到(所以我們才說 es 從寫入到能被搜索到,中間有 1s 的延遲)。每隔 5s,將數(shù)據(jù)寫入 translog 文件(這樣如果機(jī)器宕機(jī),內(nèi)存數(shù)據(jù)全沒,最多會有 5s 的數(shù)據(jù)丟失),translog 大到一定程度,或者默認(rèn)每隔 30mins,會觸發(fā) commit 操作,將緩沖區(qū)的數(shù)據(jù)都 flush 到 segment file 磁盤文件中。
說說es的更新流程吧
Lucene中不支持部分字段的Update,所以需要在Elasticsearch中實現(xiàn)該功能,具體流程如下:
- 到Update請求后,從Segment或者TransLog中讀取同id的完整Doc,記錄版本號為V1。
- 將版本V1的全量Doc和請求中的部分字段Doc合并為一個完整的Doc,同時更新內(nèi)存中的VersionMap。獲取到完整Doc后,Update請求就變成了Index請求。
- 加鎖。
- 再次從versionMap中讀取該id的最大版本號V2,如果versionMap中沒有,則從Segment或者TransLog中讀取,這里基本都會從versionMap中獲取到。
- 檢查版本是否沖突(V1==V2),如果沖突,則回退到開始的“Update doc”階段,重新執(zhí)行。如果不沖突,則執(zhí)行最新的Add請求。
- 在Index Doc階段,首先將Version + 1得到V3,再將Doc加入到Lucene中去,Lucene中會先刪同id下的已存在doc id,然后再增加新Doc。寫入Lucene成功后,將當(dāng)前V3更新到versionMap中。
- 釋放鎖,部分更新的流程就結(jié)束了
詳細(xì)描述一下ES搜索的過程?
搜索被執(zhí)行成一個兩階段過程,即 Query Then Fetch;
Query階段:
查詢會廣播到索引中每一個分片拷貝(主分片或者副本分片)。每個分片在本地執(zhí)行搜索并構(gòu)建一個匹配文檔的大小為 from + size 的優(yōu)先隊列。PS:在搜索的時候是會查詢Filesystem Cache的,但是有部分?jǐn)?shù)據(jù)還在Memory Buffer,所以搜索是近實時的。
每個分片返回各自優(yōu)先隊列中 所有文檔的 ID 和排序值 給協(xié)調(diào)節(jié)點,它合并這些值到自己的優(yōu)先隊列中來產(chǎn)生一個全局排序后的結(jié)果列表。
Fetch階段:
協(xié)調(diào)節(jié)點辨別出哪些文檔需要被取回并向相關(guān)的分片提交多個 GET 請求。每個分片加載并 豐富 文檔,如果有需要的話,接著返回文檔給協(xié)調(diào)節(jié)點。一旦所有的文檔都被取回了,協(xié)調(diào)節(jié)點返回結(jié)果給客戶端。
說說es的寫一致性
我們在發(fā)送任何一個增刪改操作的時候,比如說put /index/type/id,都可以帶上一個consistency參數(shù),指明我們想要的寫一致性是什么?
put /index/type/id?consistency=quorum
- one:要求我們這個寫操作,只要有一個primary shard是active活躍可用的,就可以執(zhí)行
- all:要求我們這個寫操作,必須所有的primary shard和replica shard都是活躍的,才可以執(zhí)行這個寫操作
- quorum:默認(rèn)的值,要求所有的shard中,必須是大部分的shard都是活躍的,可用的,才可以執(zhí)行這個寫操作
聊聊 elasticsearch 深度分頁以及scroll 滾動搜索
深度分頁
深度分頁其實就是搜索的深淺度,比如第1頁,第2頁,第10頁,第20頁,是比較淺的;第10000頁,第20000頁就是很深了。
搜索得太深,就會造成性能問題,會耗費內(nèi)存和占用cpu。而且es為了性能,他不支持超過一萬條數(shù)據(jù)以上的分頁查詢。那么如何解決深度分頁帶來的問題,我們應(yīng)該避免深度分頁操作(限制分頁頁數(shù)),比如最多只能提供100頁的展示,從第101頁開始就沒了,畢竟用戶也不會搜的那么深,我們平時搜索淘寶或者京東也就看個10來頁就頂多了。
滾動搜索
一次性查詢1萬+數(shù)據(jù),往往會造成性能影響,因為數(shù)據(jù)量太多了。這個時候可以使用滾動搜索,也就是 scroll 。
滾動搜索可以先查詢出一些數(shù)據(jù),然后再緊接著依次往下查詢。在第一次查詢的時候會有一個滾動id,相當(dāng)于一個錨標(biāo)記 ,隨后再次滾動搜索會需要上一次搜索滾動id,根據(jù)這個進(jìn)行下一次的搜索請求。每次搜索都是基于一個歷史的數(shù)據(jù)快照,查詢數(shù)據(jù)的期間,如果有數(shù)據(jù)變更,那么和搜索是沒有關(guān)系的。
es在數(shù)據(jù)量很大的情況下如何提高性能
filesystem
es每次走fileSystem cache查詢速度是最快的
所以將每個查詢的數(shù)據(jù)50% 容量
= fileSystem cache 容量。
數(shù)據(jù)預(yù)熱
數(shù)據(jù)預(yù)熱是指,每隔一段時間,將熱數(shù)據(jù)
手動在后臺查詢一遍,將熱數(shù)據(jù)刷新到fileSystem cache上
冷熱分離
類似于MySQL的分表分庫
將熱數(shù)據(jù)單獨建立一個索引 分配3臺機(jī)器只保持熱機(jī)器的索引
另外的機(jī)器保持冷數(shù)據(jù)的索引,但有一個問題,就是事先必須知道哪些是熱數(shù)據(jù) 哪些是冷數(shù)據(jù)
不可以深度分頁
跟產(chǎn)品經(jīng)理說,你系統(tǒng)不允許翻那么深的頁,默認(rèn)翻的越深,性能就越差。
類似于 app 里的推薦商品不斷下拉出來一頁一頁的
類似于微博中,下拉刷微博,刷出來一頁一頁的,你可以用 scroll api
結(jié)束
es可能我自己用的也比較少,就用來做一些搜索,沒有用來做bi,所以呢?也不是那么深入吧,希望對大家有幫助,接下來復(fù)習(xí)下隊列
日常求贊
好了各位,以上就是這篇文章的全部內(nèi)容了,能看到這里的人呀,都是真粉。
創(chuàng)作不易,各位的支持和認(rèn)可,就是我創(chuàng)作的最大動力,我們下篇文章見
微信 搜 "六脈神劍的程序人生" 回復(fù)888 有我找的許多的資料送給大家