如何让ES低成本、高性能?滴滴落地ZSTD压缩算法的实践分享

前文分别介绍了滴滴自研的ES强一致性多活是如何实现的、以及如何提升ES的性能潜力。由于滴滴ES日志场景每天写入量在5PB-10PB量级,写入压力和业务成本压力大,为了提升ES的写入性能,我们让ES支持ZSTD压缩算法,本篇文章详细展开滴滴在落地ZSTD压缩算法上的思考和实践。

// 背 景 //

ES通过索引(Index)对外提供数据检索能力,索引是用于组织和存储数据的逻辑单元。每个索引由若干个分片(shard)组成,每个分片就是一个Lucene索引,可以在不同的节点上进行分布式存储和并行处理,提高性能和可伸缩性。每个分片由一组段文件(segment)组成,段是分片中更小的存储和搜索单元,是一组物理文件,包含了检索需要的倒排索引(词项和文档ID的映射关系)和文档存储(字段值和其他元数据),如下图:

98a4d2bdec9aed3eeb161c710e8e4051.png

ES数据模型

Lucene作为ES的底层索引引擎,提供了灵活的数据检索能力,同时也导致CPU、存储占用较为严重。为实现降本增效,23年上半年,ES团队开启了Lucene压缩编码优化专项,通过改进存储层压缩算法,从而降低单位Document所占用的资源。本文概述了ES的底层索引文件,并介绍了Lucene存储压缩编码的优化。

// Lucene索引文件介绍 //

ES的压缩编码优化专项涉及到Lucene底层的文件存储,Lucene索引由一组Segment构成,每个Segment包含了一系列文件,重点文件类型如下图:

b20303e43bd25eb52b4f347fcbe666ee.png

  • 行存文件:包括原文存储文件和原文索引文件。原文存储文件,即.fdt文件。用户写入的原始数据都被存储于该文件中,因其占比大,为节约存储,Lucene在原文存储上支持LZ4压缩和ZIP压缩;原文索引文件,即.fdx文件,它存储了原文数据在原文存储文件中的位置信息,建立起了doc id和原文之间的联系,以支持快速访问和定位。

  • 列存文件:即.dvd文件,常被应用于一些OLAP分析引擎中。列存文件按列组织数据,不同Document中的同一列数据(Field),相邻存放在一起,这样可以加速该列聚合分析性查询。同时,相邻每列类型相同,在存储的时候可以进行统一性的编码优化,提高压缩率,减少存储磁盘空间的占用。

  • 索引相关文件:ES依靠分词产生倒排索引,使其具备强大的全文检索能力。索引相关文件中,重点文件包含:字典数据文件&倒排索引文件。字典数据文件,即.tim文件,通过用户配置的索引分词器,能够从用户数据中提取分词信息并存储在.tim文件中。同一列的分词信息,相邻存放,按块组织;倒排索引文件,即.doc文件,也被称为"倒排拉链表",它记录了每一个分词所关联的文档列表,能够实现快速的单词到文档的倒排查找。

// ZSTD压缩算法调研与分析 //

ES线上集群中资源比较紧张的主要是日志集群,集群写多读少,高峰期CPU使用率在85%左右,写入性能是它的主要瓶颈。通过调研可以发现原文存储文件的占比最大,基本都超过了30%,有些索引甚至超过了70%。由此,我们明确了索引文件压缩编码优化的重心。

目前滴滴ES线上采用的是7.6.0版本,对应的Lucene版本是8.4.0,该版本支持两种压缩策略:

  • BEST_SPEED,是ES索引默认的压缩算法,使用了LZ4压缩。压缩与解压速度快,CPU占用低,但压缩效果弱。    

  • BEST_COMPRESSION,使用了ZIP压缩。压缩与解压速度慢,CPU占用高,但压缩效果好。

Lucene的压缩算法仅针对占比最大的行存文件生效,其他文件通过自定义编码优化来降低存储。目前滴滴ES日志集群采用BEST_COMPRESSION压缩算法,通过ES压缩比测试发现,日志场景下,同一个索引采用ZIP比LZ4低20% ~ 40%的磁盘存储占用空间。但通过分析日志集群的CPU使用情况可以发现,ES压缩模块的CPU占比较高,一些日志集群甚至超过30%,如下图:

82f0a2c485f5502cfc61445a2bbd3fad.png

CPU损耗占比

在上述背景下,我们调研了ZSTD压缩算法,ZSTD(Zstandard)底层基于FSE编码实现,具有出色的压缩和解压速度。ZSTD算法的实现经过了高度优化,通过SIMD等指令集能够充分利用硬件并行性,同时编码过程大量依赖位移运算来完成状态的切换,以此提高处理速度。ZSTD采用字典压缩算法,通过引用字典中的匹配项,能够大大减少重复数据的存储空间,提高压缩比。与此同时,ZSTD采用多级压缩策略,在不同的压缩级别中应用不同的压缩算法,能够在不同的应用场景中灵活地平衡速度和压缩比。

为了验证它的性能,采用bamai线上1GB的日志文件做压缩性能测试,测试发现,ZSTD的压缩速度是ZIP的4.5倍,解压缩速度是ZIP的1.5倍,压缩比几乎持平,如下图所示,ZSTD压缩算法兼顾了LZ4压缩的"快"及ZIP压缩的"效果好"。

e43a0557bb5d283429a81f7a504e0b60.png

压缩算法对比

// ZSTD压缩算法落地 //

为了实现ZSTD在滴滴ES的落地,我们从以下方面着手:

源码开发

1、ES setting和engine扩展

ES通过setting给每个索引配置压缩格式,需要在ES setting中支持ZSTD压缩格式。ES会为每个shard初始化一个engine,不同的分片类型或状态对应不同的engine,例如索引close对应的是noop engine,DCDR从索引对应的following engine,需要在不同类型的engine上抽象并扩展它的ZSTD压缩能力。

2、Lucene CompressionMode 扩展

Lucene是一个由Java编写的全文搜索引擎库,而ZSTD算法是基于C++实现的,因此在Lucene端引入了zstd-jni来扩展ZSTD压缩能力。通过扩展CompressionMode,自定义ZStandardDecompressor和ZStandardCompressor来实现数据的按块压缩、解压缩。

参数调优

1、Chunk Size调优

行存文件内部是以Chunk形式组织的,Chunk Size通常为数十KB级别。滴滴ES7.6.0版本采用的是Lucene 8.4版本, LZ4压缩算法设置的Chunk Size为16kb,而ZIP压缩算法设置的是60kb。将索引设置为ZSTD压缩格式并导入一批线上数据后,压缩结果如表所示。

1e4b3d90edde88b9830d10a31429eb64.png

Chunk Size压缩比对表

增大ChunkSize可以获得一个更大的数据区间内的共享字典数据,从而获得更好的压缩效果。但这也会导致随机访问时延变大、CPU消耗进一步增大。为保证后期索引压缩格式切换为ZSTD时不会出现数据膨胀问题,ChunkSize采用的是60kb。

2、ZSTD压缩等级调优

ZSTD采用多级压缩策略,它 提供了从 1 到 22 的压缩等级,数值越大表示压缩比越高,但压缩和解压缩速度越慢、CPU损耗越高。设置不同的压缩等级,导入测试数据,压缩结果如下表所示:

263a4bfb115a36f7557a369c0e95c28f.png

压缩等级性能比对表

通过增大压缩等级能够降低存储,例如将压缩等级调整为9,.fdt文件能够下降10%左右的存储,索引整体存储下降5%,此时CPU损耗和ZIP基本持平。

ES线上日志集群写多读少,采用的都是物理机(SSD硬盘),集群高峰期CPU使用率超过80%,集群整体磁盘水位在55%左右,CPU使用率是它的瓶颈。因此,采用的压缩等级为3,该等级在速度和压缩比之间取得了较好的平衡,并且能够尽可能地降低集群CPU使用率。

其他

1、解决Lucene打包部分依赖加载失败问题,比如:Lucene采用ivy进行依赖管理,通过引入repo解决Lucene打包过程中Maven主仓库中找不到 org.restlet.jee jar的问题,如下图:

51adb8193b99feadb47681f5c351dd37.png

ivy依赖导入图

2、通过前置初始化zstd模块,解决ES运行时动态加载zstd-jni-jar失败问题。

3、通过扩展noop engine的ZSTD压缩能力,解决索引close场景ZSTD类型解析失败问题。

// 上线效果 //

经过三个月的实践与优化,目前已在16个集群上线了ES-ZSTD版本,并将日志集群全量索引(6w+)以及部分公共集群索引的压缩格式均切换为ZSTD,上线后所有日志集群高峰期CPU使用率平均降幅达到15%,使ES可以提供更高性能、更低成本的检索服务,主要效果如下:

更高性能

1、某日志集群A上线效果

ES某日志集群A上线ES-ZSTD版本并将全量索引切换压缩切换为ZSTD格式后,集群高峰期CPU使用率下降18%,写入reject同比下降50%。

4cfafb7624a1e87cca101cb6ff9b9ea5.png

集群CPU Idle图(集群A)

d85e419ac2e69709f6e34f3f21ddf0b1.png

DataNode写入reject图(集群A)

2、某超大日志索引M切换效果

ES某超大线上日志索引M压缩格式由ZIP切换为ZSTD后,写入条数不变的情况下,集群CPU使用率下降15%,写入性能提升25%。

6595c204985546c3c2187a8ae988c563.png

集群CPU Idle图(集群B)

90ea129f0d6d750607a1fc7e2e0dbf43.png

索引写入总耗时(索引M)

更低成本

1、LZ4压缩格式索引切换为ZSTD效果

ES日志集群还残留着部分LZ4压缩的日志索引,将这些日志索引切换为ZSTD压缩格式后,平均索引存储下降达到30%,如下图:

cbdc4a9796446de24816d76e3b4e4004.png

索引存储图

2、日志集群缩容

将索引压缩格式切换为ZSTD后,能够有效降低集群CPU,因此可以进行集群资源调整。目前已经缩容机器超过20台,仍在持续下线中。

// 总 结 //

ZSTD助力ES提供更高性能、更低成本的检索服务。之后也会陆续开启读写分离、ES大版本升级等项目,进一步助力业务发展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/70965.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

青大数据结构【2014】

一、单选 二、简答 为了解决顺序队列的假溢出问题,提出了循环队列,即把存储队列的表从逻辑上看成一个环 判别队列空和满有三种方法: 1)采用计数器判别,空时,计数器为0;满时,计数器…

智谷星图赵俊:让人才和区块链产业“双向奔赴”丨对话MVP

区块链产业需要什么样的人才?赵俊很有发言权。 赵俊是北京智谷星图科技有限公司的技术总监,也是FISCO BCOS官方认证讲师。他2017年接触区块链,随后选择人才培育领域深耕。“为区块链行业引进更多人才这件事很有价值,跟我的职业理…

shell脚本开发

shell脚本语言属于弱类型的语言,无需声明变量类型,直接定义使用 shell语言定义的变量,数据类型默认都是字符串类型 调用历史记录命令:! 历史记录id

【C语言】分支语句(选择结构)详解

✨个人主页: Anmia.🎉所属专栏: C Language 🎃操作环境: Visual Studio 2019 版本 目录 什么是分支语句? if语句 if if - else单分支 if - else if - else ...多分支 if - if嵌套 switch语句 基本语…

3.6 Spring MVC文件上传

1. 文件上传到本地 实现方式 Spring MVC使用commons-fileupload实现文件上传,注意事项如下: l HTTP请求方法是POST。 l HTTP请求头的Content-Type是multipart/form-data。 SpringMVC配置 配置commons-fileupload插件的文件上传解析器CommonsMultip…

【工程优化问题】基于鲸鱼、萤火虫、灰狼优化算法的张力、压缩弹簧设计问题研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

负载均衡–HAProxy安装及搭建tidb数据库负载服务

作为一名开发人员,随着经验的增加和技术的沉淀,需要提高自己架构方面的知识,同时对于一个企业来说,搭建一套高可用、高性能的技术架构,对于公司的业务开展和提高服务的性能也是大有裨益的。本文重点从软件安装和搭建ti…

1、Java简介+DOS命令+编译运行+一个简单的Java程序

Java类型: JavaSE 标准版:以前称为J2SE JavaEE 企业版:包括技术有:Servlet、Jsp,以前称为J2EE JavaME 微型版:以前称为J2ME Java应用: Android平台应用。 大数据平台开发:Hadoo…

通过MATLAB自动产生Hamming编译码的verilog实现,包含testbench

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 1. 原理 1.1 编码规则 1.2 错误检测和纠正 2. 实现过程 2.1 编码过程 2.2 解码过程 3. 应用领域 3.1 数字通信 3.2 存储系统 3.3 ECC内存 3.4 数据传输 5.算法完整程序工程 1.算法…

【软件测试】Linux环境Ant调用Jmeter脚本并且生成测试报告(详细)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 准备工作 需要在…

【BASH】回顾与知识点梳理(十五)

【BASH】回顾与知识点梳理 十五 十五. 指令与文件的搜寻15.1 脚本文件名的搜寻which (寻找『执行档』) 15.2 文件档名的搜寻whereis (由一些特定的目录中寻找文件文件名)locate / updatedbfind与时间有关的选项与使用者或组名有关的参数与文件权限及名称有关的参数额外可进行的…

【数据结构】二叉树常见题目

文章目录 前言二叉树概念满二叉树完全二叉树二叉搜索树(二叉排序树)平衡⼆叉搜索树存储⽅式 二叉树OJ二叉树创建字符串二叉树的分层遍历1二叉树的分层遍历2给定一个二叉树, 找到该树中两个指定节点的最近公共祖先二叉树搜索树转换成排序双向链表二叉树展开为链表根据一棵树的前…

软件功能测试有什么注意事项?功能测试报告起到什么作用?

软件功能测试是软件开发过程中至关重要的一环,它用于评估软件功能的质量和稳定性,并确保软件能够按照预期进行工作。然而,在进行功能测试时,有一些注意事项需要特别关注,以确保测试的准确性和有效性。 一、软件功能测…

使用Pytest集成Allure生成漂亮的图形测试报告

目录 前言 依赖包安装 Pytest Allure Pytest Adaptor 改造基于Pytest的测试用例 生成测试报告 运行测试 生成测试报告 打开测试报告 资料获取方法 前言 之前写过一篇生成测试报告的博客,但是其实Allure首先是一个可以独立运行的测试报告生成框架&#xff…

音视频技术开发周刊 | 305

每周一期,纵览音视频技术领域的干货。 新闻投稿:contributelivevideostack.com。 大神回归学界:何恺明宣布加入 MIT 「作为一位 FAIR 研究科学家,我将于 2024 年加入麻省理工学院(MIT)电气工程与计算机科学…

MOCK测试

介绍 mock:就是对于一些难以构造的对象,使用虚拟的技术来实现测试的过程。 mock测试:在测试过程中,对于某些不容易构造或者不容易获取的对象,可以用一个虚拟的对象来代替的测试方 法。 接口Mock测试:在接口…

后端开发8.品牌模块

概述 简介 效果图 数据库设计 DROP TABLE IF EXISTS `goods_brand`;CREATE TABLE `goods_brand` ( `goodsBrandId` int(11) NOT NULL AUTO_IN

上篇文章viewerjs(npm包补丁)的错误更正。(npm补丁本地没问题,流水线打包要没效果,要么报错)

1、上篇文档的包补丁在本地使用没有问题,并且同事拉代码后也会自动同步npm_modules,也没有问题 2、问题出现在打包上,我这边的项目是用teambition上的飞流设置的流水线来进行打包的,里面用到了两个指令 yarn install npm run bu…

uniapp开发(由浅到深)

文章目录 1. 项目构建1.1 脚手架构建1.2 HBuilderX创建 uni-app项目步骤: 2 . 包依赖2.1 uView2.2 使用uni原生ui插件2.3 uni-modules2.4 vuex使用 3.跨平台兼容3.1 条件编译 4.API 使用4.1 正逆参数传递 5. 接口封装6. 多端打包3.1 微信小程序3.2 打包App3.2.1 自有…

element-ui表格数据为空,图片占位提示

当表格的绑定数据为空时常需要显示暂无数据等字样&#xff0c;这时候就用到了empty-text <el-table:data"tableData"stripeborderempty-text"暂无数据"> 但&#xff0c;当数据为空&#xff0c;想用图片展示呢&#xff0c;如下图 方法一&#xff1a…