不少使用Elasticsearch的同窗會關心數據存儲在ES中的存儲容量,會有這樣的疑問:xxTB的數據入到ES會使用多少存儲空間。這個問題其實很難直接回答的,只有數據寫入ES後,才能觀察到實際的存儲空間。好比一樣是1TB的數據,寫入ES的存儲空間可能差距會很是大,可能小到只有300~400GB,也可能多到6-7TB,爲何會形成這麼大的差距呢?究其緣由,咱們來探究下Elasticsearch中的數據是如何存儲。文章中我以Elasticsearch 2.3版本爲示例,對應的lucene版本是5.5,Elasticsearch如今已經來到了6.5版本,數字類型、列存等存儲結構有些變化,但基本的概念變化很少,文章中的內容依然適用。html
Elasticsearch對外提供的是index的概念,能夠類比爲DB,用戶查詢是在index上完成的,每一個index由若干個shard組成,以此來達到分佈式可擴展的能力。好比下圖是一個由10個shard組成的index。git
shard是Elasticsearch數據存儲的最小單位,index的存儲容量爲全部shard的存儲容量之和。Elasticsearch集羣的存儲容量則爲全部index存儲容量之和。github
一個shard就對應了一個lucene的library。對於一個shard,Elasticsearch增長了translog的功能,相似於HBase WAL,是數據寫入過程當中的中間數據,其他的數據都在lucene庫中管理的。算法
因此Elasticsearch索引使用的存儲內容主要取決於lucene中的數據存儲。apache
下面咱們主要看下lucene的文件內容,在瞭解lucene文件內容前,你們先了解些lucene的基本概念。json
segment : lucene內部的數據是由一個個segment組成的,寫入lucene的數據並不直接落盤,而是先寫在內存中,通過了refresh間隔,lucene纔將該時間段寫入的所有數據refresh成一個segment,segment多了以後會進行merge成更大的segment。lucene查詢時會遍歷每一個segment完成。因爲lucene* 寫入的數據是在內存中完成,因此寫入效率很是高。可是也存在丟失數據的風險,因此Elasticsearch基於此現象實現了translog,只有在segment數據落盤後,Elasticsearch纔會刪除對應的translog。數組
doc : doc表示lucene中的一條記錄app
field :field表示記錄中的字段概念,一個doc由若干個field組成。less
term :term是lucene中索引的最小單位,某個field對應的內容若是是全文檢索類型,會將內容進行分詞,分詞的結果就是由term組成的。若是是不分詞的字段,那麼該字段的內容就是一個term。dom
倒排索引(inverted index): lucene索引的通用叫法,即實現了term到doc list的映射。
正排數據:搜索引擎的通用叫法,即原始數據,能夠理解爲一個doc list。
docvalues :Elasticsearch中的列式存儲的名稱,Elasticsearch除了存儲原始存儲、倒排索引,還存儲了一份docvalues,用做分析和排序。
lucene包的文件是由不少segment文件組成的,segments_xxx文件記錄了lucene包下面的segment文件數量。每一個segment會包含以下的文件。
Name | Extension | Brief Description |
---|---|---|
Segment Info | .si | segment的元數據文件 |
Compound File | .cfs, .cfe | 一個segment包含了以下表的各個文件,爲減小打開文件的數量,在segment小的時候,segment的全部文件內容都保存在cfs文件中,cfe文件保存了lucene各文件在cfs文件的位置信息 |
Fields | .fnm | 保存了fields的相關信息 |
Field Index | .fdx | 正排存儲文件的元數據信息 |
Field Data | .fdt | 存儲了正排存儲數據,寫入的原文存儲在這 |
Term Dictionary | .tim | 倒排索引的元數據信息 |
Term Index | .tip | 倒排索引文件,存儲了全部的倒排索引數據 |
Frequencies | .doc | 保存了每一個term的doc id列表和term在doc中的詞頻 |
Positions | .pos | Stores position information about where a term occurs in the index 全文索引的字段,會有該文件,保存了term在doc中的位置 |
Payloads | .pay | Stores additional per-position metadata information such as character offsets and user payloads 全文索引的字段,使用了一些像payloads的高級特性會有該文件,保存了term在doc中的一些高級特性 |
Norms | .nvd, .nvm | 文件保存索引字段加權數據 |
Per-Document Values | .dvd, .dvm | lucene的docvalues文件,即數據的列式存儲,用做聚合和排序 |
Term Vector Data | .tvx, .tvd, .tvf | Stores offset into the document data file 保存索引字段的矢量信息,用在對term進行高亮,計算文本相關性中使用 |
Live Documents | .liv | 記錄了segment中刪除的doc |
下面咱們以真實的數據做爲示例,看看lucene中各種型數據的容量佔比。
寫100w數據,有一個uuid字段,寫入的是長度爲36位的uuid,字符串總爲3600w字節,約爲35M。
數據使用一個shard,不帶副本,使用默認的壓縮算法,寫入完成後merge成一個segment方便觀察。
使用線上默認的配置,uuid存爲不分詞的字符串類型。建立以下索引:
PUT test_field { "settings": { "index": { "number_of_shards": "1", "number_of_replicas": "0", "refresh_interval": "30s" } }, "mappings": { "type": { "_all": { "enabled": false }, "properties": { "uuid": { "type": "string", "index": "not_analyzed" } } } } }
首先寫入100w不一樣的uuid,使用磁盤容量細節以下:
health status index pri rep docs.count docs.deleted store.size pri.store.size green open test_field 1 0 1000000 0 122.7mb 122.7mb -rw-r--r-- 1 weizijun staff 41M Aug 19 21:23 _8.fdt -rw-r--r-- 1 weizijun staff 17K Aug 19 21:23 _8.fdx -rw-r--r-- 1 weizijun staff 688B Aug 19 21:23 _8.fnm -rw-r--r-- 1 weizijun staff 494B Aug 19 21:23 _8.si -rw-r--r-- 1 weizijun staff 265K Aug 19 21:23 _8_Lucene50_0.doc -rw-r--r-- 1 weizijun staff 44M Aug 19 21:23 _8_Lucene50_0.tim -rw-r--r-- 1 weizijun staff 340K Aug 19 21:23 _8_Lucene50_0.tip -rw-r--r-- 1 weizijun staff 37M Aug 19 21:23 _8_Lucene54_0.dvd -rw-r--r-- 1 weizijun staff 254B Aug 19 21:23 _8_Lucene54_0.dvm -rw-r--r-- 1 weizijun staff 195B Aug 19 21:23 segments_2 -rw-r--r-- 1 weizijun staff 0B Aug 19 21:20 write.lock
能夠看到正排數據、倒排索引數據,列存數據容量佔比幾乎相同,正排數據和倒排數據還會存儲Elasticsearch的惟一id字段,因此容量會比列存多一些。
35M的uuid存入Elasticsearch後,數據膨脹了3倍,達到了122.7mb。Elasticsearch居然這麼消耗資源,不要着急下結論,接下來看另外一個測試結果。
咱們寫入100w同樣的uuid,而後看看Elasticsearch使用的容量。
health status index pri rep docs.count docs.deleted store.size pri.store.size green open test_field 1 0 1000000 0 13.2mb 13.2mb -rw-r--r-- 1 weizijun staff 5.5M Aug 19 21:29 _6.fdt -rw-r--r-- 1 weizijun staff 15K Aug 19 21:29 _6.fdx -rw-r--r-- 1 weizijun staff 688B Aug 19 21:29 _6.fnm -rw-r--r-- 1 weizijun staff 494B Aug 19 21:29 _6.si -rw-r--r-- 1 weizijun staff 309K Aug 19 21:29 _6_Lucene50_0.doc -rw-r--r-- 1 weizijun staff 7.0M Aug 19 21:29 _6_Lucene50_0.tim -rw-r--r-- 1 weizijun staff 195K Aug 19 21:29 _6_Lucene50_0.tip -rw-r--r-- 1 weizijun staff 244K Aug 19 21:29 _6_Lucene54_0.dvd -rw-r--r-- 1 weizijun staff 252B Aug 19 21:29 _6_Lucene54_0.dvm -rw-r--r-- 1 weizijun staff 195B Aug 19 21:29 segments_2 -rw-r--r-- 1 weizijun staff 0B Aug 19 21:26 write.lock
這回35M的數據Elasticsearch容量只有13.2mb,其中還有主要的佔比仍是Elasticsearch的惟一id,100w的uuid幾乎不佔存儲容積。
因此在Elasticsearch中創建索引的字段若是基數越大(count distinct),越佔用磁盤空間。
咱們再看看存100w個不同的整型會是如何。
health status index pri rep docs.count docs.deleted store.size pri.store.size green open test_field 1 0 1000000 0 13.6mb 13.6mb -rw-r--r-- 1 weizijun staff 6.1M Aug 28 10:19 _42.fdt -rw-r--r-- 1 weizijun staff 22K Aug 28 10:19 _42.fdx -rw-r--r-- 1 weizijun staff 688B Aug 28 10:19 _42.fnm -rw-r--r-- 1 weizijun staff 503B Aug 28 10:19 _42.si -rw-r--r-- 1 weizijun staff 2.8M Aug 28 10:19 _42_Lucene50_0.doc -rw-r--r-- 1 weizijun staff 2.2M Aug 28 10:19 _42_Lucene50_0.tim -rw-r--r-- 1 weizijun staff 83K Aug 28 10:19 _42_Lucene50_0.tip -rw-r--r-- 1 weizijun staff 2.5M Aug 28 10:19 _42_Lucene54_0.dvd -rw-r--r-- 1 weizijun staff 228B Aug 28 10:19 _42_Lucene54_0.dvm -rw-r--r-- 1 weizijun staff 196B Aug 28 10:19 segments_2 -rw-r--r-- 1 weizijun staff 0B Aug 28 10:16 write.lock
從結果能夠看到,100w整型數據,Elasticsearch的存儲開銷爲13.6mb。若是以int型計算100w數據的長度的話,爲400w字節,大概是3.8mb數據。忽略Elasticsearch惟一id字段的影響,Elasticsearch實際存儲容量跟整型數據長度差很少。
咱們再看一下開啓最佳壓縮參數對存儲空間的影響:
health status index pri rep docs.count docs.deleted store.size pri.store.size green open test_field 1 0 1000000 0 107.2mb 107.2mb -rw-r--r-- 1 weizijun staff 25M Aug 20 12:30 _5.fdt -rw-r--r-- 1 weizijun staff 6.0K Aug 20 12:30 _5.fdx -rw-r--r-- 1 weizijun staff 688B Aug 20 12:31 _5.fnm -rw-r--r-- 1 weizijun staff 500B Aug 20 12:31 _5.si -rw-r--r-- 1 weizijun staff 265K Aug 20 12:31 _5_Lucene50_0.doc -rw-r--r-- 1 weizijun staff 44M Aug 20 12:31 _5_Lucene50_0.tim -rw-r--r-- 1 weizijun staff 322K Aug 20 12:31 _5_Lucene50_0.tip -rw-r--r-- 1 weizijun staff 37M Aug 20 12:31 _5_Lucene54_0.dvd -rw-r--r-- 1 weizijun staff 254B Aug 20 12:31 _5_Lucene54_0.dvm -rw-r--r-- 1 weizijun staff 224B Aug 20 12:31 segments_4 -rw-r--r-- 1 weizijun staff 0B Aug 20 12:00 write.lock
結果中能夠發現,只有正排數據會啓動壓縮,壓縮能力確實強勁,不考慮惟一id字段,存儲容量大概壓縮到接近50%。
咱們還作了一些實驗,Elasticsearch默認是開啓_all參數的,_all可讓用戶傳入的總體json數據做爲全文檢索的字段,能夠更方便的檢索,但在現實場景中已經使用的很少,相反會增長不少存儲容量的開銷,能夠看下開啓_all的磁盤空間使用狀況:
health status index pri rep docs.count docs.deleted store.size pri.store.size green open test_field 1 0 1000000 0 162.4mb 162.4mb -rw-r--r-- 1 weizijun staff 41M Aug 18 22:59 _20.fdt -rw-r--r-- 1 weizijun staff 18K Aug 18 22:59 _20.fdx -rw-r--r-- 1 weizijun staff 777B Aug 18 22:59 _20.fnm -rw-r--r-- 1 weizijun staff 59B Aug 18 22:59 _20.nvd -rw-r--r-- 1 weizijun staff 78B Aug 18 22:59 _20.nvm -rw-r--r-- 1 weizijun staff 539B Aug 18 22:59 _20.si -rw-r--r-- 1 weizijun staff 7.2M Aug 18 22:59 _20_Lucene50_0.doc -rw-r--r-- 1 weizijun staff 4.2M Aug 18 22:59 _20_Lucene50_0.pos -rw-r--r-- 1 weizijun staff 73M Aug 18 22:59 _20_Lucene50_0.tim -rw-r--r-- 1 weizijun staff 832K Aug 18 22:59 _20_Lucene50_0.tip -rw-r--r-- 1 weizijun staff 37M Aug 18 22:59 _20_Lucene54_0.dvd -rw-r--r-- 1 weizijun staff 254B Aug 18 22:59 _20_Lucene54_0.dvm -rw-r--r-- 1 weizijun staff 196B Aug 18 22:59 segments_2 -rw-r--r-- 1 weizijun staff 0B Aug 18 22:53 write.lock
開啓_all比不開啓多了40mb的存儲空間,多的數據都在倒排索引上,大約會增長30%多的存儲開銷。因此線上都直接禁用。
而後我還作了其餘幾個嘗試,爲了驗證存儲容量是否和數據量成正比,寫入1000w數據的uuid,發現存儲容量基本爲100w數據的10倍。我還驗證了數據長度是否和數據量成正比,發現把uuid增加2倍、4倍,存儲容量也響應的增長了2倍和4倍。在此就不一一列出數據了。
文件名爲:segments_xxx
該文件爲lucene數據文件的元信息文件,記錄全部segment的元數據信息。
該文件主要記錄了目前有多少segment,每一個segment有一些基本信息,更新這些信息定位到每一個segment的元信息文件。
lucene元信息文件還支持記錄userData,Elasticsearch能夠在此記錄translog的一些相關信息。
public final class SegmentInfos implements Cloneable, Iterable<SegmentCommitInfo> { // generation是segment的版本的概念,從文件名中提取出來,實例中爲:2t/101 private long generation; // generation of the "segments_N" for the next commit private long lastGeneration; // generation of the "segments_N" file we last successfully read // or wrote; this is normally the same as generation except if // there was an IOException that had interrupted a commit /** Id for this commit; only written starting with Lucene 5.0 */ private byte[] id; /** Which Lucene version wrote this commit, or null if this commit is pre-5.3. */ private Version luceneVersion; /** Counts how often the index has been changed. */ public long version; /** Used to name new segments. */ // TODO: should this be a long ...? public int counter; /** Version of the oldest segment in the index, or null if there are no segments. */ private Version minSegmentLuceneVersion; private List<SegmentCommitInfo> segments = new ArrayList<>(); /** Opaque Map<String, String> that user can specify during IndexWriter.commit */ public Map<String,String> userData = Collections.emptyMap(); } /** Embeds a [read-only] SegmentInfo and adds per-commit * fields. * * @lucene.experimental */ public class SegmentCommitInfo { /** The {@link SegmentInfo} that we wrap. */ public final SegmentInfo info; // How many deleted docs in the segment: private int delCount; // Generation number of the live docs file (-1 if there // are no deletes yet): private long delGen; // Normally 1+delGen, unless an exception was hit on last // attempt to write: private long nextWriteDelGen; // Generation number of the FieldInfos (-1 if there are no updates) private long fieldInfosGen; // Normally 1+fieldInfosGen, unless an exception was hit on last attempt to // write private long nextWriteFieldInfosGen; //fieldInfosGen == -1 ? 1 : fieldInfosGen + 1; // Generation number of the DocValues (-1 if there are no updates) private long docValuesGen; // Normally 1+dvGen, unless an exception was hit on last attempt to // write private long nextWriteDocValuesGen; //docValuesGen == -1 ? 1 : docValuesGen + 1; // TODO should we add .files() to FieldInfosFormat, like we have on // LiveDocsFormat? // track the fieldInfos update files private final Set<String> fieldInfosFiles = new HashSet<>(); // Track the per-field DocValues update files private final Map<Integer,Set<String>> dvUpdatesFiles = new HashMap<>(); // Track the per-generation updates files @Deprecated private final Map<Long,Set<String>> genUpdatesFiles = new HashMap<>(); private volatile long sizeInBytes = -1; }
文件後綴:.si
每一個segment都有一個.si文件,記錄了該segment的元信息。
segment元信息文件中記錄了segment的文檔數量,segment對應的文件列表等信息。
/** * Information about a segment such as its name, directory, and files related * to the segment. * * @lucene.experimental */ public final class SegmentInfo { // _bl public final String name; /** Where this segment resides. */ public final Directory dir; /** Id that uniquely identifies this segment. */ private final byte[] id; private Codec codec; // Tracks the Lucene version this segment was created with, since 3.1. Null // indicates an older than 3.0 index, and it's used to detect a too old index. // The format expected is "x.y" - "2.x" for pre-3.0 indexes (or null), and // specific versions afterwards ("3.0.0", "3.1.0" etc.). // see o.a.l.util.Version. private Version version; private int maxDoc; // number of docs in seg private boolean isCompoundFile; private Map<String,String> diagnostics; private Set<String> setFiles; private final Map<String,String> attributes; }
文件後綴:.fnm
該文件存儲了fields的基本信息。
fields信息中包括field的數量,field的類型,以及IndexOpetions,包括是否存儲、是否索引,是否分詞,是否須要列存等等。
/** * Access to the Field Info file that describes document fields and whether or * not they are indexed. Each segment has a separate Field Info file. Objects * of this class are thread-safe for multiple readers, but only one thread can * be adding documents at a time, with no other reader or writer threads * accessing this object. **/ public final class FieldInfo { /** Field's name */ public final String name; /** Internal field number */ //field在內部的編號 public final int number; //field docvalues的類型 private DocValuesType docValuesType = DocValuesType.NONE; // True if any document indexed term vectors private boolean storeTermVector; private boolean omitNorms; // omit norms associated with indexed fields //index的配置項 private IndexOptions indexOptions = IndexOptions.NONE; private boolean storePayloads; // whether this field stores payloads together with term positions private final Map<String,String> attributes; // docvalues的generation private long dvGen; }
文件後綴:.fdx, .fdt
索引文件爲.fdx,數據文件爲.fdt,數據存儲文件功能爲根據自動的文檔id,獲得文檔的內容,搜索引擎的術語習慣稱之爲正排數據,即doc_id -> content,es的_source數據就存在這
索引文件記錄了快速定位文檔數據的索引信息,數據文件記錄了全部文檔id的具體內容。
/** * Random-access reader for {@link CompressingStoredFieldsIndexWriter}. * @lucene.internal */ public final class CompressingStoredFieldsIndexReader implements Cloneable, Accountable { private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(CompressingStoredFieldsIndexReader.class); final int maxDoc; //docid索引,快速定位某個docid的數組座標 final int[] docBases; //快速定位某個docid所在的文件offset的startPointer final long[] startPointers; //平均一個chunk的文檔數 final int[] avgChunkDocs; //平均一個chunk的size final long[] avgChunkSizes; final PackedInts.Reader[] docBasesDeltas; // delta from the avg final PackedInts.Reader[] startPointersDeltas; // delta from the avg } /** * {@link StoredFieldsReader} impl for {@link CompressingStoredFieldsFormat}. * @lucene.experimental */ public final class CompressingStoredFieldsReader extends StoredFieldsReader { //從fdt正排索引文件中得到 private final int version; // field的基本信息 private final FieldInfos fieldInfos; //fdt正排索引文件reader private final CompressingStoredFieldsIndexReader indexReader; //從fdt正排索引文件中得到,用於指向fdx數據文件的末端,指向numChunks地址4 private final long maxPointer; //fdx正排數據文件句柄 private final IndexInput fieldsStream; //塊大小 private final int chunkSize; private final int packedIntsVersion; //壓縮類型 private final CompressionMode compressionMode; //解壓縮處理對象 private final Decompressor decompressor; //文檔數量,從segment元數據中得到 private final int numDocs; //是否正在merge,默認爲false private final boolean merging; //初始化時new了一個BlockState,BlockState記錄下當前正排文件讀取的狀態信息 private final BlockState state; //chunk的數量 private final long numChunks; // number of compressed blocks written //dirty chunk的數量 private final long numDirtyChunks; // number of incomplete compressed blocks written //是否close,默認爲false private boolean closed; }
索引後綴:.tip,.tim
倒排索引也包含索引文件和數據文件,.tip爲索引文件,.tim爲數據文件,索引文件包含了每一個字段的索引元信息,數據文件有具體的索引內容。
5.5.0版本的倒排索引實現爲FST tree,FST tree的最大優點就是內存空間佔用很是低 ,具體能夠參看下這篇文章:http://www.cnblogs.com/bonelee/p/6226185.html
http://examples.mikemccandless.com/fst.py?terms=&cmd=Build+it 爲FST圖實例,能夠根據輸入的數據構造出FST圖
輸入到 FST 中的數據爲: String inputValues[] = {"mop","moth","pop","star","stop","top"}; long outputValues[] = {0,1,2,3,4,5};
生成的 FST 圖爲:
public final class BlockTreeTermsReader extends FieldsProducer { // Open input to the main terms dict file (_X.tib) final IndexInput termsIn; // Reads the terms dict entries, to gather state to // produce DocsEnum on demand final PostingsReaderBase postingsReader; private final TreeMap<String,FieldReader> fields = new TreeMap<>(); /** File offset where the directory starts in the terms file. */ /索引數據文件tim的數據的尾部的元數據的地址 private long dirOffset; /** File offset where the directory starts in the index file. */ //索引文件tip的數據的尾部的元數據的地址 private long indexDirOffset; //semgent的名稱 final String segment; //版本號 final int version; //5.3.x index, we record up front if we may have written any auto-prefix terms,示例中記錄的是false final boolean anyAutoPrefixTerms; } /** * BlockTree's implementation of {@link Terms}. * @lucene.internal */ public final class FieldReader extends Terms implements Accountable { //term的數量 final long numTerms; //field信息 final FieldInfo fieldInfo; final long sumTotalTermFreq; //總的文檔頻率 final long sumDocFreq; //文檔數量 final int docCount; //字段在索引文件tip中的起始位置 final long indexStartFP; final long rootBlockFP; final BytesRef rootCode; final BytesRef minTerm; final BytesRef maxTerm; //longs:metadata buffer, holding monotonic values final int longsSize; final BlockTreeTermsReader parent; final FST<BytesRef> index; }
文件後綴:.doc, .pos, .pay
.doc保存了每一個term的doc id列表和term在doc中的詞頻
全文索引的字段,會有.pos文件,保存了term在doc中的位置
全文索引的字段,使用了一些像payloads的高級特性纔會有.pay文件,保存了term在doc中的一些高級特性
/** * Concrete class that reads docId(maybe frq,pos,offset,payloads) list * with postings format. * * @lucene.experimental */ public final class Lucene50PostingsReader extends PostingsReaderBase { private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(Lucene50PostingsReader.class); private final IndexInput docIn; private final IndexInput posIn; private final IndexInput payIn; final ForUtil forUtil; private int version; //不分詞的字段使用的是該對象,基於skiplist實現了倒排鏈 final class BlockDocsEnum extends PostingsEnum { } //全文檢索字段使用的是該對象 final class BlockPostingsEnum extends PostingsEnum { } //包含高級特性的字段使用的是該對象 final class EverythingEnum extends PostingsEnum { } }
文件後綴:.dvm, .dvd
索引文件爲.dvm,數據文件爲.dvd。
lucene實現的docvalues有以下類型:
一、NONE 不開啓docvalue時的狀態
二、NUMERIC 單個數值類型的docvalue主要包括(int,long,float,double)
三、BINARY 二進制類型值對應不一樣的codes最大值可能超過32766字節,
四、SORTED 有序增量字節存儲,僅僅存儲不一樣部分的值和偏移量指針,值必須小於等於32766字節
五、SORTED_NUMERIC 存儲數值類型的有序數組列表
六、SORTED_SET 能夠存儲多值域的docvalue值,但返回時,僅僅只能返回多值域的第一個docvalue
七、對應not_anaylized的string字段,使用的是SORTED_SET類型,number的類型是SORTED_NUMERIC類型
其中SORTED_SET 的 SORTED_SINGLE_VALUED類型包括了兩類數據 : binary + numeric, binary是按ord排序的term的列表,numeric是doc到ord的映射。
/** reader for {@link Lucene54DocValuesFormat} */ final class Lucene54DocValuesProducer extends DocValuesProducer implements Closeable { //number類型的field的列存列表 private final Map<String,NumericEntry> numerics = new HashMap<>(); //字符串類型的field的列存列表 private final Map<String,BinaryEntry> binaries = new HashMap<>(); //有序字符串類型的field的列存列表 private final Map<String,SortedSetEntry> sortedSets = new HashMap<>(); //有序number類型的field的列存列表 private final Map<String,SortedSetEntry> sortedNumerics = new HashMap<>(); //字符串類型的field的ords列表 private final Map<String,NumericEntry> ords = new HashMap<>(); //docId -> address -> ord 中field的ords列表 private final Map<String,NumericEntry> ordIndexes = new HashMap<>(); //field的數量 private final int numFields; //內存使用量 private final AtomicLong ramBytesUsed; //數據源的文件句柄 private final IndexInput data; //文檔數 private final int maxDoc; // memory-resident structures private final Map<String,MonotonicBlockPackedReader> addressInstances = new HashMap<>(); private final Map<String,ReverseTermsIndex> reverseIndexInstances = new HashMap<>(); private final Map<String,DirectMonotonicReader.Meta> directAddressesMeta = new HashMap<>(); //是否正在merge private final boolean merging; } /** metadata entry for a numeric docvalues field */ static class NumericEntry { private NumericEntry() {} /** offset to the bitset representing docsWithField, or -1 if no documents have missing values */ long missingOffset; /** offset to the actual numeric values */ //field的在數據文件中的起始地址 public long offset; /** end offset to the actual numeric values */ //field的在數據文件中的結尾地址 public long endOffset; /** bits per value used to pack the numeric values */ public int bitsPerValue; //format類型 int format; /** count of values written */ public long count; /** monotonic meta */ public DirectMonotonicReader.Meta monotonicMeta; //最小的value long minValue; //Compressed by computing the GCD long gcd; //Compressed by giving IDs to unique values. long table[]; /** for sparse compression */ long numDocsWithValue; NumericEntry nonMissingValues; NumberType numberType; } /** metadata entry for a binary docvalues field */ static class BinaryEntry { private BinaryEntry() {} /** offset to the bitset representing docsWithField, or -1 if no documents have missing values */ long missingOffset; /** offset to the actual binary values */ //field的在數據文件中的起始地址 long offset; int format; /** count of values written */ public long count; //最短字符串的長度 int minLength; //最長字符串的長度 int maxLength; /** offset to the addressing data that maps a value to its slice of the byte[] */ public long addressesOffset, addressesEndOffset; /** meta data for addresses */ public DirectMonotonicReader.Meta addressesMeta; /** offset to the reverse index */ public long reverseIndexOffset; /** packed ints version used to encode addressing information */ public int packedIntsVersion; /** packed ints blocksize */ public int blockSize; }
本文地址:http://elasticsearch.cn/article/6178