1.概述

转载:Elasticsearch 7.3 的 offheap 原理

一直以来,ES 堆中常驻内存中占据比重最大是 FST,即 tip(terms index) 文件占据的空间,1TB 索引大约占用2GB 或者更多的内存,因此为了节点稳定运行,业界通常认为一个节点 open 的索引不超过5TB。现在,从 ES 7.3版本开始,将 tip 文件修改为通过 mmap 的方式加载,这使 FST占据的内存从堆内转移到了堆外由操作系统的 pagecache 管理。

参考 ES 7.3 的 release-notes :

Also mmap terms index (.tip) files for hybridfs #43150 (issue: #42838)

现在我们来聊一聊其中的一些细节。

hybridfs 是索引默认的store 类型,他根据操作系统类型自动选择 nio 或者 mmap,那么究竟哪些文件被 mmap 方式打开,手册中说:

Currently only the Lucene term dictionary, norms and doc values files are memory mapped. All other files are opened using Lucene NIOFSDirectory\

对应到文件扩展名,就是 nvd(norms),dvd(doc values),tim(term dictionary),tip(term index),cfs(compound)类型的文件使用 mmap 方式加载,其余使用 nio:

文件:org.elasticsearch.index.store.FsDirectoryFactory.HybridDirectory#openInput

方法useDelegate

boolean useDelegate(String name) {String extension = FileSwitchDirectory.getExtension(name);switch(extension) {// Norms, doc values and term dictionaries are typically performance-sensitive and hot in the page// cache, so we use mmap, which provides better performance.case "nvd":case "dvd":case "tim":// We want to open the terms index and KD-tree index off-heap to save memory, but this only performs// well if using mmap.case "tip":case "dim":// Compound files are tricky because they store all the information for the segment. Benchmarks// suggested that not mapping them hurts performance.case "cfs":// MMapDirectory has special logic to read long[] arrays in little-endian order that helps speed// up the decoding of postings. The same logic applies to positions (.pos) of offsets (.pay) but we// are not mmaping them as queries that leverage positions are more costly and the decoding of postings// tends to be less a bottleneck.case "doc":return true;// Other files are either less performance-sensitive (e.g. stored field index, norms metadata)// or are large and have a random access pattern and mmap leads to page cache trashing// (e.g. stored fields and term vectors).default:return false;}}

方法:openInput

@Override
public IndexInput openInput(String name, IOContext context) throws IOException {if (useDelegate(name)) {// we need to do these checks on the outer directory since the inner doesn't know about pending deletesensureOpen();ensureCanRead(name);// we only use the mmap to open inputs. Everything else is managed by the NIOFSDirectory otherwise// we might run into trouble with files that are pendingDelete in one directory but still// listed in listAll() from the other. We on the other hand don't want to list files from both dirs// and intersect for perf reasons.return delegate.openInput(name, context);} else {return super.openInput(name, context);}
}

你可能会想,为什么把 tip文件通过 mmap 方式读取就实现 offheap 了?像 hbase 实现 offheap 要把数据转移到堆外的数据结构,为什么 ES 不需要?

2.FST 的查找过程

onheap 的情况下,Lucene将 tip 文件的的数据读进一个数组,在 FST 查找时,seek 到某个位置,读取一些字节,然后再次 seek,再读取,相当于边读取边解析。

FST::findTargetArc

private Arc<T> findTargetArc(BytesReader in,...)
{//....in.setPosition(follow.target);arc.numArcs = in.readVInt();arc.bytesPerArc = in.readVInt();arc.posArcsStart = in.getPosition();arc.nextArc = arc.posArcsStart;//....
}

在 onheap 的情况下,这个 BytesReader 的初始化 就是简单地将文件读进数组而已

OnHeapFSTStore::init

public void init(DataInput in, long numBytes)
{bytesArray = new byte[(int) numBytes];in.readBytes(bytesArray, 0, bytesArray.length);
}

因此,在 offheap 的情况下,mmap 像数组一样读取就可以了。如果想要查看文件被 pagecache 缓存的百分比,可以用 vmtouch(推荐),pcstat,hcache,或 fincore等工具来检查:

如果想要确认某个 tip 文件是否被 mmap 方式读取的,可以使用 pmap 命令,被 mmap 映射的文件会在这里列出来:

3. tip offheap 后的效果

使用 geonames 数据集写入索引 1TB,使用 _cat/segments API 查看 segments.memory内存占用量,对比 offheap 后的内存占用效果:

store.type segments.memory
niofs 4.7GB
hybridfs 1.06GB

JVM 内存占用量降低了78%左右,不同数据样本结果不同,其他的可能会降低更多。

通过 _cat/segments 观测到的 segments.memory指标,会比实际占用的 JVM内存少一些,不过相差不大,上述结果可作为参考。

由于 offheap 后的堆外内存由操作系统的 pagecache 管理,什么时候被驱逐出去由操作系统决定,进程无法控制。如果 tip 文件的内容被驱逐出 pagecache,对 FST 的查找会涉及到磁盘 io,对查询延迟有比较大的影响。

Linux 系统的 pagecache回收有两种情况,一是当系统 free 内存不足的时候,系统自动回收 pagecache缓存的数据,其中可能包括 mmap 映射的 tip文件。

另一种情况是通过改写 /proc/sys/vm/drop_caches 或 posix_fadvise调用来手工回收,此时如果索引处于 open 状态,由 mmap 映射到 pagecache 的 tip 数据并不会被回收。 而如果索引处于 close 状态,则会被完全回收

当 FST 查找过程涉及到磁盘 io 时,查询延迟会比较大,不过目前还无法获取到查询过程有多少时间耗费在磁盘 io,只能从 profile API 看到 create_weight时间变长。

在 linux 2.6.34 的内核中,对 pagecache 的回收策略使用双链策略,参考《Linux内核设计与实现第三版》,算法描述大致如下:

该算法引入两个链表,一个 active list,一个 inactive list,两个链表都是从尾部加入,头部移出,页面换出操作只在inactive list执行,对于文件缓存,当第一次访问的时候加入到inactive list,再次访问的时候把他提升到active list,当 active list大小大于inactive list,就将active list头部的页面降级到inactive list

更多 pagecache 的信息可以参考:https://linux-mm.org/PageReplacementDesign

依据 mmap 的原理, 文件 fd被映射为指针(或者说字节数组)供进程直接访问,仅在进程访问到相应位置的时候才去读取磁盘,是根据内容按需读取磁盘。你会想既然如此,_open 索引是不是变快了?原来 nio 需要把整个文件读进堆内存,现在 mmap 一下就结束了,那么等索引首次被查询的时候才会加载到 pagecache?实际上 _open 索引并没变快,因为在 _open 索引的过程中,Lucene 会检查文件的校验和,把整个文件读取一遍:

BlockTreeTermsReader:BlockTreeTermsReader()-> CodecUtil.checksumEntireFile(indexIn);

ChecksumIndexInput in = new BufferedChecksumIndexInput(clone);
//读取文件到目标位置,并更新校验和,居然命名 seek 函数,即使 Lucene 大牛我也忍不住要吐槽
in.seek(in.length() - footerLength());
return checkFooter(in);

4.关于 _id 字段要不要 offheap 的问题

Lucene 支持字段级的 offheap设置,ES 7.3中将 tip offheap时并不包含 _id 字段,#52518 中提到,因为担心降低写入速度。不过在经历了一些测试之后发现影响并不大。

In general, the indexing rate is only affected if explicit IDs are used, as
otherwise Elasticsearch almost never performs lookups in the terms
dictionary for the purpose of indexing. So it’s quite wasteful to
require the terms index of _id to be loaded on-heap for users who have
append-only workloads. Furthermore I’ve been conducting benchmarks when
indexing with explicit ids on the http_logs dataset that suggest that
the slowdown is low enough that it’s probably not worth forcing the terms
index to be kept on-heap

题外话:这段内容说使用外部 doc id 方式入库时需要从 term dictionary中查询,这是因为使用外部 id写入时,ES 需要判断该 id 是否存在,以便执行 update 或 append 操作。因此在分片中对 _id 字段执行 Lucene 的 seekExact 查询来判断此 id 是否存在,所以使用外部 id 入库时写入速度会比较低一些(20%左右)。这也是 _id 字段需要写入的 FST 的一个原因。

在将 _id 字段 offheap 之后,使用 http_logs 数据集和外部 id 的方式执行写入测试,写入速度降低了 1.8%,JVM 内存降低了 100倍:

因此在 ES 7.7版本中会将 _id 字段也放到堆外。

结束语

把 FST 放到堆外可以让节点能够持有更多的数据,这对ES 集群能处理的数据规模有重大提升,意义重大。但是 tip 文件需要加载到内存的意义比 tim等文件要重要地多,pagecache 总会有需要回收的时候,谁能保证 tip 不被回收呢?所以总体来说让查询延迟增加不确定性,且不便重现和诊断。不过也用太担心,这种情况一般很少发生。

感谢军义、张鑫刚@小米对若干问题的探讨。

参考

https://github.com/elastic/elasticsearch/issues/38390
https://github.com/elastic/elasticsearch/pull/42838
https://github.com/elastic/elasticsearch/pull/43150
https://github.com/elastic/elasticsearch/pull/52518
https://www.elastic.co/guide/en/elasticsearch/reference/current/release-notes-7.3.0.html

【Elasticsearch】Elasticsearch 7.3 的 offheap 原理相关推荐

  1. 【Elasticsearch】Elasticsearch 查询过程中的 pre-filter 原理

    1.概述 转载:添Elasticsearch 查询过程中的 pre-filter 原理 大家都知道在对索引执行查询的时候,需要在所有的分片上执行查询,因为无法知道被查询的关键词位于哪个分片,对于全文查 ...

  2. ElasticSearch探索之路(四)索引原理:倒排索引、列式存储、Fielddata、索引压缩、联合索引

    文章目录 倒排索引 Term dictionary与Term index 列式存储--Doc Values Fielddata 索引压缩 FOR编码 Roaring Bitmaps 联合索引 倒排索引 ...

  3. Elasticsearch-基础介绍及索引原理分析

    介绍 Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 L ...

  4. elasticsearch最大节点数_Elasticsearch选举原理之Bully算法

    Elasticsearch采用了master-slave模式, ES会在集群中选取一个节点成为主节点,只有Master节点有资格维护全局的集群状态,在有节点加入或者退出集群的时候,它会重新分配分片,并 ...

  5. ElasticSearch探索之路(三)分布式原理:分布式路由、存储、搜索原理

    文章目录 分布式存储 路由 新增.索引和删除文档 取回文档 并发控制 分布式搜索 查询阶段 取回阶段 分布式存储 路由 当索引一个文档的时候,Elasticsearch会通过哈希来决定将文档存储到哪一 ...

  6. elasticsearch的简介_以及实现原理---全文检索引擎ElasticSearch工作笔记001

    可以去百度查一下 上面的elastic的官网. 我们要知道mysql 是专攻于crud的操作,而在海量数据中进行快速的查询他就不太合适了, 在海量数据中进行检索和存储,需要用elasticsearch ...

  7. Elasticsearch结构化搜索_filter执行原理深度剖析(bitset机制与caching机制)

    1)在倒排索引中查找搜索串,获取document list date来举例 word doc1 doc2 doc32017-01-01 * * 2017-02-02 * * 2017-03-03 * ...

  8. ElasticSearch 分页查询及深度分页原理与实现

    查询流程 查询阶段 在初始化查询阶段(query phase),查询被向索引中的每个分片副本(原本或副本)广播.每个分片在本地执行搜索并且建 立了匹配 document 的优先队列(priority ...

  9. Elasticsearch专栏-7.es底层写入原理

    es底层写入原理 概念说明 es数据落盘过程 mysql数据落盘过程 redis数据落盘过程 概念说明 在第一章节中,已经提到过几个名词:lucence.segment.translog.refres ...

最新文章

  1. 2019年上半年收集到的人工智能自动驾驶方向干货文章
  2. php7.2 的好处,PHP 7.2 中弃用的功能
  3. mysql 输出当前月所有日期与对应的星期
  4. 面试题整理19 矩阵Z字形扫描
  5. NIM博弈+SG函数求解
  6. ready与load的区别
  7. 小白学数据分析-----数据指标 累计用户数的使用
  8. 备份数据库的expdp语句_银行业Oracle RAC数据库迁移经验分享
  9. Linux在多线程应用程序中处理信号
  10. 弱鸡儿终于没爆零Day7
  11. 随机生成A~Z的字母CharDemo
  12. koa 的 Context
  13. 全网首发:JDK绘制文字:七、使用字体图像进行绘制
  14. .NET Framework总三
  15. 政考网:怎样考取公务员上岸?
  16. part 8 App电量优化
  17. index函数python什么意思_详解python中的index函数用法
  18. 给临时停车号码牌插上翅膀:lua脚本语言加入—鲁哇客智能挪车号码牌技术升级之路
  19. 【Python】迭代法求解非线性方程及方程组
  20. 网站信息的采集系列(一)--基本流程

热门文章

  1. 《2021国庆出行报告》出炉:全国高速拥堵里程占比同比下降37%
  2. 华为MatePad 11配置曝光:骁龙865+2K/120Hz高刷屏
  3. 抖音发布2020数据报告:日均视频搜索量破4亿,70后最爱发表情包
  4. 苏宁易购第二次债券购回基本方案:购回资金总额20亿元
  5. 被绿以后,我成了年薪百万的“小三劝退师”
  6. 那是财务自由的声音!寒武纪上市造就一批85后亿万富翁
  7. 屏幕持续升级!一加8斩获DisplayMate A+评级
  8. 没有5G也很香!iPhone去年四季度出货量了解下
  9. 三星Galaxy S20 5G版跑分曝光 骁龙865配12GB内存
  10. 任达华遇袭是效仿“宏颜获水”事件?百度回应:严惩肇事者 以儆效尤