Elasticsearch 性能优化-移除FST堆内存

July 29th, 2020 by JasonLe's Tech Leave a reply »

单节点es能存储和处理的数据量主要受3方面限制:cpu、内存、磁盘

es是java程序,内存受限于JVM的堆内存,给es进程的堆内存又不能超过32G(受限于指针压缩,超过32g指针压缩失败,内存浪费)。所有堆内存大小的限制是es处理能力的主要限制

分析es堆内存,发现es堆内存使用占比最多的是FST。

何为FST呢?

倒排索引以分词后的词为主键进行组织,每个词后面对应的都是存在该关键字的文档id(这些id使用增量编码压缩,将大数变小数,按字节存储。所以如果id有一定的公共前缀,可以增加压缩比),term查询首先就是找到词,然后再根据文档id找到文档记录。如果我们每次查找都去找倒排索引,由于倒排索引存储在磁盘上,那就需要遍历磁盘上倒排索引记录,会进行多次磁盘IO,严重影响查询性能。

联想:如果索引中查询频繁的字段值有公共前缀,那在分词表是不是就比较接近,比较容易分配到同一个block中,可以加快查询速度

而FST相当于是倒排索引的一个二级缓存索引树,在生成倒排索引之后,根据分词表,将原先的分词表划分为多个block,每个block包含25-48个词,将每个block里的词的公共前缀取出来作为作为1个节点,其叶子节点对应是block的首地址,形成一个前缀树,放在堆内存中,永不回收,这就是FST。(可以联想到如果节点上的段很多,这部分占用的堆内存将会很多)

在term查找的时候,先根据term关键字遍历堆内存中的FST,找到对应term关键字匹配的前缀节点,找到该前缀节点下的block首地址。再读取本地磁盘上的分词表,将对应的block读取到内存,找到term关键词对应的文档id,然后根据文档id找到磁盘上的原始文档数据

在refresh和merge生成新的段时,会在磁盘生成一系列的段文件,其中有一个.tip文件就是FST文件,生成tip文件之后,lucene会将每个字段的FST(es里会对索引结构里的每个设置为index的字段创建倒排索引和FST)数据解析处理,永驻在堆内存里,直到段被删除。

优化方案:
1、扩大FST中每个block包含的词的数量,block变少,FST中占有的内存也会下降–会降低查询性能

2、将FST从堆内存移到堆外内存,交给MMAP管理,nmap属于page cache,会被回收,在大量读写下,会频繁被回收,频繁从磁盘读tip文件,性能反而大大下降

3、将FST从堆内存移到堆内存,新建一个cache来管理,使用LRU策略,进行缓存更新。
每次根据key去cache中找到FST,返回FST对象,拷贝加载到对内内存,进行查找。

4、将FST从堆内存移到堆内存,新建一个cache来管理,使用LRU策略,进行更新
每次根据key去cache中找到FST,返回FST对象地址,在cache中直接进行查找。