簡單操作可以直接使用ElasticsearchRespositor接口,復(fù)雜的使用ElasticsearchTemplate
查詢
?1.單字符串全文查詢
SearchQuery searchQuery=newNativeSearchQueryBuilder().withQuery(queryStringQuery(word)).withPageable(pageable).build();
2.某字段按字符串模糊查詢
查詢某個(gè)字段中模糊包含目標(biāo)字符串夺姑,使用matchQuery
SearchQuery searchQuery=newNativeSearchQueryBuilder().withQuery(matchQuery("content",content)).withPageable(pageable).build();
3.PhraseMatch查詢墩邀,短語匹配
和match查詢類似,match_phrase查詢首先解析查詢字符串來產(chǎn)生一個(gè)詞條列表盏浙。然后會(huì)搜索所有的詞條眉睹,但只保留包含了所有搜索詞條的文檔,并且詞條的位置要鄰接废膘。一個(gè)針對短語“中華共和國”的查詢不會(huì)匹配“中華人民共和國”竹海,因?yàn)闆]有含有鄰接在一起的“中華”和“共和國”詞條。
這種完全匹配比較嚴(yán)格丐黄,類似于數(shù)據(jù)庫里的“%落日熔金%”這種斋配,使用場景比較狹窄。如果我們希望能不那么嚴(yán)格孵稽,譬如搜索“中華共和國”许起,希望帶“我愛中華人民共和國”的也能出來十偶,就是分詞后菩鲜,中間能間隔幾個(gè)位置的也能查出來,可以使用slop參數(shù)惦积。
SearchQuery searchQuery=newNativeSearchQueryBuilder().withQuery(matchPhraseQuery("content",content)).withPageable(pageable).build();
4.multi_match多個(gè)字段匹配某字符串
如果我們希望title接校,content兩個(gè)字段去匹配某個(gè)字符串,只要任何一個(gè)字段包括該字符串即可,就可以使用multimatch
SearchQuery searchQuery=newNativeSearchQueryBuilder().withQuery(multiMatchQuery(title,"title","content")).withPageable(pageable).build();
5.完全包含查詢
之前的查詢中蛛勉,當(dāng)我們輸入“我天”時(shí)鹿寻,ES會(huì)把分詞后所有包含“我”和“天”的都查詢出來,如果我們希望必須是包含了兩個(gè)字的才能被查詢出來诽凌,那么我們就需要設(shè)置一下Operator毡熏。
SearchQuery searchQuery=newNativeSearchQueryBuilder().withQuery(matchQuery("title",title).operator(MatchQueryBuilder.Operator.AND)).build();
6合并查詢
即boolQuery,可以設(shè)置多個(gè)條件的查詢方式侣诵。它的作用是用來組合多個(gè)Query痢法,有四種方式來組合,must杜顺,mustnot财搁,filter,should躬络。
must代表返回的文檔必須滿足must子句的條件尖奔,會(huì)參與計(jì)算分值;
filter代表返回的文檔必須滿足filter子句的條件穷当,但不會(huì)參與計(jì)算分值提茁;
should代表返回的文檔可能滿足should子句的條件,也可能不滿足馁菜,有多個(gè)should時(shí)滿足任何一個(gè)就可以甘凭,通過minimum_should_match設(shè)置至少滿足幾個(gè)。
mustnot代表必須不滿足子句的條件火邓。
譬如我想查詢title包含“XXX”丹弱,且userId=“1”,且weight最好小于5的結(jié)果铲咨。那么就可以使用boolQuery來組合躲胳。
SearchQuery searchQuery=newNativeSearchQueryBuilder().withQuery(boolQuery().must(termQuery("userId",userId)).should(rangeQuery("weight").lt(weight)).must(matchQuery("title",title))).build();
7過濾查詢bool的使用
Bool查詢對應(yīng)Lucene中的BooleanQuery,它由一個(gè)或者多個(gè)子句組成,每個(gè)子句都有特定的類型
must
返回的文檔必須滿足must子句的條件,并且參與計(jì)算分值
filter
返回的文檔必須滿足filter子句的條件,但是不會(huì)像must一樣,參與計(jì)算分值
should
返回的文檔可能滿足should子句的條件.在一個(gè)bool查詢中,如果沒有must或者filter,有一個(gè)或者多個(gè)should子句,那么只要滿足一個(gè)就可以返回.minimum_should_match參數(shù)定義了至少滿足幾個(gè)子句.
must_not
返回的文檔必須不滿足定義的條件
如果一個(gè)查詢既有filter又有should,那么至少包含一個(gè)should子句.
bool查詢也支持禁用協(xié)同計(jì)分選項(xiàng)disable_coord.一般計(jì)算分值的因素取決于所有的查詢條件.
bool查詢也是采用more_matches_is_better的機(jī)制,因此滿足must和should子句的文檔將會(huì)合并起來計(jì)算分值.
8高亮查詢
//高亮查詢
HighlightBuilder highlightBuilder =new HighlightBuilder();
highlightBuilder.field(new HighlightBuilder.Field("message"));
highlightBuilder.preTags("<span style=\"color:red\">");? //高亮設(shè)置
highlightBuilder.postTags("</span>");
1.概念介紹
1.索引(index)
相當(dāng)于Mysql中的數(shù)據(jù)庫(database),用于存放文檔
2. 類型(type)
相當(dāng)于Mysql中的表(table)纤勒,定義了相同數(shù)據(jù)結(jié)構(gòu)的文檔存放
3. 文檔(document)
相當(dāng)于Mysql中的一行記錄(row)坯苹,是索引的基本單元,包含一個(gè)或多個(gè)鍵值對摇天,表現(xiàn)形式就是json對象
4. 字段(filed)
相當(dāng)于Mysql中的一列(column)粹湃,是key-value形式的
一個(gè)運(yùn)行中的ES實(shí)例是一個(gè)節(jié)點(diǎn),一個(gè)集群中有一個(gè)或者多個(gè)節(jié)點(diǎn)泉坐,這些節(jié)點(diǎn)中的cluste.name是相同的为鳄。
我們在創(chuàng)建索引時(shí)可以指定這個(gè)索引的主分片數(shù)和副本分片數(shù),默認(rèn)是5個(gè)主分片腕让,1個(gè)副本分片(5*1=5個(gè)分片)孤钦。這些分片會(huì)均勻的分布在集群的所有數(shù)據(jù)節(jié)點(diǎn)中。
在客戶端發(fā)送請求時(shí),集群中所有的節(jié)點(diǎn)都能處理這個(gè)請求偏形,節(jié)點(diǎn)會(huì)找到文檔所存儲(chǔ)的分片静袖,并在該分片所在的節(jié)點(diǎn)做檢索,將結(jié)果返回給請求的那個(gè)節(jié)點(diǎn)做最終的過濾和組合俊扭,然后返回給客戶端队橙。
那么在往這個(gè)索引上面存儲(chǔ)文檔時(shí),會(huì)存儲(chǔ)到5個(gè)分片中的一個(gè)分片上萨惑,而這個(gè)路由是由下面的公式?jīng)Q定的
shard = hash(routing) % number_of_primary_shards
routing是可以設(shè)置的一個(gè)值喘帚,默認(rèn)是文檔的id
number_of_primary_shards是主分片的數(shù)量(5)
也正是這個(gè)公式的原因,所以規(guī)定了索引創(chuàng)建后不能修改主分片數(shù)
集群, 由多個(gè)節(jié)點(diǎn)組成,一個(gè)集群中有很多的很多個(gè)索引庫
setting, 配置集群中索引庫的信息, 每個(gè)索引庫默認(rèn)是5個(gè)分片和2個(gè)副本
節(jié)點(diǎn)?, 一臺(tái)物理機(jī)器
分片, 節(jié)點(diǎn)中存放的是某個(gè)索引庫的一個(gè)分片.
文檔, 文檔就是數(shù)據(jù)庫中的一行數(shù)據(jù)或者是一個(gè)爬蟲爬取回來的網(wǎng)頁信息或者是一個(gè)訂單信息.
字段, 字段是文檔中的屬性(id, title, contend, author,time)
mapping, 描述字段的信息(是否分析,是否存儲(chǔ),是否索引等)
副本, 就是分片的備份, 保存數(shù)據(jù)不丟失.
文檔
字段
mapping
2.使用方法
1.客戶端連接
spring-boot-data-elasticsearch(ElasticsearchRespositor搭配ElasticsearchTemplate使用)
TransportClient(7以后不支持)
RestHighLevelClient(推薦使用咒钟,版本推薦7)
2.maven倉庫
<properties>
? ? <!--不加請求會(huì)出現(xiàn)addParameters的報(bào)錯(cuò),-->
? ? <elasticsearch.version>7.3.2</elasticsearch.version>
</properties>
<dependency>
? ? <groupId>org.elasticsearch</groupId>
? ? <artifactId>elasticsearch</artifactId>
?? <version>7.3.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
? ? <artifactId>elasticsearch-rest-high-level-client</artifactId>
?? <version>7.3.2</version>
</dependency>
3.初始化客戶端
//初始化客戶端
RestHighLevelClient client =new RestHighLevelClient(
RestClient.builder(
new HttpHost("集群",9200,"http")
)
);
//創(chuàng)建查詢r(jià)equest
SearchRequest search =new SearchRequest();
//構(gòu)建查詢條件
SearchSourceBuilder sourceBuilder =new SearchSourceBuilder();
//matchQuery模糊查詢
sourceBuilder.query(QueryBuilders.matchQuery("xx","yy"));
//queryStringQuery全文查詢
sourceBuilder.query(QueryBuilders.queryStringQuery("xx"));
//短語匹配 ,和match查詢類似吹由,match_phrase查詢首先解析查詢字符串來產(chǎn)生一個(gè)詞條列表。
// 然后會(huì)搜索所有的詞條朱嘴,但只保留包含了所有搜索詞條的文檔倾鲫,并且詞條的位置要鄰接。
// 一個(gè)針對短語“中華共和國”的查詢不會(huì)匹配“中華人民共和國”萍嬉,因?yàn)闆]有含有鄰接在一起的“中華”和“共和國”詞條乌昔。
sourceBuilder.query(QueryBuilders.matchPhraseQuery("xx","yy"));
//multi_match多個(gè)字段匹配某字符串
sourceBuilder.query(QueryBuilders.multiMatchQuery("xx","yy","zz"));
//完全包含查詢,之前的查詢中,當(dāng)我們輸入“我天”時(shí)壤追,ES會(huì)把分詞后所有包含“我”和“天”的都查詢出來
//如果我們希望必須是包含了兩個(gè)字的才能被查詢出來磕道,那么我們就需要設(shè)置一下Operator。and或者or
sourceBuilder.query(QueryBuilders.matchQuery("xx","yy").operator(Operator.AND));
//BoolQueryBuilder合并查詢,使用合并查詢可以把多個(gè)查詢條件合并到一起
BoolQueryBuilder boolQuery =new BoolQueryBuilder();
//范圍查詢
boolQuery.must(QueryBuilders.rangeQuery("xx")
.gt("zz")
.lt("mm"));
sourceBuilder.query(boolQuery);
//把查詢條件添加到查詢r(jià)equest中
search.source(sourceBuilder);
//同步調(diào)用
SearchResponse response = client.search(search, RequestOptions.DEFAULT);
//異步調(diào)用
client.searchAsync(search, RequestOptions.DEFAULT, new ActionListener() {
@Override
? ? public void onResponse(SearchResponse searchResponse) {
}
@Override
? ? public void onFailure(Exception e) {
}
});
3.集群
1.ES 選主流程
每個(gè)節(jié)點(diǎn)結(jié)算最小的ID行冰,把它選舉為臨時(shí)Master溺蕉,然后對該master進(jìn)行投票;
每個(gè)節(jié)點(diǎn)收集票數(shù)悼做,當(dāng)票數(shù)大于指定的法定個(gè)數(shù)時(shí)疯特,成為Master,然后對加入的節(jié)點(diǎn)進(jìn)行集群信息廣播肛走。
法定個(gè)數(shù):有Master資格的節(jié)點(diǎn)數(shù): (n/2 + 1 )
流程描述
服務(wù)啟動(dòng)后漓雅,進(jìn)行ping操作,ping所有的節(jié)點(diǎn)朽色,然后得到一個(gè)fullPingResponses邻吞,本節(jié)點(diǎn)也加入該list;
創(chuàng)建兩個(gè)列表
活動(dòng)的Master列表 activeMasters:遍歷剛剛的fullPingResponses然后將每個(gè)節(jié)點(diǎn)認(rèn)為的Master存入該list葫男,正常情況下是只有一個(gè)Master抱冷;
候選Master列表 candidateMasters:遍歷剛剛的fullPingResponses然后將具備Master節(jié)點(diǎn)資格的節(jié)點(diǎn)加入此list;
如果activeMasters不為空腾誉,則從activeMasters中選擇徘层,否則從candidateMasters中選擇,從候選列表選擇時(shí)要先判斷候選者列表大小是否大于法定人數(shù)利职,否則失敗;
在從列表進(jìn)行選擇時(shí)趣效,直接對列表進(jìn)行排序,然后選出最小ID的節(jié)點(diǎn)充當(dāng)臨時(shí)節(jié)點(diǎn);
開始進(jìn)行投票猪贪,每個(gè)節(jié)點(diǎn)都向自己認(rèn)為的Master進(jìn)行joinRequest請求,此時(shí)會(huì)有兩種情況
本節(jié)點(diǎn)是Master時(shí):此時(shí)該節(jié)點(diǎn)進(jìn)行統(tǒng)計(jì)跷敬,統(tǒng)計(jì)發(fā)送過來的joinRequest個(gè)數(shù),如果在指定的時(shí)間(默認(rèn)30s热押,可配置)內(nèi)達(dá)到法定人數(shù)西傀,發(fā)布集群信息,并回復(fù)joinRequest請求桶癣,最后完成選舉拥褂,否則選舉失敗牙寞;
本節(jié)點(diǎn)不是Master時(shí):拒絕其他節(jié)點(diǎn)joinRequest饺鹃,向其認(rèn)為的Master發(fā)送joinRequest請求,并等待间雀,如果在指定的時(shí)間(1min悔详,可配置)未收到回復(fù)或異常重試3次都失敗了則選舉失敗,重新嘗試惹挟;如果收到的回復(fù)中沒有Master信息或者M(jìn)aster信息不是之前選擇的臨時(shí)Master節(jié)點(diǎn)則選舉失敗茄螃。
2.垃圾回收
不要更改默認(rèn)的垃圾回收器!
Elasticsearch 默認(rèn)的垃圾回收器( GC )是 CMS连锯。 這個(gè)垃圾回收器可以和應(yīng)用并行處理归苍,以便它可以最小化停頓。 然而运怖,它有兩個(gè) stop-the-world 階段霜医,處理大內(nèi)存也有點(diǎn)吃力。
盡管有這些缺點(diǎn)驳规,它還是目前對于像 Elasticsearch 這樣低延遲需求軟件的最佳垃圾回收器肴敛。官方建議使用 CMS。
3.重要配置修改
#數(shù)據(jù)保存路徑吗购,你可以通過逗號分隔指定多個(gè)目錄医男。
path.data: /path/to/data1,/path/to/data2?
#?日志路徑
# 插件路徑
path.plugins: /path/to/plugins
#最小主節(jié)點(diǎn)數(shù)
discovery.zen.minimum_master_nodes: 2
3.日志
Elasticsearch 會(huì)輸出很多日志,都放在?ES_HOME/logs?目錄下捻勉。默認(rèn)的日志記錄等級是?INFO?镀梭。它提供了適度的信息,但是又設(shè)計(jì)好了不至于讓你的日志太過龐大踱启。
你?可以?修改?logging.yml?文件然后重啟你的節(jié)點(diǎn)——但是這樣做即繁瑣還會(huì)導(dǎo)致不必要的宕機(jī)時(shí)間报账。作為替代研底,你可以通過?cluster-settings?API 更新日志記錄級別,就像我們前面剛學(xué)過的那樣透罢。
要實(shí)現(xiàn)這個(gè)更新榜晦,選擇你感興趣的日志器,然后在前面補(bǔ)上?logger.?羽圃。對根日志器你可以用?logger._root?來表示乾胶。
讓我們調(diào)高節(jié)點(diǎn)發(fā)現(xiàn)的日志記錄級別:
PUT /_cluster/settings
{??
? "transient" :
? ? ? ?{??
? ? ? ? ?"logger.discovery" : "DEBUG"? ?
? ? ? ? ?}
}
還有另一個(gè)日志叫?慢日志?。這個(gè)日志的目的是捕獲那些超過指定時(shí)間閾值的查詢和索引請求朽寞。這個(gè)日志用來追蹤由用戶產(chǎn)生的很慢的請求很有用识窿。
默認(rèn)情況,慢日志是不開啟的脑融。要開啟它喻频,需要定義具體動(dòng)作(query,fetch 還是 index)肘迎,你期望的事件記錄等級(?WARN?半抱、?DEBUG?等),以及時(shí)間閾值膜宋。
這是一個(gè)索引級別的設(shè)置窿侈,也就是說可以獨(dú)立應(yīng)用給單個(gè)索引:
PUT /my_index/_settings{??
?"index.search.slowlog.threshold.query.warn" : "10s", ? ? "index.search.slowlog.threshold.fetch.debug": "500ms", ? ? "index.indexing.slowlog.threshold.index.info": "5s"?
獲取慢于 500 毫秒輸出一個(gè)?DEBUG?日志秋茫。
你也可以在?elasticsearch.yml?文件里定義這些閾值。沒有閾值設(shè)置的索引會(huì)自動(dòng)繼承在靜態(tài)配置文件里配置的參數(shù)肛著。
一旦閾值設(shè)置過了圆兵,你可以和其他日志器一樣切換日志記錄等級:
PUT /_cluster/settings
{??
? ?"transient" :
? ? ? ?{? ? ??
? "logger.index.search.slowlog" : "DEBUG",
?? "logger.index.indexing.slowlog" : "WARN"
面試-------------------------
Elasticsearch 查詢數(shù)據(jù)的工作原理是什么殉农?
ES 寫入數(shù)據(jù)的工作原理是什么敖莅蟆尤仍?ES 查詢數(shù)據(jù)的工作原理是什么啊怠惶?底層的 Lucene 介紹一下唄耀态?倒排索引了解嗎轮傍?
面試官心理分析
問這個(gè),其實(shí)面試官就是要看看你了解不了解 es 的一些基本原理首装,因?yàn)橛?es 無非就是寫入數(shù)據(jù)创夜,搜索數(shù)據(jù)。你要是不明白你發(fā)起一個(gè)寫入和搜索請求的時(shí)候仙逻,es 在干什么驰吓,那你真的是......
對 es 基本就是個(gè)黑盒涧尿,你還能干啥?你唯一能干的就是用 es 的 api 讀寫數(shù)據(jù)了檬贰。要是出點(diǎn)什么問題姑廉,你啥都不知道,那還能指望你什么呢偎蘸?
面試題剖析
es 寫數(shù)據(jù)過程
客戶端選擇一個(gè) node 發(fā)送請求過去庄蹋,這個(gè) node 就是coordinating node(協(xié)調(diào)節(jié)點(diǎn))瞬内。
coordinating node對 document 進(jìn)行路由迷雪,將請求轉(zhuǎn)發(fā)給對應(yīng)的 node(有 primary shard)。
實(shí)際的 node 上的primary shard處理請求虫蝶,然后將數(shù)據(jù)同步到replica node章咧。
coordinating node如果發(fā)現(xiàn)primary node和所有replica node都搞定之后,就返回響應(yīng)結(jié)果給客戶端能真。
es-write
es 讀數(shù)據(jù)過程
可以通過doc id來查詢赁严,會(huì)根據(jù)doc id進(jìn)行 hash,判斷出來當(dāng)時(shí)把doc id分配到了哪個(gè) shard 上面去粉铐,從那個(gè) shard 去查詢疼约。
客戶端發(fā)送請求到任意一個(gè) node,成為coordinate node蝙泼。
coordinate node對doc id進(jìn)行哈希路由程剥,將請求轉(zhuǎn)發(fā)到對應(yīng)的 node,此時(shí)會(huì)使用round-robin隨機(jī)輪詢算法汤踏,在primary shard以及其所有 replica 中隨機(jī)選擇一個(gè)织鲸,讓讀請求負(fù)載均衡。
接收請求的 node 返回 document 給coordinate node溪胶。
coordinate node返回 document 給客戶端搂擦。
es 搜索數(shù)據(jù)過程
es 最強(qiáng)大的是做全文檢索,就是比如你有三條數(shù)據(jù):
java真好玩兒啊
java好難學(xué)啊
j2ee特別牛Copy?to?clipboardErrorCopied
你根據(jù)java關(guān)鍵詞來搜索哗脖,將包含java的document給搜索出來瀑踢。es 就會(huì)給你返回:java真好玩兒啊,java好難學(xué)啊才避。
客戶端發(fā)送請求到一個(gè)coordinate node丘损。
協(xié)調(diào)節(jié)點(diǎn)將搜索請求轉(zhuǎn)發(fā)到所有的 shard 對應(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ù)的合并、排序肢娘、分頁等操作呈础,產(chǎn)出最終結(jié)果舆驶。
fetch phase:接著由協(xié)調(diào)節(jié)點(diǎn)根據(jù)doc id去各個(gè)節(jié)點(diǎn)上拉取實(shí)際的document數(shù)據(jù),最終返回給客戶端而钞。
寫請求是寫入 primary shard沙廉,然后同步給所有的 replica shard;讀請求可以從 primary shard 或 replica shard 讀取臼节,采用的是隨機(jī)輪詢算法撬陵。
寫數(shù)據(jù)底層原理
es-write-detail
先寫入內(nèi)存 buffer,在 buffer 里的時(shí)候數(shù)據(jù)是搜索不到的网缝;同時(shí)將數(shù)據(jù)寫入 translog 日志文件巨税。
如果 buffer 快滿了,或者到一定時(shí)間粉臊,就會(huì)將內(nèi)存 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 操作灶芝,如果 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)級別的一個(gè)內(nèi)存緩存中去。只要buffer中的數(shù)據(jù)被 refresh 操作刷入os cache中只壳,這個(gè)數(shù)據(jù)就可以被搜索到了俏拱。
為什么叫 es 是準(zhǔn)實(shí)時(shí)的?NRT吼句,全稱near real-time锅必。默認(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?buffer 了,數(shù)據(jù)在 translog 里面已經(jīng)持久化到磁盤去一份了绷柒。
重復(fù)上面的步驟志于,新的數(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á)到一定長度的時(shí)候瓤荔,就會(huì)觸發(fā)commit操作净蚤。
commit 操作發(fā)生第一步,就是將 buffer 中現(xiàn)有數(shù)據(jù)refresh到os cache中去输硝,清空 buffer今瀑。然后,將一個(gè)commit point寫入磁盤文件点把,里面標(biāo)識著這個(gè)commit point對應(yīng)的所有segment file橘荠,同時(shí)強(qiáng)行將os cache中目前所有的數(shù)據(jù)都fsync到磁盤文件中去。最后清空現(xiàn)有 translog 日志文件郎逃,重啟一個(gè) translog哥童,此時(shí) commit 操作完成。
這個(gè) commit 操作叫做flush褒翰。默認(rèn) 30 分鐘自動(dòng)執(zhí)行一次flush贮懈,但如果 translog 過大,也會(huì)觸發(fā)flush优训。flush 操作就對應(yīng)著 commit 的全過程朵你,我們可以通過 es api,手動(dòng)執(zhí)行 flush 操作揣非,手動(dòng)將 os cache 中的數(shù)據(jù) fsync 強(qiáng)刷到磁盤上去抡医。
translog 日志文件的作用是什么?你執(zhí)行 commit 操作之前早敬,數(shù)據(jù)要么是停留在 buffer 中忌傻,要么是停留在 os cache 中毛仪,無論是 buffer 還是 os cache 都是內(nèi)存,一旦這臺(tái)機(jī)器死了芯勘,內(nèi)存中的數(shù)據(jù)就全丟了箱靴。所以需要將數(shù)據(jù)對應(yīng)的操作寫入一個(gè)專門的日志文件translog中,一旦此時(shí)機(jī)器宕機(jī)荷愕,再次重啟的時(shí)候衡怀,es 會(huì)自動(dòng)讀取 translog 日志文件中的數(shù)據(jù),恢復(fù)到內(nèi)存 buffer 和 os cache 中去安疗。
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ù)的。有 5 秒的數(shù)據(jù)料滥,停留在 buffer然眼、translog os cache、segment file os cache 中幔欧,而不在磁盤上罪治,此時(shí)如果宕機(jī),會(huì)導(dǎo)致 5 秒的數(shù)據(jù)丟失礁蔗。
總結(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ù)全沒,最多會(huì)有 5s 的數(shù)據(jù)丟失)瘤缩,translog 大到一定程度喇完,或者默認(rèn)每隔 30mins,會(huì)觸發(fā) commit 操作剥啤,將緩沖區(qū)的數(shù)據(jù)都 flush 到 segment file 磁盤文件中锦溪。
數(shù)據(jù)寫入 segment file 之后,同時(shí)就建立好了倒排索引府怯。
刪除/更新數(shù)據(jù)底層原理
如果是刪除操作刻诊,commit 的時(shí)候會(huì)生成一個(gè).del文件,里面將某個(gè) doc 標(biāo)識為deleted狀態(tài)牺丙,那么搜索的時(shí)候根據(jù).del文件就知道這個(gè) doc 是否被刪除了则涯。
如果是更新操作,就是將原來的 doc 標(biāo)識為deleted狀態(tài)冲簿,然后新寫入一條數(shù)據(jù)粟判。
buffer 每 refresh 一次,就會(huì)產(chǎn)生一個(gè)segment file民假,所以默認(rèn)情況下是 1 秒鐘一個(gè)segment file浮入,這樣下來segment file會(huì)越來越多龙优,此時(shí)會(huì)定期執(zhí)行 merge羊异。每次 merge 的時(shí)候,會(huì)將多個(gè)segment file合并成一個(gè)彤断,同時(shí)這里會(huì)將標(biāo)識為deleted的 doc 給物理刪除掉野舶,然后將新的segment file寫入磁盤,這里會(huì)寫一個(gè)commit point宰衙,標(biāo)識所有新的segment file平道,然后打開segment file供搜索使用,同時(shí)刪除舊的segment file供炼。
底層 lucene
簡單來說一屋,lucene 就是一個(gè) jar 包,里面包含了封裝好的各種建立倒排索引的算法代碼袋哼。我們用 Java 開發(fā)的時(shí)候冀墨,引入 lucene jar,然后基于 lucene 的 api 去開發(fā)就可以了涛贯。
通過 lucene诽嘉,我們可以將已有的數(shù)據(jù)建立索引,lucene 會(huì)在本地磁盤上面,給我們組織索引的數(shù)據(jù)結(jié)構(gòu)虫腋。
倒排索引
在搜索引擎中骄酗,每個(gè)文檔都有一個(gè)對應(yīng)的文檔 ID,文檔內(nèi)容被表示為一系列關(guān)鍵詞的集合悦冀。例如趋翻,文檔 1 經(jīng)過分詞,提取了 20 個(gè)關(guān)鍵詞盒蟆,每個(gè)關(guān)鍵詞都會(huì)記錄它在文檔中出現(xiàn)的次數(shù)和出現(xiàn)位置嘿歌。
那么,倒排索引就是關(guān)鍵詞到文檔ID 的映射茁影,每個(gè)關(guān)鍵詞都對應(yīng)著一系列的文件宙帝,這些文件中都出現(xiàn)了關(guān)鍵詞。
舉個(gè)栗子募闲。
有以下文檔:
DocIdDoc
1谷歌地圖之父跳槽 Facebook
2谷歌地圖之父加盟 Facebook
3谷歌地圖創(chuàng)始人拉斯離開谷歌加盟 Facebook
4谷歌地圖之父跳槽 Facebook 與 Wave 項(xiàng)目取消有關(guān)
5谷歌地圖之父拉斯加盟社交網(wǎng)站 Facebook
對文檔進(jìn)行分詞之后步脓,得到以下倒排索引。
WordIdWordDocIds
1谷歌1, 2, 3, 4, 5
2地圖1, 2, 3, 4, 5
3之父1, 2, 4, 5
4跳槽1, 4
5Facebook1, 2, 3, 4, 5
6加盟2, 3, 5
7創(chuàng)始人3
8拉斯3, 5
9離開3
10與4
......
另外浩螺,實(shí)用的倒排索引還可以記錄更多的信息靴患,比如文檔頻率信息,表示在文檔集合中有多少個(gè)文檔包含某個(gè)單詞要出。
那么鸳君,有了倒排索引,搜索引擎可以很方便地響應(yīng)用戶的查詢患蹂。比如用戶輸入查詢Facebook或颊,搜索系統(tǒng)查找倒排索引,從中讀出包含這個(gè)單詞的文檔传于,這些文檔就是提供給用戶的搜索結(jié)果囱挑。
要注意倒排索引的兩個(gè)重要細(xì)節(jié):
倒排索引中的所有詞項(xiàng)對應(yīng)一個(gè)或多個(gè)文檔;
倒排索引中的詞項(xiàng)根據(jù)字典順序升序排列
上面只是一個(gè)簡單的例子沼溜,并沒有嚴(yán)格按照字典順序升序排列平挑。