在《ElasticSearch 降本增效常见的方法》一文中曾提到过 zstd 压缩算法 [1],一步一个脚印我们终于在京东 ES 上线支持了 zstd;我觉得促使目标完成主要以下几点原因:
Elastic 官方原因:zstd 压缩算法没有在 Elastic 官方的开发计划中;Elastic 的 licenes 变更,很多功能使用受限
ES 产品竞争力:提升京东 ES 产品在业界的竞争力,两大云友商和其他大厂都在陆续支持,在对外比拼的时候,我们需要提升我们这方面的能力
信创大背景:我们需要对开源组件有更好的自主管控和建设能力
京东零售 ES 与云 ES 产品融合:有更好的机会去打磨我们的 ES 内核
降本增效:ztsd 压缩算法,能够在降低存储成本的前提下,保证性能几乎不受损,写入性能还有所提升
测试集群配置:4c8g; 3 个数据节点;
测试索引设置:3 主分片 1 副本
测试数据 mapping: keyword 字段 14 个,geo_point 字段 3 个,integer 字段 2 个,text 字段 1 个,date 字段:2 个,ip 类型字段 1 个,boolean 字段 1 个
在考虑到读写性能和压缩比均衡的情况下,我们推荐使用 jd_zstd(压缩等级 3):
jd_zstd(压缩等级 3) 写入性能相对于 best_compression*提升 38.46%,相对于 lz提升 5.88%*;
jd_zstd(压缩等级 3) 存储相对于 lz4*节省 24%*,与 best_compression 基本持平,单位写的 gb 实际是要比 best_compression 的存储量小。
下表为 es6.8.23 版本,在 cpu 压测到 100% 时,不通压缩算法下ES 的 bulk、termquery、rangequery、matchquery 等 TPS 以及压缩比测试结果:
压缩算法 | bulk | termquery | rangequery | matchquery | 数据存储大小(580W 条文档)segment forcemerge 为 1 个 | 压缩率,基准为 lz(ES 默认为 lz 压缩算法) |
---|---|---|---|---|---|---|
lz4 | 34K | 7.7K | 790 | 450 | 13gb | - |
best_compression | 26K | 4.7K | 780 | 430 | 10gb | 76.9% |
jd_zstd(压缩等级 3) | 36K | 5.4K | 790 | 450 | 10gb | 76.9% |
jd_zstd(压缩等级 6) | 32K | 5.6K | 790 | 460 | 9.8gb | 75.38% |
jd_zstd(压缩等级 9) | 25K | 5.5K | 790 | 450 | 9.8gb | 75.38% |
注意⚠️:测试数据仅供参考,实际情况与用户数据有关
写多读少的场景,比如日志和监控场景。
云上ES等待上线后,可以进行申请
目前我们暂时只在内部泰山零售 ES 上线,支持 7.X 和 6.8.23 版本;后续会在云舰 ES 和公有云 ES 上线,由于 licenes 的限制,我们将只推出 6.8.23 版本。
Q1: 如何申请?
**A1:** 内部用户:之前在泰山平台申请的杰斯 ES,如果使用的是 7.X 和 6.8.23,可以选择版本升级到最新版本。新建集群,直接提工单申请
Q2 ztsd 如何使用?
A2:我们在 ES 中支持两种 zstd 压缩等级,用户可以根据自己的业务和数据特性选择合适的压缩等级; ES 创建索引时指定 index.codec:jd_zstd(压缩等级为 3)或者jd_zstd_6(压缩等级为 6) 即可,其余没有其他任何特殊之处。
注意⚠️:index.codec 的压缩算法不支持动态修改,必须创建索引时设定好。
# 创建索引zstdtest 压缩等级为 3
PUT zstdtest
{
"settings": {
"index": {
"codec": "jd_zstd"
}
}
}
# 创建索引zstdtest_6 压缩等级为 3
PUT zstdtest_6
{
"settings": {
"index": {
"codec": "jd_zstd_6"
}
}
}
首先我们介绍下 ES 与 Lucene 的关系;如下图所示,在集群层面:一个 ES 集群由多个节点组成。数据层面:1 个索引是由多个分片组成的,一个分片可以看是一个 Lucene 实例;一个分片包含多个 segement,一个 segement 即一组数据的最小单元,包含很多的数据文件。
lucene[2] 的数据文件主要由以下文件组成:
NAME | Extension | Brief Description |
---|---|---|
Segments File | segments_N | 存储已经落盘数据的位移提交点 |
Lock File | write.lock | 锁文件,防止多个 IndexWriters 写同一个文件 |
Segment Info | .si | 存储单个 segment 的 metadata |
Compound File | .cfs, .cfe | 复合文件主要是为了减少文件描述符;在 IndexWriterConfig 可以配置是否生成复合索引文件;复合文件实质是索引文件的组合,意思是无论是否设置了使用复合文件,总是先生成非复合索引文件,随后在 flush 阶段,才将这些文件生成.cfs、.cfe 文件,其中.liv、.si 所以文件不会被组合到.cfs、.cfe 中。 |
Fields | .fnm | 存储有关字段的信息 |
Field Index | .fdx | 指向字段数据的指针;存储了原文数据在原文存储文件中的位置信息,建立起了 doc id 和原文之间的联系,以支持快速访问和定位 |
Field Data | .fdt | 文档的存储字段 |
Term Dictionary | .tim | term 词典,存储 term 信息 |
Term Index | .tip | Term 词典的索引 |
Frequencies | .doc | 文档列表,其中包含每个 term 以及频率 |
Positions | .pos | 存储 term 在索引中出现位置的位置信息 |
Payloads | .pay | 存储附加的每个位置元数据信息,如字符偏移和用户 payloads |
Norms | .nvd, .nvm | 编码文档和字段的长度以及权重提升因子 |
Per-Document Values | .dvd, .dvm | 编码额外的评分因子或其他每个文档的信息 |
Term Vector Index | .tvx | 矢量数据的索引文件;将偏移量存储到文档数据文件中 |
Term Vector Data | .tvd | term 矢量数据 |
Live Documents | .liv | 有关哪些文档处于存活的信息;当发生标记删除时会产生该文件 |
Point values | .dii, .dim | 保留索引点,如果存在 |
上述的文件大致可以分为以下几类:
"_source": {
"enabled": false
}
注意⚠️:关闭_source 后, update, update_by_query, reindex 等功能无法正常使用,因此有 update 等需求的索引不能关闭_source.
列存相关文件:.dvd 文件,常用于 OLAP 分析,ES 使用列存来支持 sorting, aggregations 和 scripts 功能。不同文档 Document 中的同一列 (Field) 数据相邻存放,加速列聚合分析性查询。相邻每列类型相同,在存储的时候可以进行统一性的编码优化,提高压缩率,减少存储磁盘空间的占用。ES 中字段使用 doc_values 字为 true,即为开启列存储。
索引相关文件:主要文件包括字典数据文件.tim 和倒排索引.doc 文件。ES 依靠分词器产生倒排索引,从而具备强大的全文检索能力。索引配置分词器后,将从摄入文档数据中提取分词信息并存储于.tim 文件。同一列的分词信息相邻存放,按块组织;.doc 文件也被称为"倒排拉链表",记录每一个词项所关联的文档 id 列表,实现词项到文档的快速倒排查找。倒排索引也会进行压缩,其压缩算法主要有 Frame Of Reference、Roaring Bitmap 和 fst 等。
向量数据文件:矢量索引 tvx 和矢量数据.tvd 文件,支持以图搜图,和音频的查找等。通过对摄入实体进行矢量化,然后使用向量搜索算法进行检索。相关向量搜索算法有 HNSW[3],近似向量搜索knn[4];elastic 公司在今年 5 月份左右推出用于人工智能的 Elasticsearch相关性引擎 ESRE[5]。
zstd 主要压缩为行存储相关文件.fdm、.fdt 和.fdx;如下代码块为压缩文件对比,可以看出在不同的压缩算法中,这几个文件的大小是不同的。
# 为了节省篇幅部分文件省略
## lz4压缩算法索引testlz4 0 号分片
total 2.4G
-rw-r--r-- 1 admin admin 1.2K Nov 16 16:19 _32.fdm
-rw-r--r-- 1 admin admin 1.3G Nov 16 16:19 _32.fdt
-rw-r--r-- 1 admin admin 76K Nov 16 16:19 _32.fdx
-rw-r--r-- 1 admin admin 85M Nov 16 16:21 _32.kdd
-rw-r--r-- 1 admin admin 149M Nov 16 16:21 _32_Lucene80_0.dvd
.........................................
-rw-r--r-- 1 admin admin 401 Nov 16 16:21 segments_b
-rw-r--r-- 1 admin admin 0 Oct 16 16:05 write.lock
## best_compression压缩算法索引 testbestcompression 0 号分片
total 1.9G
-rw-r--r-- 1 admin admin 287 Nov 16 17:01 _2b.fdm
-rw-r--r-- 1 admin admin 781M Nov 16 17:01 _2b.fdt
-rw-r--r-- 1 admin admin 17K Nov 16 17:01 _2b.fdx
-rw-r--r-- 1 admin admin 85M Nov 16 17:03 _2b.kdd
-rw-r--r-- 1 admin admin 148M Nov 16 17:03 _2b_Lucene80_0.dvd
.........................................
-rw-r--r-- 1 admin admin 401 Nov 16 17:03 segments_a
-rw-r--r-- 1 admin admin 0 Oct 16 16:27 write.lock
## zstd压缩等级为3 索引testzstd3 0 号分片
total 1.9G
-rw-r--r-- 1 admin admin 286 Nov 16 17:26 _8e.fdm
-rw-r--r-- 1 admin admin 758M Nov 16 17:26 _8e.fdt
-rw-r--r-- 1 admin admin 15K Nov 16 17:26 _8e.fdx
-rw-r--r-- 1 admin admin 84M Nov 16 17:29 _8e.kdd
-rw-r--r-- 1 admin admin 148M Nov 16 17:29 _8e_Lucene80_0.dvd
-rw-r--r-- 1 admin admin 3.5K Nov 16 17:29
.........................................
-rw-r--r-- 1 admin admin 402 Nov 16 17:29 segments_9
-rw-r--r-- 1 admin admin 0 Nov 15 16:50 write.lock
## zstd压缩等级为6 索引testzstd6 0 号分片
total 1.9G
-rw-r--r-- 1 admin admin 286 Nov 16 16:56 _29.fdm
-rw-r--r-- 1 admin admin 742M Nov 16 16:56 _29.fdt
-rw-r--r-- 1 admin admin 9.8K Nov 16 16:56 _29.fdx
-rw-r--r-- 1 admin admin 86M Nov 16 16:58 _29.kdd
-rw-r--r-- 1 admin admin 148M Nov 16 16:58 _29_Lucene80_0.dvd
.........................................
-rw-r--r-- 1 admin admin 412 Nov 16 16:58 segments_a
-rw-r--r-- 1 admin admin 0 Oct 16 16:04 write.lock
## zstd压缩等级为9 索引testzstd9 0 号分片
total 1.9G
-rw-r--r-- 1 admin admin 286 Nov 16 17:21 _gp.fdm
-rw-r--r-- 1 admin admin 738M Nov 16 17:21 _gp.fdt
-rw-r--r-- 1 admin admin 13K Nov 16 17:21 _gp.fdx
-rw-r--r-- 1 admin admin 85M Nov 16 17:23 _gp.kdd
-rw-r--r-- 1 admin admin 149M Nov 16 17:23 _gp_Lucene80_0.dvd
.........................................
-rw-r--r-- 1 admin admin 402 Nov 16 17:23 segments_8
-rw-r--r-- 1 admin admin 0 Nov 15 16:50 write.lock
理论上来说 index.codec 支持的压缩算法最好下沉到 lucene 代码中,目前我们并没有维护 lucene 代码,因此我们直接 ES 侧面代码实现。
zstd[1] 算法是基于 C++ 实现,而 ES 是基于 java 编写,因此借助开源的力量,引入 zstd-jni 来实现 zstd 压缩能力.
# zstd_jni版本 1.5.5-1
api "com.github.luben:zstd-jni:${versions.zstd_jni}"
在 ES 代码中编写自定义的 index.codec;扩展 CompressionMode 压缩模式,自定义实现 ZstdCompressor 压缩和 ZstdDecompressor 解压缩方法,可以在这设定 zstd 的压缩等级以及控制读写数据块大小;最后通过 java 的 spl 机制实现加载我们自定义的压缩算法实现类
在 server/src/main/resources/META-INF/services/org.apache.lucene.codecs.Codec 文件中定义如下.
org.elasticsearch.index.codec.custom.ZstdCodec
注意⚠️:由于 ES 节点启动的时候,有 security 检查机制,因此我们需要在 server/src/main/resources/org/elasticsearch/bootstrap/security.policy 文件中添加代码权限授权策略
grant codeBase "${codebase.zstd-jni}" {
permission java.lang.RuntimePermission "loadLibrary.*";
permission java.lang.RuntimePermission "libzstd.*";
};
[1] https://github.com/facebook/zstd
[2] https://lucene.apache.org/core/8_11_2/core/org/apache/lucene/codecs/lucene87/package-summary.html#package.description
[3] Y. Malkov, D. Yashunin,Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs(2016), IEEE Transactions on Pattern Analysis and Machine Intelligence
[4] https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#approximate-knn
[5] https://mp.weixin.qq.com/s/awxgy9pSgv0lVPTfvzfxBw
[6] https://mp.weixin.qq.com/s/dmJwEpl6CWtv-MLdvR7g
作者:京东科技 杨松柏
来源:京东云开发者社区 转载请注明来源京东 ES 支持 ZSTD 压缩算法上线了:高性能,低成本 | 京东云技术团队