在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    ES 源碼分析之?dāng)?shù)據(jù)類型轉(zhuǎn)換

    公司有的小伙伴問我,為什么不推薦我們使用 nested 結(jié)構(gòu)呢,還說性能低。那么,ES 針對 nested 之類的結(jié)構(gòu)。因?yàn)镋S 源碼我也基本看完了。索性,直接寫成筆記。比直接在代碼里面寫注釋來的更舒心點(diǎn)。

    1問題描述

    • ES 是 lucene 不僅僅是集群版的概念,還有涉及到支持豐富的數(shù)據(jù)類型。如 nested 、object 等等結(jié)構(gòu)。它是怎么支持的呢?
    • ES 還支持 _id、_version 等等字段。這種是怎么存儲的呢?
    • 聽說 ES 的 parent doc 和 nested doc 是分開來存儲的,那么獲取的時(shí)候,他們是通過哪種關(guān)系關(guān)聯(lián)的呢?

    2類型轉(zhuǎn)換

    2.1初步代碼入口

    代碼具體入口 org.elasticsearch.index.shard.IndexShard#prepareIndex

    public static Engine.Index prepareIndex(DocumentMapperForType docMapper, SourceToParse source, long seqNo, long primaryTerm, long version, VersionType versionType, Engine.Operation.Origin origin, long autoGeneratedIdTimestamp, boolean isRetry, long ifSeqNo, long ifPrimaryTerm) { long startTime = System.nanoTime(); // 涉及到 nested 等等結(jié)構(gòu)的轉(zhuǎn)換,直接看【2.2 類型具體轉(zhuǎn)換代碼】 ParsedDocument doc = docMapper.getDocumentMapper().parse(source); // Mapping 是否要處理 if (docMapper.getMapping() != null) { doc.addDynamicMappingsUpdate(docMapper.getMapping()); } // _id 轉(zhuǎn) uid。這里是為了數(shù)據(jù)能保持整齊,方便壓縮。可以參考 【哈夫曼編碼】。 Term uid = new Term(IdFieldMapper.NAME, Uid.encodeId(doc.id())); return new Engine.Index(uid, doc, seqNo, primaryTerm, version, versionType, origin, startTime, autoGeneratedIdTimestamp, isRetry, ifSeqNo, ifPrimaryTerm); }

    2.2類型具體轉(zhuǎn)換代碼

    /** * 內(nèi)部轉(zhuǎn)換文檔,如果有 nested 結(jié)構(gòu),需要再次轉(zhuǎn)換一下 * @param mapping * @param context * @param parser * @throws IOException */ private static void internalParseDocument(Mapping mapping, MetadataFieldMapper[] metadataFieldsMappers, ParseContext context, XContentParser parser) throws IOException { final boolean emptyDoc = isEmptyDoc(mapping, parser); /** * 預(yù)處理,為 root document 拆開,添加如下:比如,_id、_version 也是一個(gè) document,具體看下面的 【2.3 支持 _id 之類的字段】 */ for (MetadataFieldMapper metadataMapper : metadataFieldsMappers) { metadataMapper.preParse(context); } if (mapping.root.isEnabled() == false) { // entire type is disabled parser.skipChildren(); } else if (emptyDoc == false) { // 轉(zhuǎn)換對象或者 nested 結(jié)構(gòu),這個(gè)方法會反復(fù)遞歸調(diào)用。主要是 object 結(jié)構(gòu)或者 nested 結(jié)構(gòu) parseObjectOrNested(context, mapping.root); } // 為各個(gè)非 root document 添加 _version 等等字段 for (MetadataFieldMapper metadataMapper : metadataFieldsMappers) { metadataMapper.postParse(context); } }

    2.3前置處理之支持_id之類的字段

    代碼位置:org.elasticsearch.index.mapper.MetadataFieldMapper#preParse下面只貼出 _id 的處理

    /** * _id 也是一個(gè) doc * @param context */ @Override public void preParse(ParseContext context) { BytesRef id = Uid.encodeId(context.sourceToParse().id()); context.doc().add(new Field(NAME, id, Defaults.FIELD_TYPE)); }

    這里只是了其中的一個(gè)例子:_id ,其他的比如 _version、_seqno、_source 等等處理也類似。

    2.4轉(zhuǎn)換復(fù)雜的結(jié)構(gòu),比如nested結(jié)構(gòu)

    ES 在轉(zhuǎn)換 nested 結(jié)構(gòu)的時(shí)候,比較有意思。

    2.4.1類型轉(zhuǎn)換整體入口

    /** * 轉(zhuǎn)換 object 或者 nested 結(jié)構(gòu)的,這里會出現(xiàn)遞歸調(diào)用,主要是為了解決 object、nested 結(jié)構(gòu) * @param context * @param mapper * @throws IOException */ static void parseObjectOrNested(ParseContext context, ObjectMapper mapper) throws IOException { if (mapper.isEnabled() == false) { context.parser().skipChildren(); return; } XContentParser parser = context.parser(); XContentParser.Token token = parser.currentToken(); if (token == XContentParser.Token.VALUE_NULL) { // the object is null (“obj1” : null), simply bail return; } String currentFieldName = parser.currentName(); if (token.isValue()) { throw new MapperParsingException(“object mapping for [” + mapper.name() + “] tried to parse field [” + currentFieldName + “] as object, but found a concrete value”); } ObjectMapper.Nested nested = mapper.nested(); // 如果是 nested 結(jié)構(gòu),每次都會new 一個(gè)空白的 document ,而且,這個(gè)方法 #{innerParseObject},是遞歸實(shí)現(xiàn),把 object 或者 document 變成多個(gè) document if (nested.isNested()) { // 進(jìn)入下方的:【2.4.2 nested 轉(zhuǎn)換初步入口】 context = nestedContext(context, mapper); } // if we are at the end of the previous object, advance if (token == XContentParser.Token.END_OBJECT) { token = parser.nextToken(); } if (token == XContentParser.Token.START_OBJECT) { // if we are just starting an OBJECT, advance, this is the object we are parsing, we need the name first token = parser.nextToken(); } // 轉(zhuǎn)換對象 innerParseObject(context, mapper, parser, currentFieldName, token); // restore the enable path flag if (nested.isNested()) { nested(context, nested); } }

    2.4.2nested轉(zhuǎn)換初步入口

    /** * 內(nèi)部轉(zhuǎn)換 nested 結(jié)構(gòu),生成一個(gè)空白的 nested 結(jié)構(gòu) * TODO nested 文檔的 _id 既然跟父文檔的一樣,lucene 寫入每個(gè) doc ,都是拼接。那么,在get 的時(shí)候,自然會獲取到相同的 _id 多個(gè)文檔,包含了 nested 結(jié)構(gòu)。然后,再內(nèi)部轉(zhuǎn)換為我們 最想要的結(jié)果。 * @param context * @param mapper * @return */ private static ParseContext nestedContext(ParseContext context, ObjectMapper mapper) { // 創(chuàng)建 nested 上下文,并且,new 一個(gè)空白的 document。為后面的 nested 的字段或者對象之類的,全部加上 context = context.createNestedContext(mapper.fullPath()); ParseContext.Document nestedDoc = context.doc(); ParseContext.Document parentDoc = nestedDoc.getParent(); // We need to add the uid or id to this nested Lucene document too, // If we do not do this then when a document gets deleted only the root Lucene document gets deleted and // not the nested Lucene documents! Besides the fact that we would have zombie Lucene documents, the ordering of // documents inside the Lucene index (document blocks) will be incorrect, as nested documents of different root // documents are then aligned with other root documents. This will lead tothe nested query, sorting, aggregations // and inner hits to fail or yield incorrect results. IndexableField idField = parentDoc.getField(IdFieldMapper.NAME); if (idField != null) { // We just need to store the id as indexed field, so that IndexWriter#deleteDocuments(term) can then // delete it when the root document is deleted too. nestedDoc.add(new Field(IdFieldMapper.NAME, idField.binaryValue(), IdFieldMapper.Defaults.NESTED_FIELD_TYPE)); } else { throw new IllegalStateException(“The root document of a nested document should have an _id field”); } // the type of the nested doc starts with __, so we can identify that its a nested one in filters // note, we don’t prefix it with the type of the doc since it allows us to execute a nested query // across types (for example, with similar nested objects) nestedDoc.add(new Field(TypeFieldMapper.NAME, mapper.nestedTypePathAsString(), TypeFieldMapper.Defaults.NESTED_FIELD_TYPE)); return context; }

    仔細(xì)看看里面的英文。主要的一點(diǎn)是:nested 結(jié)構(gòu)的 _id 和 parent 的 _id 保持一致。那么,通過 GET docId 這種操作,就可以拿到所有的文檔了。而且,刪除的時(shí)候,特別的方便。算是 ES 這種的一個(gè)方案吧。

    2.4.3數(shù)據(jù)處理

    每個(gè)字段的填充入口在:org.elasticsearch.index.mapper.DocumentParser#innerParseObject這里是一個(gè)遞歸調(diào)用的操作。比較繞。

    2.5后置處理之設(shè)置_version等等

    下面貼出來 _version 的處理代碼的入口:org.elasticsearch.index.mapper.VersionFieldMapper#postParse,可以看看具體的實(shí)現(xiàn)。

    @Override public void postParse(ParseContext context) { // In the case of nested docs, let’s fill nested docs with version=1 so that Lucene doesn’t write a Bitset for documents // that don’t have the field. This is consistent with the default value for efficiency. Field version = context.version(); assert version != null; for (Document doc : context.nonRootDocuments()) { // 為此 doc 添加一個(gè) _version 字段 doc.add(version); } }

    這里支持舉了 _version 舉個(gè)例子,其他類似。

    3總結(jié)

    • ES 是 lucene 不僅僅是集群版的概念,還有涉及到支持豐富的數(shù)據(jù)類型。如 nested 、object 等等結(jié)構(gòu)。它是怎么支持的呢?答:ES 針對 nested 、object 直接拍平處理
    • ES 還支持 _id、_version 等等字段。這種是怎么存儲的呢?答:ES 針對 _id 、_version 是保存為獨(dú)立的文檔的。
    • 聽說 ES 的 parent doc 和 nested doc 是分開來存儲的,那么獲取的時(shí)候,他們是通過那種關(guān)系關(guān)聯(lián)的呢?答:通過 root Doc 的 ID 來做關(guān)聯(lián)的。

    4其他

    后續(xù)請關(guān)注 ES 寫入流程。讓我們看看 ES 是如何處理分布式請求及保證高可用的。

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年6月28日 17:26
    下一篇 2022年6月28日 17:26

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息