[11]elasticsearch源碼深入分析——文檔(document)的落地

本篇為elasticsearch源碼分析系列文章的第十一篇,本篇開始進(jìn)入索引有關(guān)操作的講解。以后的若干篇我們會(huì)連續(xù)討論文檔的創(chuàng)建,檢索,更新,刪除,版本控制等一系列內(nèi)容。

文檔

ElasticSearch存儲(chǔ)系統(tǒng)中的實(shí)體叫做文檔,document。如果用關(guān)系型數(shù)據(jù)庫(kù)來(lái)類比的話,一個(gè)文檔相當(dāng)于數(shù)據(jù)庫(kù)中的一行記錄。ElasticSearch中的文檔有個(gè)特點(diǎn),相同字段必須是相同的類型,也就是說(shuō)所有包含title字段的文檔,title字段類型都必須一樣,要么同為string,要么同為int。

文檔由多個(gè)字段組成,每個(gè)字段的類型可以是,文本,數(shù)值,日期,還可以是字符串?dāng)?shù)組這種復(fù)雜的類型。字段類型在ElasticSearch中非常重要,它涉及到各種分析和排序操作如何被執(zhí)行的信息。Elastic官方推薦我們使用Mapping映射來(lái)干預(yù)字段的類型。與關(guān)系型數(shù)據(jù)庫(kù)不同,ElasticSearch不需要有固定的結(jié)構(gòu),每個(gè)文檔可以有不同的字段,此外,在程序開發(fā)期間,不必確定有哪些字段。

文檔的類型

在ElasticSearch中,文檔類型可以讓程序員輕松的區(qū)分單個(gè)索引中的不同對(duì)象。每個(gè)文檔可以有不同的結(jié)構(gòu),但在實(shí)際生產(chǎn)環(huán)境中我們還是推薦將文檔中的類型詳細(xì)化,這樣對(duì)以后的開發(fā)會(huì)有很大的幫助。

文檔類型的映射

上面提到的映射,指的是ElasticSearch在映射中存儲(chǔ)有關(guān)字段的信息,這種類型信息就是映射Mapping。每個(gè)文檔類型都有自己的映射,即使在初始化時(shí)沒有提前定義。在涉及到全文搜索和倒排索引的內(nèi)容中,會(huì)有對(duì)文檔分析的過(guò)程,在這個(gè)過(guò)程中每個(gè)字段都必須根據(jù)不同類型作相應(yīng)的分析。舉例來(lái)說(shuō),對(duì)數(shù)值字段和文本字段的分析肯定是不同的分析過(guò)程,數(shù)字的分析就不應(yīng)該是按照字母的排序來(lái)分析。

使用ElasticSearch的ResultAPI來(lái)新建文檔

在ElasticSearch中,所有文檔都是數(shù)據(jù),所有數(shù)據(jù)都有定義好的索引和類型?,F(xiàn)在我們通過(guò)一個(gè)比較常見的例子來(lái)建立文檔:

創(chuàng)建文檔

上面操作的意思是,我們建立了一個(gè)名為article的索引和名為computer的類型,文檔的標(biāo)示符為1

如果一切正常,那么這種使用RESTfulAPI的創(chuàng)建方式會(huì)返回一個(gè)JSON響應(yīng),與如下輸出類似:

文檔創(chuàng)建成功

前面的相應(yīng)包含了操作狀態(tài)的信息,顯示了創(chuàng)建的文檔放在什么地方,還包含了文檔的唯一標(biāo)示符_id和當(dāng)前版本_version的信息。每次ElasticSearch的更新版本都會(huì)自動(dòng)遞增。

而且ElasticSearch在創(chuàng)建文檔時(shí),如果沒指定文檔標(biāo)示符,那么這個(gè)文檔的標(biāo)示符會(huì)被自動(dòng)創(chuàng)建。

自動(dòng)創(chuàng)建文檔標(biāo)示符

這都是怎么做到的呢?我們會(huì)在下一節(jié)從源碼的角度解釋。

ElasticSearch源碼如何新建文檔

在以前文章中強(qiáng)調(diào)的Node實(shí)例化的過(guò)程中,加載了ActionModule這個(gè)模塊,這個(gè)模塊是接收客戶端發(fā)送的RESTful請(qǐng)求的的模塊,ActionModule的加載如下:

ActionModule actionModule = new ActionModule(false, settings, clusterModule.getIndexNameExpressionResolver(), settingsModule.getIndexScopedSettings(), settingsModule.getClusterSettings(), settingsModule.getSettingsFilter(), threadPool, pluginsService.filterPlugins(ActionPlugin.class), client, circuitBreakerService, usageService);

在加載完了ActionModule后,會(huì)通過(guò)ActionModule的方法initRestHandlers()來(lái)初始化HTTP處理程序,這個(gè)handler就能解析客戶端通過(guò)http協(xié)議發(fā)送到ElasticSearch集群中的RESTful請(qǐng)求。

加載RestIndexActionindex處理器,

registerHandler.accept(new RestIndexAction(settings, restController))

如下圖所示,注冊(cè)不同的REST處理程序路徑,以用來(lái)不同的匹配請(qǐng)求。

不同的索引請(qǐng)求

可以看到控制器匹配路徑中,有index,type和id,如果不指定id,則id會(huì)被自動(dòng)創(chuàng)建,而且不指定id必須用POST方法來(lái)發(fā)送請(qǐng)求。

因?yàn)镋lasticSearch中的Controller底層都是Netty實(shí)現(xiàn)的。所以在端口綁定后,Netty4HttpChannel會(huì)去監(jiān)聽端口收到的http請(qǐng)求。在ElasticSearch的Controller接收到Netty4HttpChannel轉(zhuǎn)發(fā)的請(qǐng)求后,會(huì)調(diào)用RestIndexAction中的方法prepareRequest()。該方法返回RestChannelConsumer類型的實(shí)例,該實(shí)例是虛擬類BaseRestHandler中的Functional接口。閱讀這個(gè)接口的定義的方法,可以知道ElasticSearch中的REST請(qǐng)求是通過(guò)準(zhǔn)備一個(gè)表示通道的請(qǐng)求執(zhí)行的通道消費(fèi)者(a channel consumer)來(lái)處理的。

接收到請(qǐng)求后開始構(gòu)建IndexRequest,這個(gè)實(shí)例作用是將JSON類型的文檔轉(zhuǎn)換為一個(gè)特定的和可搜索的索引。

IndexRequest回首先取得RestRequest中的三個(gè)構(gòu)造實(shí)例必須的參數(shù):

  • index:文檔的索引
  • type:文檔的類型
  • id:文檔指定的標(biāo)識(shí)

然后在依次取得一些附加參數(shù):

  • routing:控制分片的路由請(qǐng)求。使用這個(gè)值來(lái)哈希的分片,而不是id。
  • parent:設(shè)置document的父id。
  • pipeline:在執(zhí)行索引document前,設(shè)置攝取管道(ingest pipeline)
  • source:設(shè)置document索引的字節(jié)形式。
  • timeout:超時(shí)時(shí)間
  • refresh:解析刷新策略
  • version_type:設(shè)置版本類型
  • op_type:字符串,用來(lái)表示是索引數(shù)據(jù)還是新建數(shù)據(jù)

參數(shù)詳情如下圖:

image.png

這參數(shù)都是NodeClient在索引文檔時(shí)候需要用到的數(shù)據(jù),NodeClient在Node初始化時(shí)候就加載完成,他是用來(lái)在本地節(jié)點(diǎn)上執(zhí)行操作的模擬客戶端。

方法prepareRequest最后返回channel -> client.index(indexRequest, new RestStatusToXContentListener<>(channel, r -> r.getLocation(indexRequest.routing()))),因?yàn)樵摲椒ㄐ枰祷?strong>RestChannelConsumer類型的返回值,所以改寫成jdk7版本易于理解的代碼版本如下圖所示:

返回代碼

該段代碼中最重要的就是NodeClient的index()方法,此方法的關(guān)鍵是新建了一個(gè)Task,這個(gè)Task包含了id,type,action,description,parentTask,startTime等信息。

該task在老版本會(huì)被TransportIndexAction處理,但是6.0版本后TransportBulkAction已經(jīng)取代了TransportIndexAction。task會(huì)被當(dāng)做參數(shù)送入TransportBulkAction的doExecute方法中,另外兩個(gè)參數(shù)是BulkRequest和ActionListener

void doExecute(Task task, BulkRequest bulkRequest, ActionListener<BulkResponse> listener)

BulkRequest中包含了該文檔存儲(chǔ)的信息,而ActionListener則用來(lái)監(jiān)聽action的響應(yīng)或失敗,用以做回調(diào)操作。

doExecute方法主要做了以下操作:

  • 收集請(qǐng)求中的所有索引
  • 過(guò)濾掉不存在的索引,同時(shí)建立一個(gè)我們無(wú)法創(chuàng)建的索引圖。判斷不存在的索引和無(wú)法創(chuàng)建的索引主要是看索引是否有別名
  • 如果有遺漏的索引,則創(chuàng)建缺少的所有索引。注意在所有的創(chuàng)建完成后開始批量處理數(shù)據(jù)

然后執(zhí)行TransportBulkAction類的executeBulk方法,完成數(shù)據(jù)的落地。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容