1.概述

转载:https://elasticsearch.cn/article/6178

转载防丢失

前言

很多使用Elasticsearch的同学会关心数据存储在ES中的存储容量,会有这样的疑问:xxTB的数据入到ES会使用多少存储空间。这个问题其实很难直接回答的,只有数据写入ES后,才能观察到实际的存储空间。比如同样是1TB的数据,写入ES的存储空间可能差距会非常大,可能小到只有300~400GB,也可能多到6-7TB,为什么会造成这么大的差距呢?究其原因,我们来探究下Elasticsearch中的数据是如何存储。文章中我以Elasticsearch 2.3版本为示例,对应的lucene版本是5.5,Elasticsearch现在已经来到了6.5版本,数字类型、列存等存储结构有些变化,但基本的概念变化不多,文章中的内容依然适用。

Elasticsearch索引结构

Elasticsearch对外提供的是index的概念,可以类比为DB,用户查询是在index上完成的,每个index由若干个shard组成,以此来达到分布式可扩展的能力。比如下图是一个由10个shard组成的index。

shard是Elasticsearch数据存储的最小单位,index的存储容量为所有shard的存储容量之和。Elasticsearch集群的存储容量则为所有index存储容量之和。

一个shard就对应了一个lucene的library。对于一个shard,Elasticsearch增加了translog的功能,类似于HBase WAL,是数据写入过程中的中间数据,其余的数据都在lucene库中管理的。

所以Elasticsearch索引使用的存储内容主要取决于lucene中的数据存储。

lucene数据存储

下面我们主要看下lucene的文件内容,在了解lucene文件内容前,大家先了解些lucene的基本概念。

lucene基本概念

segment : lucene内部的数据是由一个个segment组成的,写入lucene的数据并不直接落盘,而是先写在内存中,经过了refresh间隔,lucene才将该时间段写入的全部数据refresh成一个segment,segment多了之后会进行merge成更大的segment。lucene查询时会遍历每个segment完成。由于lucene* 写入的数据是在内存中完成,所以写入效率非常高。但是也存在丢失数据的风险,所以Elasticsearch基于此现象实现了translog,只有在segment数据落盘后,Elasticsearch才会删除对应的translog。
doc : doc表示lucene中的一条记录
field :field表示记录中的字段概念,一个doc由若干个field组成。
term :term是lucene中索引的最小单位,某个field对应的内容如果是全文检索类型,会将内容进行分词,分词的结果就是由term组成的。如果是不分词的字段,那么该字段的内容就是一个term。
倒排索引(inverted index): lucene索引的通用叫法,即实现了term到doc list的映射。
正排数据:搜索引擎的通用叫法,即原始数据,可以理解为一个doc list。
docvalues :Elasticsearch中的列式存储的名称,Elasticsearch除了存储原始存储、倒排索引,还存储了一份docvalues,用作分析和排序。

lucene文件内容

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倍。在此就不一一列出数据了。

lucene各文件具体内容和实现

lucene数据元信息文件

文件名为:segments_xxx

该文件为lucene数据文件的元信息文件,记录所有segment的元数据信息。

该文件主要记录了目前有多少segment,每个segment有一些基本信息,更新这些信息定位到每个segment的元信息文件。

lucene元信息文件还支持记录userData,Elasticsearch可以在此记录translog的一些相关信息。

文件示例

具体实现类

public final class SegmentInfos implements Cloneable, Iterable<SegmentCommitInfo> {// generation是segment的版本的概念,从文件名中提取出来,实例中为:2t/101private long generation;     // generation of the "segments_N" for the next commitprivate 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&lt;String, String&gt; 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// writeprivate 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// writeprivate long nextWriteDocValuesGen; //docValuesGen == -1 ? 1 : docValuesGen + 1;// TODO should we add .files() to FieldInfosFormat, like we have on// LiveDocsFormat?// track the fieldInfos update filesprivate final Set<String> fieldInfosFiles = new HashSet<>();// Track the per-field DocValues update filesprivate final Map<Integer,Set<String>> dvUpdatesFiles = new HashMap<>();// Track the per-generation updates files@Deprecatedprivate final Map<Long,Set<String>> genUpdatesFiles = new HashMap<>();private volatile long sizeInBytes = -1;
}

segment的元信息文件

文件后缀:.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 {// _blpublic 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 segprivate boolean isCompoundFile;private Map<String,String> diagnostics;private Set<String> setFiles;private final Map<String,String> attributes;
}

fields信息文件

文件后缀:.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 vectorsprivate 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 positionsprivate final Map<String,String> attributes;// docvalues的generationprivate 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的startPointerfinal long[] startPointers;//平均一个chunk的文档数final int[] avgChunkDocs;//平均一个chunk的sizefinal long[] avgChunkSizes;final PackedInts.Reader[] docBasesDeltas; // delta from the avgfinal 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正排索引文件readerprivate final CompressingStoredFieldsIndexReader indexReader;//从fdt正排索引文件中获得,用于指向fdx数据文件的末端,指向numChunks地址4private 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,默认为falseprivate 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,默认为falseprivate 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 图为:

elasticsearch_store_tip1.png



文件示例


具体实现类

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 demandfinal 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,示例中记录的是falsefinal 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 valuesfinal 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 {}
}

列存文件(docvalues)

文件后缀:.dvm, .dvd

索引文件为.dvm,数据文件为.dvd。

lucene实现的docvalues有如下类型:

1、NONE 不开启docvalue时的状态
2、NUMERIC 单个数值类型的docvalue主要包括(int,long,float,double)
3、BINARY 二进制类型值对应不同的codes最大值可能超过32766字节,
4、SORTED 有序增量字节存储,仅仅存储不同部分的值和偏移量指针,值必须小于等于32766字节
5、SORTED_NUMERIC 存储数值类型的有序数组列表
6、SORTED_SET 可以存储多值域的docvalue值,但返回时,仅仅只能返回多值域的第一个docvalue
7、对应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 structuresprivate final Map<String,MonotonicBlockPackedReader> addressInstances = new HashMap<>();private final Map<String,ReverseTermsIndex> reverseIndexInstances = new HashMap<>();private final Map<String,DirectMonotonicReader.Meta> directAddressesMeta = new HashMap<>();//是否正在mergeprivate 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;//最小的valuelong minValue;//Compressed by computing the GCDlong 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;}

参考资料
lucene source code

lucene document

lucene字典实现原理——FST

【Elasticsearch】 Elasticsearch中数据是如何存储的相关推荐

  1. 【Elasticsearch】Elasticsearch中数据是如何存储的

    1.概述 转载:Elasticsearch中数据是如何存储的 前言 很多使用Elasticsearch的同学会关心数据存储在ES中的存储容量,会有这样的疑问:xxTB的数据入到ES会使用多少存储空间. ...

  2. Java中数据是如何存储

    2019独角兽企业重金招聘Python工程师标准>>> 一:JAVA中数据的存储方式 ①:寄存器:这是最快的存储区,因为它位于不同于其他存储区的地方---处理器内部.但是寄存器的数量 ...

  3. 在 Java 应用程序中使用 Elasticsearch: 高性能 RESTful 搜索引擎和文档存储快速入门指南

    如果您使用过 Apache Lucene 或 Apache Solr,就会知道它们的使用体验非常有趣.尤其在您需要扩展基于 Lucene 或 Solr 的解决方案时,您就会了解 Elasticsear ...

  4. NodeJS同步MySQL上游数据到ElasticSearch数据库中

    NodeJS同步MySQL上游数据到ElasticSearch数据库中 项目地址: https://github.com/Miazzy/xdata-elasticsearchs-service.git ...

  5. 【Elasticsearch】Lucene 中的 Stored Fields 存储优化 自定义 存储类型 序列化 方式

    1.概述 转载:Lucene 中的 Stored Fields 存储优化 1 背景 Qunar 酒店的搜索和 suggest 是基于 Lucene 构建的,在我们的使用场景中,由于召回和排序是作为两个 ...

  6. 面试精讲之面试考点及大厂真题 - 分布式专栏 17 ElasticSearch解决大数据量检索难题

    17 ElasticSearch解决大数据量检索难题 理想的书籍是智慧的钥匙. --列夫·托尔斯泰 引言 如果你的项目里有超过千万上亿级别的数据,且数据日增量较大需要高性能检索时,如订单数据,你该怎么 ...

  7. 【Elasticsearch】在 Elastic Cloud 上的 Elasticsearch 服务中,如何针对日志和指标用例确定热温架构的规模

    1.概述 转载:在 Elastic Cloud 上的 Elasticsearch 服务中,如何针对日志和指标用例确定热温架构的规模 主要是需要翻墙,比较麻烦. 希望深入了解 Amazon Elasti ...

  8. 【ElasticSearch】大数据搜索选开源还是商业软件?ElasticSearch 对比 Splunk

    1.概述 转载:大数据搜索选开源还是商业软件?ElasticSearch 对比 Splunk述 本文就架构,功能,产品线,概念等方面就ElasticSearch和Splunk做了一下全方位的对比,希望 ...

  9. 一键同步Elasticsearch,DataWorks数据集成同步解决方案上线!

    简介:企业的实时数据除了存储在大数据引擎中,还有很多非结构化的日志数据,通过阿里云的Elasticsearch,用全托管的方式提供低成本的冷热存储方案,轻松助力企业搭建统一的云上全观测运维监控平台,实 ...

最新文章

  1. ERPLAB中文教程:创建与查看EventList
  2. 上海交通大学c语言章节作业,上海交通大学级C语言测试题.doc
  3. C提高_day03_const小专题
  4. Binder源码分析之Java层(原)
  5. 【机器学习】快速入门简单线性回归 (SLR)
  6. OpenJDK织机和结构化并发
  7. 使用MVC模式制作游戏-教程和简介
  8. 实现瀑布流的核心代码
  9. 反编译后怎么修改服务器地址,反编译后怎么修改服务器地址
  10. xss绕过字符过滤_XSS绕过实战练习
  11. 导出无法正常启动的VMware虚拟机中的文件
  12. office转PDF文档
  13. python如何读取二进制文件为图片_python之读取二进制文件
  14. 中科大开源数据集CCPD 2019详细介绍
  15. C语言每日一练——第88天:汉诺塔问题(河内塔)
  16. 数据分析案例(6)淘宝电商数据客户价值分析
  17. 微软所有正版软件下载网站ITELLYOU
  18. 【人工生态系统优化算法】基于人工生态系统优化算法求解单目标优化问题附matlab代码
  19. 程序员5大热门发展行业,就业迷茫的同学注意啦!
  20. 阿里巴巴巨震,堪比地震

热门文章

  1. 警方通报6少年深夜洗劫小米专卖店
  2. 撒贝宁探班威马体验百度Apollo L4级自动驾驶 感慨:确实震撼
  3. 苹果AirPods Max拼多多百亿补贴价来了:券后价3999元
  4. 索尼PS5上手体验公布:体积巨大运行安静
  5. 京东方将首次向华为供应on-cell OLED面板 用于Mate 40系列
  6. 心心念特斯拉Cybertruck?现在可以下单了,订金1000块
  7. 瑞幸咖啡退市成定局:董事长被要求辞职,新店却仍在扩张
  8. 苹果AirPods大受欢迎:与iPod鼎盛时期相当
  9. 名下房产、汽车、存款被查封后,王思聪要筹拍电影了...
  10. 共享充电宝还在打仗,支付宝已经笑了