(零)ElasticSearch架構(gòu)概述
ElasticSearch是現(xiàn)在技術(shù)前沿的大數(shù)據(jù)引擎,常見的組合有ES+Logstash+Kibana作為一套成熟的日志系統(tǒng)旨指,其中Logstash是ETL工具漫贞,Kibana是數(shù)據(jù)分析展示平臺(tái)纤怒。ES讓人驚艷的是他強(qiáng)大的搜索相關(guān)能力和災(zāi)備策略嘹黔,ES開放了一些接口供開發(fā)者研發(fā)自己的插件骨杂,ES結(jié)合中文分詞的插件會(huì)給ES的搜索和分析起到很大的推動(dòng)作用涂身。ElasticSearch是使用開源全文檢索庫ApacheLucene進(jìn)行索引和搜索的,說架構(gòu)必須和Lucene的一些東西打交道搓蚪。
關(guān)于Lucene:
ApacheLucene將寫入索引的所有信息組織成一種倒排索引(Inverted Index)的結(jié)構(gòu)之中蛤售,該結(jié)構(gòu)是種將詞項(xiàng)映射到文檔的數(shù)據(jù)結(jié)構(gòu)。其工作方式與傳統(tǒng)的關(guān)系數(shù)據(jù)庫不同妒潭,大致來說倒排索引是面向詞項(xiàng)而不是面向文檔的悴能。且Lucene索引之中還存儲(chǔ)了很多其他的信息,如詞向量等等雳灾,每個(gè)Lucene都是由多個(gè)段構(gòu)成的漠酿,每個(gè)段只會(huì)被創(chuàng)建一次但會(huì)被查詢多次,段一旦創(chuàng)建就不會(huì)再被修改谎亩。多個(gè)段會(huì)在段合并的階段合并在一起炒嘲,何時(shí)合并由Lucene的內(nèi)在機(jī)制決定,段合并后數(shù)量會(huì)變少匈庭,但是相應(yīng)的段本身會(huì)變大夫凸。段合并的過程是非常消耗I/O的,且與之同時(shí)會(huì)有些不再使用的信息被清理掉阱持。在Lucene中夭拌,將數(shù)據(jù)轉(zhuǎn)化為倒排索引,將完整串轉(zhuǎn)化為可用于搜索的詞項(xiàng)的過程叫做分析衷咽。文本分析由分析器(Analyzer)來執(zhí)行鸽扁,分析其由分詞器(Tokenizer),過濾器(Filter)和字符映射器(Character Mapper)組成镶骗,其各個(gè)功能顯而易見献烦。除此之外,Lucene有自己的一套完整的查詢語言來幫助我們進(jìn)行搜索和讀寫卖词。
?[注]ES中的索引指的是查詢/尋址時(shí)URI中的一個(gè)字段如:[host]:[port(9200)]/[index]/[type]/[ID]?[option]巩那,而Lucene中的索引更多地和ES中的分片的概念相對(duì)應(yīng)。
回到ElasticSearch此蜈,ES的架構(gòu)遵循的設(shè)計(jì)理念有以下幾個(gè)特征:
1.?合理的默認(rèn)配置:只需修改節(jié)點(diǎn)中的Yaml配置文件即横,就可以迅捷配置。這和Spring4中對(duì)配置的簡(jiǎn)化有相似的地方裆赵。
2.?分布式工作模式:ES強(qiáng)大的Zen發(fā)現(xiàn)機(jī)制不僅支持組廣播也支持點(diǎn)單播东囚,且有“知一點(diǎn)即知天下”之妙。
3.?對(duì)等架構(gòu):節(jié)點(diǎn)之間自動(dòng)備份分片战授,且使分片本身和樣本之間盡量”遠(yuǎn)離“页藻,可以避免單點(diǎn)故障桨嫁。且Master節(jié)點(diǎn)和Data節(jié)點(diǎn)幾乎完全等價(jià)。
4.?易于向集群擴(kuò)充新節(jié)點(diǎn):大大簡(jiǎn)化研發(fā)或運(yùn)維將新節(jié)點(diǎn)加入集群所需的工作份帐。
5.?不對(duì)索引中的數(shù)據(jù)結(jié)構(gòu)增加任何限制:ES支持在一個(gè)索引之中存在多種數(shù)據(jù)類型璃吧。
6.?準(zhǔn)實(shí)時(shí):搜索和版本同步,由于ES是分布式應(yīng)用废境,一個(gè)重大的挑戰(zhàn)就是一致性問題畜挨,無論索引還是文檔數(shù)據(jù),然而事實(shí)證明ES表現(xiàn)優(yōu)秀噩凹。
(一)分片策略
選擇合適的分片數(shù)和副本數(shù)巴元。ES的分片分為兩種,主分片(Primary Shard)和副本(Replicas)驮宴。默認(rèn)情況下逮刨,ES會(huì)為每個(gè)索引創(chuàng)建5個(gè)分片,即使是在單機(jī)環(huán)境下堵泽,這種冗余被稱作過度分配(Over Allocation)禀忆,目前看來這么做完全沒有必要,僅在散布文檔到分片和處理查詢的過程中就增加了更多的復(fù)雜性落恼,好在ES的優(yōu)秀性能掩蓋了這一點(diǎn)。假設(shè)一個(gè)索引由一個(gè)分片構(gòu)成离熏,那么當(dāng)索引的大小超過單個(gè)節(jié)點(diǎn)的容量的時(shí)候佳谦,ES不能將索引分割成多份,因此必須在創(chuàng)建索引的時(shí)候就指定好需要的分片數(shù)量滋戳。此時(shí)我們所能做的就是創(chuàng)建一個(gè)新的索引钻蔑,并在初始設(shè)定之中指定這個(gè)索引擁有更多的分片。反之如果過度分配奸鸯,就增大了Lucene在合并分片查詢結(jié)果時(shí)的復(fù)雜度咪笑,從而增大了耗時(shí),所以我們得到了以下結(jié)論:
我們應(yīng)該使用最少的分片娄涩!
主分片窗怒,副本和節(jié)點(diǎn)最大數(shù)之間數(shù)量存在以下關(guān)系:
節(jié)點(diǎn)數(shù)<=主分片數(shù)*(副本數(shù)+1)
控制分片分配行為。以上是在創(chuàng)建每個(gè)索引的時(shí)候需要考慮的優(yōu)化方法蓄拣,然而在索引已創(chuàng)建好的前提下扬虚,是否就是沒有辦法從分片的角度提高了性能了呢?當(dāng)然不是球恤,首先能做的是調(diào)整分片分配器的類型辜昵,具體是在elasticsearch.yml中設(shè)置cluster.routing.allocation.type屬性,共有兩種分片器even_shard,balanced(默認(rèn))咽斧。even_shard是盡量保證每個(gè)節(jié)點(diǎn)都具有相同數(shù)量的分片堪置,balanced是基于可控制的權(quán)重進(jìn)行分配躬存,相對(duì)于前一個(gè)分配器,它更暴漏了一些參數(shù)而引入調(diào)整分配過程的能力舀锨。
每次ES的分片調(diào)整都是在ES上的數(shù)據(jù)分布發(fā)生了變化的時(shí)候進(jìn)行的岭洲,最有代表性的就是有新的數(shù)據(jù)節(jié)點(diǎn)加入了集群的時(shí)候。當(dāng)然調(diào)整分片的時(shí)機(jī)并不是由某個(gè)閾值觸發(fā)的雁竞,ES內(nèi)置十一個(gè)裁決者來決定是否觸發(fā)分片調(diào)整钦椭,這里暫不贅述。另外碑诉,這些分配部署策略都是可以在運(yùn)行時(shí)更新的彪腔,更多配置分片的屬性也請(qǐng)大家自行Google。
(二)路由優(yōu)化
ES中所謂的路由和IP網(wǎng)絡(luò)不同进栽,是一個(gè)類似于Tag的東西德挣。在創(chuàng)建文檔的時(shí)候,可以通過字段為文檔增加一個(gè)路由屬性的Tag快毛。ES內(nèi)在機(jī)制決定了擁有相同路由屬性的文檔格嗅,一定會(huì)被分配到同一個(gè)分片上,無論是主分片還是副本唠帝。那么屯掖,在查詢的過程中,一旦指定了感興趣的路由屬性襟衰,ES就可以直接到相應(yīng)的分片所在的機(jī)器上進(jìn)行搜索贴铜,而避免了復(fù)雜的分布式協(xié)同的一些工作,從而提升了ES的性能瀑晒。于此同時(shí)绍坝,假設(shè)機(jī)器1上存有路由屬性A的文檔,機(jī)器2上存有路由屬性為B的文檔苔悦,那么我在查詢的時(shí)候一旦指定目標(biāo)路由屬性為A轩褐,即使機(jī)器2故障癱瘓,對(duì)機(jī)器1構(gòu)不成很大影響玖详,所以這么做對(duì)災(zāi)況下的查詢也提出了解決方案把介。所謂的路由,本質(zhì)上是一個(gè)分桶(Bucketing)操作蟋座。當(dāng)然劳澄,查詢中也可以指定多個(gè)路由屬性,機(jī)制大同小異蜈七。
(三)ES上的GC調(diào)優(yōu)
ElasticSearch本質(zhì)上是個(gè)Java程序秒拔,所以配置JVM垃圾回收器本身也是一個(gè)很有意義的工作。我們使用JVM的Xms和Xmx參數(shù)來提供指定內(nèi)存大小,本質(zhì)上提供的是JVM的堆空間大小砂缩,當(dāng)JVM的堆空間不足的時(shí)候就會(huì)觸發(fā)致命的OutOfMemoryException作谚。這意味著要么內(nèi)存不足,要么出現(xiàn)了內(nèi)存泄露庵芭。處理GC問題妹懒,首先要確定問題的源頭,一般有兩種方案:
1. 開啟ElasticSearch上的GC日志
2. 使用jstat命令
3. 生成內(nèi)存Dump
關(guān)于第一條双吆,在ES的配置文件elasticsearch.yml中有相關(guān)的屬性可以配置眨唬,關(guān)于每個(gè)屬性的用途這里當(dāng)然說不完。
第二條好乐,jstat命令可以幫助我們查看JVM堆中各個(gè)區(qū)的使用情況和GC的耗時(shí)情況匾竿。
第三條,最后的辦法就是將JVM的堆空間轉(zhuǎn)儲(chǔ)到文件中去蔚万,實(shí)質(zhì)上是對(duì)JVM堆空間的一個(gè)快照岭妖。
想了解更多關(guān)于JVM本身GC調(diào)優(yōu)方法請(qǐng)參考:http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html
另外,通過修改ES節(jié)點(diǎn)的啟動(dòng)參數(shù)反璃,也可以調(diào)整GC的方式昵慌,但是實(shí)質(zhì)上和上述方法是等同的。
(四)避免內(nèi)存交換
這一點(diǎn)很簡(jiǎn)單淮蜈,由于操作系統(tǒng)的虛擬內(nèi)存頁交換機(jī)制斋攀,會(huì)給性能帶來障礙,如數(shù)據(jù)寫滿內(nèi)存會(huì)寫入Linux中的Swap分區(qū)梧田。
可以通過在elasticsearch.yml文件中的bootstrap.mlockall設(shè)置為true來實(shí)現(xiàn)淳蔼,但是需要管理員權(quán)限,需要修改操作系統(tǒng)的相關(guān)配置文件柿扣。
(五)控制索引合并
上文提到過,ES中的分片和副本本質(zhì)上都是Lucene索引闺魏,而Lucene索引又基于多個(gè)索引段構(gòu)建(至少一個(gè))未状,索引文件中的絕大多數(shù)都是只被寫一次,讀多次析桥,在Lucene內(nèi)在機(jī)制控制下司草,當(dāng)滿足某種條件的時(shí)候多個(gè)索引段會(huì)被合并到一個(gè)更大的索引段,而那些舊的索引段會(huì)被拋棄并移除磁盤泡仗,這個(gè)操作叫做段合并埋虹。
Lucene要執(zhí)行段合并的理由很簡(jiǎn)單充分:索引段粒度越小,查詢性能越低且耗費(fèi)的內(nèi)存越多娩怎。頻繁的文檔更改操作會(huì)導(dǎo)致大量的小索引段搔课,從而導(dǎo)致文件句柄打開過多的問題,如修改系統(tǒng)配置截亦,增大系統(tǒng)允許的最大文件打開數(shù)爬泥〖硖郑總的來講,當(dāng)索引段由多一個(gè)合并為一個(gè)的時(shí)候袍啡,會(huì)減少索引段的數(shù)量從而提高ES性能踩官。對(duì)于研發(fā)者來講,我們所能做的就是選擇合適的合并策略境输,盡管段合并完全是Lucene的任務(wù)蔗牡,但隨著Lucene開放更多配置借口,新版本的ES還是提供了三種合并的策略tiered嗅剖,log_byte_size辩越,log_doc。另外窗悯,ES也提供了兩種Lucene索引段合并的調(diào)度器:concurrent和serial区匣。其中各者具體區(qū)別,這里暫不贅述蒋院,只是拋磚引玉亏钩。