1、前言
很多使用Elasticsearch的同學會關(guān)心數(shù)據(jù)存儲在ES中的存儲容量,會有這樣的疑問:xxTB的數(shù)據(jù)入到ES會使用多少存儲空間。這個問題其實很難直接回答的,只有數(shù)據(jù)寫入ES后,才能觀察到實際的存儲空間。比如同樣是1TB的數(shù)據(jù),寫入ES的存儲空間可能差距會非常大,可能小到只有300~400GB,也可能多到6-7TB,為什么會造成這么大的差距呢?究其原因,我們來探究下Elasticsearch中的數(shù)據(jù)是如何存儲。文章中我以Elasticsearch 2.3版本為示例,對應(yīng)的lucene版本是5.5,Elasticsearch現(xiàn)在已經(jīng)來到了6.5版本,數(shù)字類型、列存等存儲結(jié)構(gòu)有些變化,但基本的概念變化不多,文章中的內(nèi)容依然適用。
2、Elasticsearch索引結(jié)構(gòu)
Elasticsearch對外提供的是index的概念,可以類比為DB,用戶查詢是在index上完成的,每個index由若干個shard組成,以此來達到分布式可擴展的能力。比如下圖是一個由10個shard組成的index。
shard是Elasticsearch數(shù)據(jù)存儲的最小單位,index的存儲容量為所有shard的存儲容量之和。Elasticsearch集群的存儲容量則為所有index存儲容量之和。
一個shard就對應(yīng)了一個lucene的library。對于一個shard,Elasticsearch增加了translog的功能,類似于HBase WAL,是數(shù)據(jù)寫入過程中的中間數(shù)據(jù),其余的數(shù)據(jù)都在lucene庫中管理的。
所以Elasticsearch索引使用的存儲內(nèi)容主要取決于lucene中的數(shù)據(jù)存儲。
3、lucene數(shù)據(jù)存儲
下面我們主要看下lucene的文件內(nèi)容,在了解lucene文件內(nèi)容前,大家先了解些lucene的基本概念。
3.1 lucene基本概念
- segment : lucene內(nèi)部的數(shù)據(jù)是由一個個segment組成的,寫入lucene的數(shù)據(jù)并不直接落盤,而是先寫在內(nèi)存中,經(jīng)過了refresh間隔,lucene才將該時間段寫入的全部數(shù)據(jù)refresh成一個segment,segment多了之后會進行merge成更大的segment。lucene查詢時會遍歷每個segment完成。由于lucene* 寫入的數(shù)據(jù)是在內(nèi)存中完成,所以寫入效率非常高。但是也存在丟失數(shù)據(jù)的風險,所以Elasticsearch基于此現(xiàn)象實現(xiàn)了translog,只有在segment數(shù)據(jù)落盤后,Elasticsearch才會刪除對應(yīng)的translog。
- doc : doc表示lucene中的一條記錄
- field :field表示記錄中的字段概念,一個doc由若干個field組成。
- term :term是lucene中索引的最小單位,某個field對應(yīng)的內(nèi)容如果是全文檢索類型,會將內(nèi)容進行分詞,分詞的結(jié)果就是由term組成的。如果是不分詞的字段,那么該字段的內(nèi)容就是一個term。
- 倒排索引(inverted index): lucene索引的通用叫法,即實現(xiàn)了term到doc list的映射。
- 正排數(shù)據(jù):搜索引擎的通用叫法,即原始數(shù)據(jù),可以理解為一個doc list。
- docvalues :Elasticsearch中的列式存儲的名稱,Elasticsearch除了存儲原始存儲、倒排索引,還存儲了一份docvalues,用作分析和排序。
3.2 lucene文件內(nèi)容
lucene包的文件是由很多segment文件組成的,segments_xxx文件記錄了lucene包下面的segment文件數(shù)量。每個segment會包含如下的文件。
| Name | Extension | Brief Description |
|---|---|---|
| Segment Info | .si | segment的元數(shù)據(jù)文件 |
| Compound File | .cfs, .cfe | 一個segment包含了如下表的各個文件,為減少打開文件的數(shù)量,在segment小的時候,segment的所有文件內(nèi)容都保存在cfs文件中,cfe文件保存了lucene各文件在cfs文件的位置信息 |
| Fields | .fnm | 保存了fields的相關(guān)信息 |
| Field Index | .fdx | 正排存儲文件的元數(shù)據(jù)信息 |
| Field Data | .fdt | 存儲了正排存儲數(shù)據(jù),寫入的原文存儲在這 |
| Term Dictionary | .tim | 倒排索引的元數(shù)據(jù)信息 |
| Term Index | .tip | 倒排索引文件,存儲了所有的倒排索引數(shù)據(jù) |
| Frequencies | .doc | 保存了每個term的doc id列表和term在doc中的詞頻 |
| Positions | .pos | 全文索引的字段,會有該文件,保存了term在doc中的位置 |
| Payloads | .pay | 全文索引的字段,使用了一些像payloads的高級特性會有該文件,保存了term在doc中的一些高級特性 |
| Norms | .nvd, .nvm | 文件保存索引字段加權(quán)數(shù)據(jù) |
| Per-Document Values | .dvd, .dvm | lucene的docvalues文件,即數(shù)據(jù)的列式存儲,用作聚合和排序 |
| Term Vector Data | .tvx, .tvd, .tvf | 保存索引字段的矢量信息,用在對term進行高亮,計算文本相關(guān)性中使用 |
| Live Documents | .liv | 記錄了segment中刪除的doc |
4、測試數(shù)據(jù)示例
下面我們以真實的數(shù)據(jù)作為示例,看看lucene中各類型數(shù)據(jù)的容量占比。
寫100w數(shù)據(jù),有一個uuid字段,寫入的是長度為36位的uuid,字符串總為3600w字節(jié),約為35M。
數(shù)據(jù)使用一個shard,不帶副本,使用默認的壓縮算法,寫入完成后merge成一個segment方便觀察。
使用線上默認的配置,uuid存為不分詞的字符串類型。創(chuàng)建如下索引:
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,使用磁盤容量細節(jié)如下:
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
可以看到正排數(shù)據(jù)、倒排索引數(shù)據(jù),列存數(shù)據(jù)容量占比幾乎相同,正排數(shù)據(jù)和倒排數(shù)據(jù)還會存儲Elasticsearch的唯一id字段,所以容量會比列存多一些。
35M的uuid存入Elasticsearch后,數(shù)據(jù)膨脹了3倍,達到了122.7mb。Elasticsearch竟然這么消耗資源,不要著急下結(jié)論,接下來看另一個測試結(jié)果。
我們寫入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的數(shù)據(jù)Elasticsearch容量只有13.2mb,其中還有主要的占比還是Elasticsearch的唯一id,100w的uuid幾乎不占存儲容積。
所以在Elasticsearch中建立索引的字段如果基數(shù)越大(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
從結(jié)果可以看到,100w整型數(shù)據(jù),Elasticsearch的存儲開銷為13.6mb。如果以int型計算100w數(shù)據(jù)的長度的話,為400w字節(jié),大概是3.8mb數(shù)據(jù)。忽略Elasticsearch唯一id字段的影響,Elasticsearch實際存儲容量跟整型數(shù)據(jù)長度差不多。
我們再看一下開啟最佳壓縮參數(shù)對存儲空間的影響:
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
結(jié)果中可以發(fā)現(xiàn),只有正排數(shù)據(jù)會啟動壓縮,壓縮能力確實強勁,不考慮唯一id字段,存儲容量大概壓縮到接近50%。
我們還做了一些實驗,Elasticsearch默認是開啟_all參數(shù)的,_all可以讓用戶傳入的整體json數(shù)據(jù)作為全文檢索的字段,可以更方便的檢索,但在現(xiàn)實場景中已經(jīng)使用的不多,相反會增加很多存儲容量的開銷,可以看下開啟_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的存儲空間,多的數(shù)據(jù)都在倒排索引上,大約會增加30%多的存儲開銷。所以線上都直接禁用。
然后我還做了其他幾個嘗試,為了驗證存儲容量是否和數(shù)據(jù)量成正比,寫入1000w數(shù)據(jù)的uuid,發(fā)現(xiàn)存儲容量基本為100w數(shù)據(jù)的10倍。我還驗證了數(shù)據(jù)長度是否和數(shù)據(jù)量成正比,發(fā)現(xiàn)把uuid增長2倍、4倍,存儲容量也響應(yīng)的增加了2倍和4倍。在此就不一一列出數(shù)據(jù)了。
5、lucene各文件具體內(nèi)容和實現(xiàn)
5.1 lucene數(shù)據(jù)元信息文件
文件名為:segments_xxx
該文件為lucene數(shù)據(jù)文件的元信息文件,記錄所有segment的元數(shù)據(jù)信息。
該文件主要記錄了目前有多少segment,每個segment有一些基本信息,更新這些信息定位到每個segment的元信息文件。
lucene元信息文件還支持記錄userData,Elasticsearch可以在此記錄translog的一些相關(guān)信息。
5.1.1 文件示例
5.1.2 具體實現(xiàn)類
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;
}
5.2 segment的元信息文件
文件后綴:.si
每個segment都有一個.si文件,記錄了該segment的元信息。
segment元信息文件中記錄了segment的文檔數(shù)量,segment對應(yīng)的文件列表等信息。
5.2.1 文件示例
5.2.2 具體實現(xiàn)類
/**
* 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;
}
5.3 fields信息文件
文件后綴:.fnm
該文件存儲了fields的基本信息。
fields信息中包括field的數(shù)量,field的類型,以及IndexOpetions,包括是否存儲、是否索引,是否分詞,是否需要列存等等。
5.3.1 文件示例
5.3.2 具體實現(xiàn)類
/**
* 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在內(nèi)部的編號
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;
}
5.4 數(shù)據(jù)存儲文件
文件后綴:.fdx, .fdt
索引文件為.fdx,數(shù)據(jù)文件為.fdt,數(shù)據(jù)存儲文件功能為根據(jù)自動的文檔id,得到文檔的內(nèi)容,搜索引擎的術(shù)語習慣稱之為正排數(shù)據(jù),即doc_id -> content,es的_source數(shù)據(jù)就存在這
索引文件記錄了快速定位文檔數(shù)據(jù)的索引信息,數(shù)據(jù)文件記錄了所有文檔id的具體內(nèi)容。
5.4.1 文件示例
5.4.2 具體實現(xiàn)類
/**
* 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的數(shù)組坐標
final int[] docBases;
//快速定位某個docid所在的文件offset的startPointer
final long[] startPointers;
//平均一個chunk的文檔數(shù)
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數(shù)據(jù)文件的末端,指向numChunks地址4
private final long maxPointer;
//fdx正排數(shù)據(jù)文件句柄
private final IndexInput fieldsStream;
//塊大小
private final int chunkSize;
private final int packedIntsVersion;
//壓縮類型
private final CompressionMode compressionMode;
//解壓縮處理對象
private final Decompressor decompressor;
//文檔數(shù)量,從segment元數(shù)據(jù)中獲得
private final int numDocs;
//是否正在merge,默認為false
private final boolean merging;
//初始化時new了一個BlockState,BlockState記錄下當前正排文件讀取的狀態(tài)信息
private final BlockState state;
//chunk的數(shù)量
private final long numChunks; // number of compressed blocks written
//dirty chunk的數(shù)量
private final long numDirtyChunks; // number of incomplete compressed blocks written
//是否close,默認為false
private boolean closed;
}
5.5 倒排索引文件
索引后綴:.tip,.tim
倒排索引也包含索引文件和數(shù)據(jù)文件,.tip為索引文件,.tim為數(shù)據(jù)文件,索引文件包含了每個字段的索引元信息,數(shù)據(jù)文件有具體的索引內(nèi)容。
5.5.0版本的倒排索引實現(xiàn)為FST tree,F(xiàn)ST tree的最大優(yōu)勢就是內(nèi)存空間占用非常低 ,具體可以參看下這篇文章:http://www.cnblogs.com/bonelee/p/6226185.html
http://examples.mikemccandless.com/fst.py?terms=&cmd=Build+it 為FST圖實例,可以根據(jù)輸入的數(shù)據(jù)構(gòu)造出FST圖
輸入到 FST 中的數(shù)據(jù)為:
String inputValues[] = {"mop","moth","pop","star","stop","top"};
long outputValues[] = {0,1,2,3,4,5};
生成的 FST 圖為:
5.5.1 文件示例
5.5.2 具體實現(xiàn)類
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. */
/索引數(shù)據(jù)文件tim的數(shù)據(jù)的尾部的元數(shù)據(jù)的地址
private long dirOffset;
/** File offset where the directory starts in the index file. */
//索引文件tip的數(shù)據(jù)的尾部的元數(shù)據(jù)的地址
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的數(shù)量
final long numTerms;
//field信息
final FieldInfo fieldInfo;
final long sumTotalTermFreq;
//總的文檔頻率
final long sumDocFreq;
//文檔數(shù)量
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;
}
5.6 倒排鏈文件
文件后綴:.doc, .pos, .pay
.doc保存了每個term的doc id列表和term在doc中的詞頻
全文索引的字段,會有.pos文件,保存了term在doc中的位置
全文索引的字段,使用了一些像payloads的高級特性才會有.pay文件,保存了term在doc中的一些高級特性
5.6.1 文件示例
5.6.2 具體實現(xiàn)類
/**
* 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實現(xiàn)了倒排鏈
final class BlockDocsEnum extends PostingsEnum {
}
//全文檢索字段使用的是該對象
final class BlockPostingsEnum extends PostingsEnum {
}
//包含高級特性的字段使用的是該對象
final class EverythingEnum extends PostingsEnum {
}
}
5.7 列存文件(docvalues)
文件后綴:.dvm, .dvd
索引文件為.dvm,數(shù)據(jù)文件為.dvd。
lucene實現(xiàn)的docvalues有如下類型:
- 1、NONE 不開啟docvalue時的狀態(tài)
- 2、NUMERIC 單個數(shù)值類型的docvalue主要包括(int,long,float,double)
- 3、BINARY 二進制類型值對應(yīng)不同的codes最大值可能超過32766字節(jié),
- 4、SORTED 有序增量字節(jié)存儲,僅僅存儲不同部分的值和偏移量指針,值必須小于等于32766字節(jié)
- 5、SORTED_NUMERIC 存儲數(shù)值類型的有序數(shù)組列表
- 6、SORTED_SET 可以存儲多值域的docvalue值,但返回時,僅僅只能返回多值域的第一個docvalue
- 7、對應(yīng)not_anaylized的string字段,使用的是SORTED_SET類型,number的類型是SORTED_NUMERIC類型
其中SORTED_SET 的 SORTED_SINGLE_VALUED類型包括了兩類數(shù)據(jù) : binary + numeric, binary是按ord排序的term的列表,numeric是doc到ord的映射。
5.7.1 文件示例
5.7.2 具體實現(xiàn)類
/** 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的數(shù)量
private final int numFields;
//內(nèi)存使用量
private final AtomicLong ramBytesUsed;
//數(shù)據(jù)源的文件句柄
private final IndexInput data;
//文檔數(shù)
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的在數(shù)據(jù)文件中的起始地址
public long offset;
/** end offset to the actual numeric values */
//field的在數(shù)據(jù)文件中的結(jié)尾地址
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的在數(shù)據(jù)文件中的起始地址
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;
}
轉(zhuǎn)載自:https://elasticsearch.cn/article/6178









