2023 IoTDB 用户大会分享:如何用 IoTDB 监控工具进行深度系统调优

本文最后更新于:10 个月前

背景

2023 年 12 月 3 日,IoTDB 一年一度的 用户大会 成功举办。

在本次大会中,我有幸作为讲师之一做了《优其效:如何用 IoTDB 监控工具进行深度系统调优》的分享,系统介绍了 IoTDB 这一年来在可观测性方面的进展,并展示了它如何显著提升我们的性能调优和问题排查效率。

本博客将通过文字和图片的方式展示我的分享内容。

我们在可观测性方面做的工作后续也会有更多的博客输出出来,敬请期待!

内容

大家好,我是来自天谋科技的谭新宇,接下来我为大家分享的主题是”如何用 IoTDB 监控工具进行深度系统调优”。

本次分享分为 5 个方面,首先我们将介绍数据库系统的用户服务和架构演进挑战,这些挑战的本质都是如何去提升效率;接着我们会对 IoTDB 可观测性的发展进行概览,主要包括 Logging,Metrics 和 Tracing 三个方面;然后我们会介绍一下 IoTDB 的监控模块,其构建主要参考了火焰图作者著作《性能之巅》的思路,即从负载视角和资源视角两个维度对系统进行观测;最后我们会概述一下 IoTDB 的 4 个监控面板并着重做一些性能调优和问题排查的典型案例分享。

首先先来分析一下数据库系统的用户服务和架构演进有着怎样的挑战。

对于用户服务,主要存在以下三个挑战:

第一是如何快速找到业务场景的瓶颈点?系统的性能存在木板效应,会受限于系统最慢的模块,比如某节点的 CPU 和磁盘还没有打满,但网卡已经被打满了,此时增加写入负载也不会获得任何性能上的提升。

第二是如何对业务场景进行针对性调优?不同硬件环境和业务负载的排列组合会使得很多默认参数并不是当下最优的值,针对这个问题,一种理想流派是像 ottertune 一样使用机器学习的方式去找到最优的参数组合,另一种更为实际的流派则是能够对系统进行白盒调优。

第三是如何形成可扩展的调优体系?对于性能调优这个工作,其实非常容易形成马太效应,即越会调优的人越容易被分配更多性能调优的工作,虽然他会越来越能调优,但这也容易形成单点瓶颈,导致调优工作横向扩展不起来,这样其实是不利于整个产研团队和实施团队的共同成长的。因此需要针对调优这项工作形成可复制的调优方法论,大家共享互补调优的知识,一起成长。

对于架构演进,也主要存在以下三个挑战:

第一是如何确定典型业务场景?性能优化需要结合场景谈论才有意义,而一个系统往往也会有很多用户场景,这就需要从中抽象出来通用普适的典型场景并总结他们的典型特征。比如硬件环境到底是 4 核 8G 还是 64 核 128G ,业务需求到底是追求低延迟还是高吞吐等等。

第二是如何进一步演进典型业务场景下的性能?任何系统在特定业务场景下都存在进一步性能演进的可能,我们需要在寻找瓶颈的过程中区分出来哪些是工程问题(比如 GC 参数调优,代码写的冗余),哪些是学术问题(比如针对 IoTDB 特定的时序场景,有些数据库原理的 trade-off 发生了变化,这个时候就可以结合场景做一些更针对的设计,IoTDB 近几年在 Fast 和 ICDE 等顶会上发表的论文都是沿着这个思路去设计的),区分出这两个问题之后就可以利用不同的思路去并行协作优化性能了。

第三是如何确保性能优化的 ROI 最大?对于一个系统怎么优化,收集一圈能够得到一大堆思路,到底哪些效果会好,哪些效果会差?我们需要能够精确评估一个优化的正向作用和负面影响,并能够量化排列优先级,这样才可能将有限的资源持续投入到 ROI 最大的性能优化上,坚持做最正确的选择。

分析完了挑战,其实我们也都清楚了可观测性对于解决这些挑战的重要性。那么接下来我们介绍一下 IoTDB 的可观测性发展概览。

随着分布式架构成为主流,可观测性这一名词逐渐被大家频繁提及。学术界一般会将可观测性分为三个更具体的方向进行研究,分别是 Logging,Metrics 和 Tracing。

Logging 的职责是记录离散事件,从而使得事后可以通过这些记录来分析程序的行为。

Metrics 的职责是将不同类型的消息分别进行统计聚合处理,从而能够对系统进行持续的监控和预警。

Tracing 的职责是记录完整的调用轨迹,这就包含了服务间的网络传输信息与各个服务内部的调用堆栈信息。

IoTDB 自诞生时就使用了 Logback 框架来管理日志,随着版本的不断迭代,目前已经将不同级别和模块的日志拆分成了不同的文件便于检索。

虽然这些日志很重要,但它所有的信息都是离散的。如果要对某一类的信息进行一些汇总聚合统计,比如统计一段时间的平均刷盘点数,就需要首先 cat 文件,接着再 grep 过滤出同一类型的日志,然后还要写脚本来计算次数,平均值之类的,这就非常繁琐。

很自然的这就需要引入 Metrics 了。

IoTDB 在 0.12 版本就开始设计开发 Metrics,但从 1.0 版本之后才开始投入大量精力打磨 Metrics,到了 1.3 版本 Metrics 已经基本打磨的差不多了。

我们用了 Micrometer 和 DropWizard 的算法库来作为监控指标的类型支撑,具体的存储可以导出到 Prometheus 或者 IoTDB 中,可视化目前主要是在用 Grafana 工具。

右边贴了一张我们监控面板的图,还是非常漂亮的,后面会进一步介绍。

有了 Metric 之后,我们可以统计同一类请求的聚合信息,例如平均值,P99 值等等。这其实已经能够解决 90% 以上的问题,但对于剩下 10% 的问题,比如海量小查询和一个大查询并发执行时,大查询的执行耗时会被吞并,从而无法体现在 Metrics 中。此时我们就需要具备单独观测一条请求完整调用链路耗时的能力。

为了满足这种需求,今年我们也启动了对 Tracing 工作的研究,我们用 OpenTelemetry,ElasticSearch 和 Grafana 搭建了 Tracing 系统。

比如右图对于 show region 请求的调用链路,我们可以在 Grafana 中展示这个请求跨进程通信时不同进程内部调用栈的详细耗时信息,这对于慢查询等场景的性能排查效率会有显著提升。

总体而言,IoTDB 的可观测性能力在今年发生了质变。我们有信心也非常欢迎我们的用户朋友前来体验。

接下来我会着重介绍一下 IoTDB 的监控模块:

对于监控模块而言,它的灵魂就是他拥有哪些监控指标。

这里我们参照火焰图作者著作《性能之巅》的思路。从负载分析和资源分析这两个相反的角度去互补推进监控指标体系的建设。

对于自顶向下的负载视角:

我们对客户端写入 IoTDB 的流程进行了拆解。对于每个 IoTDB 的连接,当它将请求交给到 IoTDB 执行时,该连接被视为忙碌状态;当它在客户端攒批等待或者向服务端传输时,该连接被视为闲置状态。通过这种区分,我们能够对瓶颈是否在 IoTDB 内部有一个评估。比如每次连接繁忙都是 10ms,之后却要闲置 5 分钟,那基本瓶颈就不在 IoTDB 端了。

如果发现连接繁忙的时间要更大,要如何进一步去寻找瓶颈呢。我们将 IoTDB 的写入请求延迟进行了拆解,将写入流程分成了若干阶段,并对一般情况下更为耗时的阶段进行了更细粒度的拆分,从而能够确保发现瓶颈出现在哪个模块。比如调度执行阶段一直存在远程转发,那就需要去排查客户端的分区缓存是否失效。

总之,通过这种自顶向下的分析,我们能够找到系统当前的瓶颈是在哪些模块。

对于自底向上的资源视角:

我们主要从 4 个维度进行了考虑:

在磁盘方面,我们希望我们要比 Linux 的常用磁盘监控命令 iostat 更为丰富,比如除了磁盘利用率,吞吐 iops 之外,我们还想统计进程级别的磁盘读写情况和 page cache 的使用情况。

在网络方面,我们希望我们要比 Linux 的常用网络监控命令 sar 更为丰富,比如除了网络吞吐和收发包的速度之外,我们还想统计进程级别的连接个数等等。

在 CPU 方面,我们不仅要统计操作系统和进程的 CPU 利用率,还想统计 IoTDB 进程内部不同模块不同线程池的 CPU 利用率,也还想统计进程内部线程池的关键参数。

在 JVM 方面,我们不仅要对堆内堆外的内存大小做观测,对不同状态的线程个数做观测,还想对 GC 做更细致的观测。

总之,通过这种自底向上的分析,我们能为很多模块的瓶颈原因提供思路

那到了 1.3.0 版本,我们前面提到的监控指标都已经实现了,那么监控模块对于性能的影响到底大不大呢,线上敢不敢打开呢?

其实这块我们也在持续的做性能优化,尽管 IoTDB 监控指标的个数已经从 1.0.0 版本的 134 涨到了 1.3.0 版本的 905,增加了接近 8 倍,但监控模块 CPU 的开销反而从 11.34% 降低到了 5.81%,减少了近 50% 。其对于读写性能的影响也从 7% 以内降低到了 3% 以内。

因此,大家可以放心的开启监控模块,它对于系统运维的收益绝对远远大于这一点点性能损耗。

基于这些监控指标,接下来我们简单介绍一下 IoTDB 的监控面板:

主要分为四个监控面板:

分别 Performance Overview,System,ConfigNode 和 DataNode 面板。

下面将给出这些面板的示例:

对于 Performance Overview 面板:

它汇总统计了集群的基本信息,例如集群大小,总时间序列个数,总写入吞吐等等。

它还以延迟拆解的方式展示了客户端写入不同阶段的耗时统计,辅助定位瓶颈存在于哪个模块。

任何一个子面板我们都写了详细的注释,比如左图这个面板就展示了不同接口的耗时统计。

同时我们也可以在一个面板中同时查看多个节点的监控数据,便于定位相同时间不同节点的状态。

对于 System 面板,它提供了 CPU, JVM, 磁盘和网络维度的监控数据,在用于定位系统资源是否为瓶颈时非常管用。

对于 ConfigNode 面板,它也汇总统计了集群的基本信息,还提供了元数据及数据分区 Leader 分布等维度的监控。在用户定位集群扩展性能力时非常有用,比如是否所有的节点上都分配了读写流量,是否有节点宕机等等。

对于 DataNode 面板,它汇总了单节点引擎内部的细致监控,如存储,查询,元数据,共识和流处理引擎等等。在判断模块内部瓶颈原因时非常有用。

现在 IoTDB 有接近上千的监控指标,这些指标很难在今天短短的分享中介绍完,那接下来我就分享 5 个典型案例来展示一下 IoTDB 监控模块的能力:

第一个案例是在某高吞吐量场景下如何去确认瓶颈所在。

当业务链路较为复杂时,如果整体的性能不达标,用户其实是不太好去确认到底瓶颈是在业务上还是在 IoTDB 中。

那现在 IoTDB 的监控模块则是可以帮忙定位瓶颈是否在数据库中。

比如对于一个 Flink 实时消费 Kafka 数据来写入 IoTDB 的用户场景,业务链路上有 128 节点的 Kafka 集群,96 节点的 Flink 和 IoTDB 集群。

由于集群规模较大,部署测试调优运维的成本都较高。当时跑通整个链路后业务给我们的反馈就是 IoTDB 写入性能不够,IoTDB 集群总写入吞吐仅为 15GB/s,扩展性很差等等。

那当我们进行瓶颈排查之后发现锅并不在 IoTDB 而是在业务上层。

比如我们发现每个连接平均控制 4s 才会繁忙执行请求 20ms,每个节点平均每秒才接受 3 个请求且系统资源利用率都非常低。

因此我们推动了业务侧进行排查,他们发现即使把 Flink 的 Sink 侧置为空写整体吞吐也才 20GB/s,最终他们找到了问题所在并对 Flink 侧进行了优化。

在业务调整进行复测,我们发现 IoTDB 集群的整体吞吐可以达到 62.6GB/s,相比之前提升了 4 倍以上的性能,集群的线性比最高也达到了 0.89。

如果没有监控模块指导我们去推动业务侧改造,我们还一门心思的在数据库内部找瓶颈,那最终的结果一定是事倍功半。

第二个案例是某车联网场景的写入性能尖刺调优。

由于 IoTDB 是 Java 写的,很多用户也会询问我们 GC 对 IoTDB 性能的影响。由于我们在内存中也做了不少的池化来自己管理内存,所以大部分场景下用户其实感知不到 GC 对性能的影响。只有极少数个别场景才会观测到,比如这个案例就是 GC 导致了写入性能尖刺。

那现在 IoTDB 的监控模块内嵌了 GC 调优分析器,其实是具备对 GC 深度观测和调优的能力的,接下来让我们一探究竟。

该场景的整体架构是一个 3c 12D 的 IoTDB 集群,也是 Flink 实时消费 Kafka 的数据写入 IoTDB 集群。

在写入压测过程中,我们发现 IoTDB 的写入吞吐能力基本符合预期,但是存在尖刺,有时吞吐会接近 0。进一步排查原因我们发现这是由于 JVM 每 20 分钟会触发一次 Full GC,每次 Full GC 都耗时 1 min 以上,那这样的 GC 其实是非常不健康的。

那对于 GC 应该如何调优呢?常见的流程是启动 JVM 时打开 GC 日志,测试一段时间后上传 GC 日志到特定的网站进行分析,其会将不同原因导致的 GC 进行耗时和次数的汇总,然后我们可以基于这些聚合后的高密度 GC 信息再分析应该如何调整 GC 算法参数。

在建设好可观测性之后,现在的 IoTDB 如何去做 GC 调优分析呢?

我们首先提供了 GC 耗时比例的新手指标,它表示了 GC STW 耗时占整个 JVM RunTime 耗时的比例,如果这个比例小于 5-10%,则说明 GC 对系统整体的吞吐影响不大,如果在延迟上没有额外要求,那一般就不需要再调优了。

如果这个比例大于 10-15%,则一般说明可以对 GC 进行进一步调优。我们这时提供了若干专家指标,比如我们对不同 GC 原因导致的耗时和次数进行了统计,还对于种种 GC 前后的内存申请,内存大小都做了统计,这都能作为我们进行调优 GC 的数据支撑。

在该用户环境下,我们的调优思路首先是将 GC 算法从 PS 换成了对大内存更为友好的 G1,接着又结合负载和 IoTDB 的特点进行了 GC 参数的调优,其核心思路也是延缓并发标记阈值,提升 MixedGC 吞吐,控制单次 GC 耗时软上限等。

那最终调优的结果呢是写入吞吐稳定,不再又 Full GC,同时写入性能也提升了接近 50%,还是比较可观的。

这个案例主要是说明一下 IoTDB 对 GC 的观测能力和调优能力。

第三个案例是某测试场景下的硬件瓶颈原因探究。

在一些 POC 阶段,当系统出现瓶颈时,如果将系统视为黑盒,其实是不知道如何升级硬件的收益最高的。

结合 IoTDB 的监控模块,我们可以量化算出升级硬件带来的潜在性能收益,用以选择收益最高的硬件升级方案。

比如在该测试场景下,我们用 2 个客户端机器去压测高配机器的单节点集群,发现系统地性能仅为 1.2GB/s,不符合我们对如此高配机器的想象。

那接着我们对系统资源进行了分析,发现 CPU 和网络都没有达到瓶颈,但磁盘的繁忙程度达到了 100% 成为了瓶颈,从而限制了整体吞吐,此时就需要升级磁盘才能进一步提升性能了。

这就是一个典型的木桶效应,在理想状况下,所有资源应该同时达到瓶颈,这样硬件资源才没有浪费;然而在实际情况中,往往会有个别资源先到瓶颈,从而限制整体性能。

在升级磁盘之后,我们发现磁盘和网络不再是瓶颈,写入吞吐也提升到了原来的 2.5 倍,此时 CPU 又成为了新的瓶颈。

通过该案例,可以说明 IoTDB 的系统资源监控可以帮助我们快速找到硬件瓶颈,从而用最小的成本达到最大的收益。

案例 4 是某周测场景的写入性能波动变大问题排查。

对于服务器的 CPU 利用率出现波动这类问题,其实是比较难排查的,因为他不一定持续,等到我们去排查的时候可能已经不波动了。

对于这类问题,IoTDB 监控模块内置了操作系统,进程,线程池和模块 CPU 利用率监控,我们可以首先确认该波动是不是 IoTDB 引起的,如果是则可以一更进一步给出调优建议。

比如该问题就是我们在日常的周测场景中发现 1.2.0 rc5 版本的写入吞吐相比之前的一个版本波动更大,这其实属于很细致的观察了,不一定对业务有什么影响。

但我们没有放弃这一次机会进行了原因探究。

首先我们排查了操作系统及进程的 CPU 监控:发现新版本的 CPU 利用率波动更大,它应该是造成写入性能波动更大的原因。

那更进一步我们直接用了我们的大杀器-线程池 CPU 利用率监控,发现新版本中后台执行的合并线程池利用率在 0-18% 进行大幅波动,而老版本则稳定在 8% 左右。

更进一步我们去排查了存储引擎 TsFile 层级监控:发现新版本的存储引擎合并速度更快,文件状态也更健康。

因此我们就检查那段时间合入的代码,发现新版本修复了之前合并模块 IO 大小预估偏大的问题,可能导致之前受 IO 限速不能执行的合并任务受现在能够被执行。

至此我们已经明确了该现象的根因,考虑到改进后文件合并的更为健康,因此我们也没有进一步修改默认的限速参数。

但对于写入性能波动有要求的场景,我们也可以进一步降低合并模块的限速,从而达到与之前版本近似的效果。

该案例主要是说明 IoTDB 对 CPU 利用率的观测和掌控能力。

最后一个案例则是某钢厂场景的写入性能周期性下降 5% 排查。

随着我们可观测性做的不断深入,很多用户也开始对我们的监控面板越来越感兴趣,每天就上来翻一翻。

那在翻的过程中如果发现监控面板中有一些不影响业务的异样,是否有必要继续深挖?我们欢迎并鼓励这样的行为。

该案例就是由于用户的深挖反而促进了我们内核迭代的进一步演进,从而达到了双赢的效果。

该场景的架构是一个 3c3d 的集群,客户端会定期攒批写入 IoTDB,也会定期查询单设备过去 1 天的全量数据。

在用户日常巡检监控面板时,他发现了一个有趣的现象,即每 7 天会出现一次持续一天的 5% 性能下降。

这个问题其实可大可小,如果没有我们的监控面板,业务都不会感知到这件事情的存在,但该用户跟我们进行了反馈,于是我们也进行排查。

我们首先排查了系统及进程 CPU 监控:发现写入性能下降 5% 之后 CPU 占用率增加 5%,那依然还是怀疑 CPU 利用率的升高应该是写入性能下降 5% 的根因。

然后我们排查了写入延迟拆解监控,发现写入性能刚下降时存在跨节点转发,这代表客户端缓存失效,同时也观测到写入性能下降时调度执行阶段 P99 耗时增加。这基本可以辅助确定写入性能下降时 IoTDB 切换了时间分区,导致数据需要被写到新的节点。

接着我们对查询进行了分析,尽管查询的逻辑数据量始终是最近一天的数据,但跨分片查询时,由于涉及到更多的 operator 算子和跨节点序列化反序列化开销,这也会对 CPU 造成更大的消耗。

至此该问题的原因便找到了,它也催生了 IoTDB 内核的两个后续优化,第一个是尽量使得同一设备的数据保留在一个分片中,这样即可以避免该现象出现,第二个则是线程池 CPU 利用率监控,有了它我们则可以更直观的观察到增加的 5% CPU 都是在查询线程池导致的,排查效率就更高了。

该案例主要说明了 IoTDB 对这种很细微的业务感知不到的波动也具有诊断和迭代能力。

最后对以上 5 个案例做一个总结:

IoTDB 的可观测性目标是高效定位遇到的一切性能问题,虽然还有很长的路要走,但大家已经能够看到我们这一年的质变。

据我们的经验而言,针对硬件环境和业务负载调优一般可以获得 50% - 1000% 的性能提升。

所以我们非常欢迎大家来试用我们的商业版 IoTDB,也非常欢迎大家在使用监控模块过程中对想不通的性能问题和我们沟通。

我们期待与用户一起获得业务和技术上的成长。

我的分享就这么多,谢谢大家!


2023 IoTDB 用户大会分享:如何用 IoTDB 监控工具进行深度系统调优
https://tanxinyu.work/2023-iotdb-submit/
作者
谭新宇
发布于
2023年12月7日
许可协议