Elasticsearch(后文简称 ES)的基础是 Lucene,所有的索引和文档数据是存储在本地的磁盘中,具体的路径可在 ES 的配置文件../config/elasticsearch.yml中配置,如下:
# ----------------------------------- Paths ------------------------------------ # # Path to directory where to store the data (separate multiple locations by comma): # path.data: /path/to/data # # Path to log files: # path.logs: /path/to/logs磁盘涉及到 IO 的读写速度问题,因此如果条件允许的条件下,尽可能使用 SSD 硬盘和 高配置的 CPU。IO的提升,会极大改进 ES 的速度和性能。
磁盘备份采用 RAID0。因为 Elasticsearch 在自身层面通过副本,已经提供了备份的功能,所以不需要利用磁盘的备份功能,但是如果同时使用磁盘备份功能的话,对写入速度有较大的提升。
内部压缩硬件资源比较昂贵,一般不会花大成本去购置这些,可控的解决方案还是需要从软件方面来实现性能优化提升。
其实,对于一个分布式、可扩展、支持PB级别数据、实时的搜索与数据分析引擎,ES 本身对于索引数据和文档数据的存储方面内部做了很多优化,具体体现在对数据的压缩,那么是如何压缩的呢?介绍前先要说明下 Postings lists 的概念。
Postings lists搜索引擎一项很重要的工作就是高效的压缩和快速的解压缩一系列有序的整数列表。我们都知道,Elasticsearch 基于 Lucene,一个 Lucene 索引 我们在 Elasticsearch 称作 分片 , 这个 java 库引入了 按段搜索 的概念。新的文档首先被添加到内存索引缓存中,然后写入到一个基于磁盘的段。在每个 segment 内文档都会有一个 0 到文档个数之间的标识符(最高值 2^31 -1),称之为 doc ID。这在概念上类似于数组中的索引:它本身不做存储,但足以识别每个item 数据。
Segments 按顺序存储有关文档的数据,在一个Segments 中 doc ID 是 文档的索引。因此,segment 中的第一个文档的 doc ID 为0,第二个为1,等等,直到最后一个文档,其 doc ID 等于 segment 中文档的总数减1。
那么这些 doc ID 有什么用呢?倒排索引需要将 terms 映射到包含该单词 (term) 的文档列表,这样的映射列表我们称之为:倒排列表(postings list)。具体某一条映射数据称之为:倒排索引项(Posting)。
举个例子,文档和词条之间的关系如下图所示,右边的关系表即为倒排列表:
倒排列表 用来记录有哪些文档包含了某个单词(Term)。一般在文档集合里会有很多文档包含某个单词,每个文档会记录文档编号(doc ID),单词在这个文档中出现的次数(TF)及单词在文档中哪些位置出现过等信息,这样与一个文档相关的信息被称做 倒排索引项(Posting),包含这个单词的一系列倒排索引项形成了列表结构,这就是某个单词对应的 倒排列表
Frame Of Reference了解了分词(Term)和文档(Document)之间的映射关系后,为了高效的计算交集和并集,我们需要倒排列表(postings lists)是有序的,这样方便我们压缩和解压缩。
针对倒排列表,Lucene 采用一种增量编码的方式将一系列 ID 进行压缩存储,即称为Frame Of Reference的压缩方式(FOR),自Lucene 4.1以来一直在使用。
在实际的搜索引擎系统中,并不存储倒排索引项中的实际文档编号(Doc ID),而是代之以文档编号差值(D-Gap)。文档编号差值是倒排列表中相邻的两个倒排索引项文档编号的差值,一般在索引构建过程中,可以保证倒排列表中后面出现的文档编号大于之前出现的文档编号,所以文档编号差值总是大于0的整数。如图2所示的例子中,原始的 3个文档编号分别是187、196和199,通过编号差值计算,在实际存储的时候就转化成了:187、9、3。
之所以要对文档编号进行差值计算,主要原因是为了更好地对数据进行压缩,原始文档编号一般都是大数值,通过差值计算,就有效地将大数值转换为了小数值,而这有助于增加数据的压缩率。
比如一个词对应的文档ID 列表[73, 300, 302, 332,343, 372] ,ID列表首先要从小到大排好序;
第一步: 增量编码就是从第二个数开始每个数存储与前一个id的差值,即300-73=227,302-300=2,...,一直到最后一个数;
第二步: 就是将这些差值放到不同的区块,Lucene使用256个区块,下面示例为了方便展示使用了3个区块,即每3个数一组;