12 月 3 日,2023 IoTDB 用户大会在北京成功举行,收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题,多位学术泰斗、企业代表、开发者,深度分享了工业物联网时序数据库 IoTDB 的技术创新、应用效果,与各行业标杆用户的落地实践、解决方案,并共同探讨时序数据管理领域的行业趋势。
我们邀请到天谋科技高级开发工程师,Apache IoTDB PMC Member 谭新宇参加此次大会,并做主题报告——《优其效:如何用 IoTDB 监控工具进行深度系统调优》。以下为内容全文。
目录
数据库系统的用户服务和架构演进挑战
IoTDB 可观测性发展概览
IoTDB 监控模块
IoTDB 监控面板
典型案例分享
各位线上、线下的朋友们大家下午好,我是来自天谋科技的谭新宇,我在 IoTDB 的主要工作是持续提升 IoTDB 的分布式能力和可观测性。今天跟大家汇报分享的题目是如何用 IoTDB 监控工具进行深度的系统调优。
本次分享主要分为五个方面。首先,我们会介绍一下数据库系统的用户服务和架构演进挑战,这些挑战的本质都是如何去提升效率。接着,我们会对 IoTDB 的可观测性发展进行一个概览,主要包括 Logging、Metrics 和 Tracing 三个方面。然后,我们会介绍一下 IoTDB 的监控模块,其构建主要参考了火焰图作者著作《性能之巅》的思路,即从负载视角和资源视角两个维度对系统进行观测。最后,我们会介绍一下 IoTDB 的四个监控面板,并着重做一些性能调优和问题排查的典型案例分享。
01
数据库系统的用户服务和架构演进挑战
首先先来分析一下数据库系统的用户服务和架构演进到底有着怎样的挑战。
对于用户服务的挑战,我们这边列了三个:第一是如何快速找到业务场景的瓶颈点。系统的性能是存在木板效应的,会受限于系统最慢的模块。比如说某节点的 CPU 和磁盘还没有被打满,但网络已经被打满了,其实这个时候去增加写入负载,也不会获得任何写入性能的提升。
第二个是如何对业务负载进行进一步的针对性调优。结合不同硬件环境和业务负载的排列组合,会使得系统的默认参数在当下环境不总是最优的。针对这个问题,一种理想的解决方案是像 OtterTune 一样,使用机器学习的方式去找到最优的一组参数组合,另一种更为实际的流派则是能够对系统进行白盒调优。
第三个是如何形成可扩展的调优体系。对于性能调优,这个工作其实非常容易形成马太效应,即越会调优的同学会被分配更多性能调优的工作。这样虽然他会越来越能调优,但是也容易形成单点瓶颈,导致性能调优这个工作横向扩展不起来,这样其实是不利于整个产研团队和实施团队的共同成长的。因此需要针对调优这个工作形成可复制的调优方法论,大家共享、互补性能调优的知识,一起成长。
对于架构演进,主要也存在以下三个挑战:第一是如何确定典型的业务场景。性能优化需要结合场景谈论才有意义,而一个系统往往也会有很多的用户场景,这就需要从中抽象出来通用普适的典型场景,并总结出来它们的典型特征。比如说用户的硬件环境到底是 4C 8G,还是 64C 128G?用户需求到底追求的是低延时还是高吞吐?这都会对我们架构的进一步演进有着指导意义。
第二个是如何在典型业务场景下进一步去演进性能。任何系统在特定业务场景下都存在进一步演进性能的可能性,这就需要我们在寻找瓶颈的过程中,区分出来哪些是工程问题,比如是 GC 参数的调优,或者仅仅就是代码写得不高效等等;哪些是学术问题,比如针对 IoTDB 所深耕的时序场景,到底有哪些数据库原理的 tradeoff 发生了具体的一些变化,那这个时候我们就可以结合场景,去做一些更针对性的设计。IoTDB 近几年在 FAST 和 ICDE 等顶会上发表的论文,都是源于这个思路去设计的。一旦区分出了这两种问题,我们就可以用不同的思路去并行优化 IoTDB 的性能。
第三个是如何确保性能优化的 ROI 最大。对于一个系统怎么去优化,其实询问任何人都可以得到一大堆的调优思路,到底哪些思路是有效的,哪些思路是无效的?我们需要能够去精确的预估一个性能优化工作的正面收益和负面影响,并且能够去量化排列它们的优先级,这样才有可能持续将有限的资源投入到 ROI 收益最大的性能优化工作上,从而坚持做最正确的选择。
02
IoTDB 可观测性发展概览
分析完了挑战,相信大家也都清楚了可观测性对于解决这些挑战的重要性。那接下来我们会对 IoTDB 的可观测性发展进行一个概览。
随着分布式架构成为主流,可观测性这一名词在近几年也逐渐被大家频繁提及。学术界一般会将可观测性进一步区分为三个子方向进行研究,分别是 Logging、Metrics 和 Tracing。Logging 的职责是记录离散的事件,从而能够事后通过这些日志来分析程序当时的行为。Metrics 的职责是将不同类型的消息进行统计、聚合、处理,从而能够对系统进行持续的监测和预警。Tracing 的职责就是去记录完整的调用轨迹,这就包含了服务间的网络传输信息,以及各服务内部的调用堆栈信息等等。
IoTDB 自诞生之初就使用了 Logback 框架来管理日志,随着版本的不断迭代,目前已经能够将不同级别和模块的日志拆分成不同的文件便于检索。这些日志虽然很重要,但它记录的还是离散事件,如果我们想要对某一类请求的信息进行一些统计聚合处理,就会比较繁琐。比如说,如果我们想统计一段时间的平均刷盘点数,那我们就需要 cat 一下整个文件,grep 过滤出来一些对应的日志,然后还需要写脚本去分析它的次数以及平均耗时等等,这就非常繁琐了,这就很自然的需要引入 Metrics。
IoTDB 在 0.12 版本就开始去设计开发 Metrics 了,但到了 1.0 版本之后,才开始投入大量的精力去打磨 Metrics。到 1.3.0 版本,其实 Metrics 已经被打磨得差不多了,我们用 Micrometer 和 DropWizard 算法库来支撑了监控指标的底层的实现,让它的存储可以导出到 Prometheus 或者 IoTDB 自身中。同时我们也用 Grafana 去可视化了这些监控工具。右边是我们监控面板的一张图,还是比较漂亮的,等会儿也会进行进一步的介绍。
有了这些 Metrics 之后,我们可以去统计同一类请求具体的一些聚合信息,比如说平均值、P99 值、中位数值等等,这其实已经能够解决 90% 以上的问题了。但是对于剩下 10% 的问题,比如说海量的小查询和一个大查询并发执行的时候,大查询的执行耗时会被小查询所吞并,这样就无法体现在 Metrics 中。那这个时候就需要具备单独观测一条请求,完整调用链路具体耗时的能力。
为了满足这种需求,我们在今年也启动了对 Tracing 工作的研究。我们用 OpenTelemetry、ElasticSearch 以及 Grafana 去搭建了 Tracing 系统。比如右图对于 show region 的请求,它的调用链路如上图所示,我们可以在 Grafana 中去展示这个请求跨进程通信时,不同进程内部详细的耗时信息,这对于慢查询的性能问题的排查效率会有显著的提升。
总体而言,IoTDB 的可观测性能力在今年发生了质变,我们有信心也非常欢迎我们的用户朋友前来体验。
03
IoTDB 监控模块
接下来我会着重介绍一下 IoTDB 的监控模块。
对于监控模块而言,其实它的灵魂是它拥有哪些监控指标。这里我们参照火焰图作者著作《性能之巅》的思路,从负载分析和资源分析这两个相反的角度去互补推进 IoTDB 监控指标体系的建设。
对于自顶向下的负载视角,我们首先对客户端写入 IoTDB 的流程进行了拆解。对于 IoTDB 的每一个连接,当它把请求交给 IoTDB 去执行时,这个连接会被视为忙碌状态;当它在客户端攒批,或者在向服务端传输的时候,这个连接会被视为闲置状态。通过这种区分,我们就能够对瓶颈是否在 IoTDB 内部有一个评估。比如说,如果连接的平均的繁忙时间是 10 毫秒,但是它之后却要闲置 5 分钟,那基本就可以证明瓶颈不在 IoTDB 内部了。
那如果发现确实瓶颈在 IoTDB 内部,这个 busy time 比 idle time 要更大,那如何进一步去寻找瓶颈呢?我们也对 IoTDB 写入请求延迟进行了更细粒度的拆解,将写入流程分成了若干的子阶段,并且对一般情况下更为耗时的阶段进行了更细致的拆分,从而确保我们能够发现瓶颈到底在哪个模块。比如说在右边这张图里面,我们一级的子阶段就包括了权限验证、SQL 解析、分析、规划以及具体的执行等等。如果我们在调度执行阶段,发现 Remote Schedule 的 QPS 一直不为零,那就说明一直存在远程转发,这个时候我们就需要去排查客户端的分区缓存是否失效等等。
总之,通过这种自顶向下的分析,我们能够去找到系统当前的瓶颈到底是在哪些模块。
对于自底向上的资源视角,我们主要做了四个维度的考虑。首先在磁盘方面,我们希望我们的监控是比 Linux 常用的磁盘监控工具 iostat 要更丰富的。比如除了磁盘的利用率、吞吐 IOPS,我们还希望去监控进程级别的读写流量,以及 page cache 的使用情况等等。
对于网络,我们希望我们的监控也是比 Linux 下常用的网络监控工具 sar 要更加丰富,比如除了网络的吞吐,我们还想监控进程级别的连接个数等等。
在 CPU 层面,我们想要去区分操作系统和进程自身的 CPU 占用率,以及想要进一步区分进程内部不同模块和不同线程池的 CPU 利用率,还想进一步统计进程内部线程池的关键参数,比如说活跃的线程个数,以及队列里面堆积的任务个数等等。
在 JVM 方面,我们想要对堆内和堆外的内存大小进行实际的观测,以及要对不同状态的线程个数进行实际观测。我们还要对每一次 GC,不论是 Full GC 还是 Young GC 的情况进行具体的观测,来辅助我们的 GC 调优。
总之,通过这种自底向上的分析,我们能够为很多模块的瓶颈原因来提供一个具体的思路。
那到了 1.3.0 版本,我们前面提到的这些监控指标都已经实现了,那么监控模块对于性能的影响到底大不大呢?我们敢不敢在线上去打开呢?其实这块我们也有做持续的性能优化,以下是一些统计的结果。
IoTDB 的监控指标从 1.0.0 版本的 134 个,到现在已经涨到了 905 个,增加了接近 9 倍。但监控模块的 CPU 开销反而从 11.34% 降到了 5.81%,其中有一部分还是后台运行的 CPU 开销,对于读写性能的影响也从 <7% 降到了 <3%。因此大家可以放心地在线上去开启监控模块,它对于系统运维的收益绝对远远大于那一点点的性能损耗。
04
IoTDB 监控面板
基于这些监控指标,接下来我们来简单介绍一下 IoTDB 的监控面板,主要分为四个监控面板,分别是 Performance Overview、System、ConfigNode 和 DataNode,下面将给出这些面板的具体示例。
对于 Performance Overview 面板,我们汇总统计了集群的基本信息,比如说集群大小、总时间序列个数、总写入吞吐、上线时间等等。同时我们还以延迟拆解的方式,去展示了客户端写入不同阶段的耗时统计,来用于辅助定位瓶颈到底出现于哪些模块。
任何一个子面板,我们都写了详细的注释,比如左图这个面板就展示了不同 thrift 接口的耗时统计。此外,我们也可以在一个面板中去同时查看多个节点的监控数据,用于定位相同时间不同节点的状态。
在资源方面,对于 System 面板,我们也提供了 CPU、JVM、网络、磁盘等方面的监控数据,用于定位系统资源是否是瓶颈时非常管用。
对于 ConfigNode 面板,它也汇总统计了集群的基本信息,还提供了元数据以及数据分区 Leader 分布等维度方面的监控。在用户定位集群的扩展性能力时,它是非常有用的,比如是否所有的节点都承载了读写流量、Leader 是否分配均匀、是否有节点宕机等等。
对于 DataNode 面板,它汇总统计了单节点引擎内部的细致监控,比如说存储引擎、查询引擎、元数据引擎、共识引擎、流处理引擎等等,在判断模块内部瓶颈原因时非常有用。
05
典型案例分享
IoTDB 现在有接近上千的监控指标,这些指标也不会在今天短短的分享中就介绍完,那接下来我就分享五个典型案例,来展示一下 IoTDB 监控模块的能力。
第一个案例是在某高吞吐量场景下如何去确认瓶颈所在。当业务链路比较复杂的时候,如果整体的性能不达标,用户其实是不太好去确认到底瓶颈是在业务上还是在 IoTDB 中的,比如说 Flink 消费 Kafka 去写入 IoTDB,整体性能就是不符合预期,那到底瓶颈是 Kafka、Flink 还是 IoTDB?就不太好去定位。那现在有了 IoTDB 监控模块之后,我们其实是可以去帮忙定位瓶颈是否在 IoTDB 中的。
比如对于这个用户案例,它也是一个 Flink 去实时消费 Kafka 数据来写入 IoTDB 的用户场景。这个业务链路是有 128 节点的 Kafka 集群、96 节点的 Flink 集群和 IoTDB 集群。由于整个集群的规模确实比较大,所以它每次去部署测试、运维调优的成本都比较高,也比较耗时。当时跑通整个链路之后,用户给我们的反馈是整体的写入性能不够,那他们就认为是 IoTDB 的写入性能不够,比如说“IoTDB 集群总写入吞吐仅有 15GB/s”、“扩展性很差”等等。
于是我们就进行了一个具体的瓶颈排查,我们排查之后发现其实瓶颈并不在 IoTDB,而是在业务上层,那么是怎么得出这个结论的呢?我们发现首先从连接角度,每个连接大概平均空闲 4s 之后,才会去繁忙 20ms,同时每个节点平均每秒钟仅仅接受了 3 个请求。而且在 System 面板里面,DataNode 的 CPU、JVM、网络、磁盘 I/O 利用率都非常低,这其实就说明了瓶颈根本没有在 IoTDB 自身,而是在上层业务那一块就已经卡着,写不到 IoTDB 里面去了。
所以我们也推动了业务侧进行了性能问题的复查。他们发现其实把 Flink 的 Sink 侧置为空写,整体吞吐也才 20 GB/s,进一步实锤了瓶颈实际是在 Flink 端。之后用户也找到了 Flink 侧的问题,也对它们进行了优化。
当业务调整之后我们也进行了复测,我们发现 IoTDB 集群的整体吞吐达到了 62.6 GB/s,相比之前有 4 倍以上的性能提升,集群的线性比最高也达到了 0.89。这个案例其实想说的就是,如果没有监控模块指导我们去推动业务侧的改造,我们还一门心思地在数据库内部去寻找瓶颈,那最终的结果一定是事倍功半。
第二个案例是某车联网场景下的写入性能尖刺调优。由于 IoTDB 是用 Java 写的,很多用户会去询问 GC 对于 IoTDB 的影响到底有多大?其实我们在内存中做了比较多的池化,来自己管理内存,尽量减少 GC 对于 IoTDB 性能的影响,所以在大部分场景下,用户其实是感知不到 GC 对于性能的影响的。但是确实也存在部分场景下可能会感知到,比如这个案例就是 GC 导致了写入性能的尖刺,遇到这种情况应该怎么去处理呢?其实现在 IoTDB 的监控模块去内嵌了一个 GC 调优的分析器,是具备对 GC 进行深度观测和调优的能力的,接下来让我们一探究竟。
该场景的架构整体是一个 3C 12D 的 IoTDB 集群,也是 Flink 实时消费 Kafka 的数据来写入 IoTDB。
在写入压测的过程中,我们发现 IoTDB 的写入吞吐能力基本是符合预期的,也基本满足了业务的需求。但是主要存在的一个问题就是会存在写入尖刺,有时吞吐会降低到接近 0 的程度。进一步排查原因之后,我们发现这是由于 JVM 大概每 20 分钟会进行一次 Full GC,而每一次 Full GC 都会耗时一分钟以上,这样的 GC 其实是非常不健康的,会影响业务的 P99 延迟等等。
那对于 GC 应该怎么去调优呢?大家常用的一种调优方式可能是启动 JVM 的时候把 GC 日志打开,然后测试一段时间之后,上传 GC 日志到特定的网站去进行一些分析。这些网站会将不同原因导致的 GC 进行运行耗时和次数的汇总,然后我们就可以基于这些聚合后的高密度的 GC 信息,再分析如何去调优 GC 算法的参数。
在建设好 IoTDB 的可观测性之后,那现在 IoTDB 如何去做 GC 调优分析呢?我们不需要去依赖这种网站,直接用我们自己的 System 面板就可以做到。我们首先提供了一个 GC 耗时比例的新手指标,它表示了 GC stop-the-world 的时间占整个 JVM RunTime 耗时的比例。如果这个比例小于 5% 到 10%,其实说明 GC 对于系统整体的吞吐影响不大,这个时候如果对于延迟没有什么额外的要求,一般就不再需要再进一步的调优了;如果这个比例大于 10% 到 15%,一般说明我们需要对 GC 进行进一步的调优。
我们也提供了若干的专家指标,比如说我们对不同 GC 原因导致的耗时和次数进行了统计,这其实就是那些网站做的事情。同时我们还记录了新生代和老年代每分钟申请的内存空间的晋升大小;我们在每次 Full GC 之后对于常驻内存的对象也进行了一个统计;我们还在持续观测堆内堆外各个区的内存的大小,这些都能够对 GC 调优提供数据支撑。
在该用户场景下我们进行了 GC 调优,我们的主要思路首先是将 GC 算法从 PS 换成对大内存更为友好的 G1。接着我们又结合负载和 IoTDB 的特点,进行了 GC 参数的调优。比如因为 IoTDB 自己做了一些内存池化,所以我们去延缓了 G1 的并发标记的阈值,来减少一些无谓的 CPU 的消耗。同时我们也提升了一些 MixedGC 的吞吐,来增加一些并行度等等。此外我们也控制了单次 GC 的耗时的软上限,在每次 GC 的收益、吞吐和延迟的 tradeoff 中间找到一个对用户更为友好的角度。
最终我们调优的结果是写入吞吐稳定,没有了 Full GC,同时写入性能也提升了约 50%,还是比较可观的。这个案例主要是想说明一下 IoTDB 对于 GC 的深度观测能力和调优能力。
第三个案例是某测试场景下的硬件瓶颈原因探究。在一些 POC 阶段,当系统出现瓶颈时,如果将系统视为黑盒,其实我们是不知道如何去升级硬件的收益是最高的。比如说在一些云上的环境,升级硬件其实是很方便的,但如果这个时候我们把它当成黑盒的话,我们也不知道应该怎么去升级硬件,如果全部升级的话,可能成本又太高。那这个时候结合 IoTDB 的监控模块,我们就可以量化地算出升级硬件带来的潜在的性能收益,从而用于选择收益最高的硬件升级方案。
比如在这个测试场景下,我们用 2 个客户端机器去压测了一个 1C1D 单节点的高配机器,发现系统的性能仅仅为 1.2GB/s,这其实是不太符合我们对如此高配的机器性能的一个想象的。
接着我们对系统资源进行了分析,发现 CPU 和网络都没有达到瓶颈,但磁盘的繁忙程度达到了 100%,吞吐也达到 200MB 以上,应该是吞吐繁忙导致磁盘的利用率被打满了,磁盘又限制了整体的吞吐。这个时候就需要升级磁盘才能获得进一步的新的提升了,升级 CPU 和网卡是不会有任何收益的。
这其实就是典型的木桶效应,在理想情况下,所有的资源应该同时达到瓶颈,这样硬件资源才没有被浪费。但是在实际情况中,往往会有个别的资源先达到瓶颈,从而限制整体的性能。如果没有精准地定位到这个硬件瓶颈,反而去升级其它的硬件,最终的结果就是白花了钱,也不会得到任何收益。
在升级磁盘之后,我们发现磁盘和网络不再是瓶颈,写入吞吐也提升到了原来的 2.5 倍,从 1.2GB/s 升级到了 2.8GB/s。这个时候 CPU 又成为了新的瓶颈,这种情况下一种方式就是去进一步升级 CPU,但另一种方式是,我们也可以结合系统资源,去对内核的一些算法的策略做一些更有针对性的统计。比如说我们可以选择一些对 CPU 消耗更小,但压缩比没那么高的压缩算法,也可以去掉网络上的一些默认的压缩等等来节约 CPU,来把更多的资源打到磁盘上,这样就对各个资源能够有一个更好的利用。
通过该案例,其实是可以说明 IoTDB 的系统资源监控可以帮助我们快速地找到硬件瓶颈,从而用最小的成本来达到最大的硬件升级的收益。
案例四是某周测场景下的写入性能波动变大的问题排查。对于服务器的 CPU 利用率出现波动,其实这种问题是比较难去排查的,因为它不一定持续,等到我们去排查的时候,可能这个现象已经消失了,已经不再波动了,那就更无从查起了。这就需要对系统的 CPU 利用率进行持续的监控。
对于这类问题,IoTDB 的监控模块内置了操作系统、进程、内部线程池以及模块 CPU 的利用率监控。首先我们就可以通过操作系统和进程来判断这个波动到底是不是 IoTDB 引起的,也有可能根本就不是 IoTDB 引起的。那如果是的话,我们也能够进一步去找到到底是 IoTDB 内部的哪一个线程池、哪一个模块引起的,这样我们就可以根据它们的细微波动来给出进一步的调优建议。
比如该问题就是在我们日常的周测场景中,我们发现 1.2.0 RC5 版本,它的写入吞吐相比之前的一个版本是波动更大的,这里上图是新版本,下图是老版本,确实能看见波动更大。这其实是属于比较细致的观察了,因为它不一定对业务有什么影响,但是我们也没有放弃这一次机会去进行原因的探究。
首先,我们排查了操作系统及进程的 CPU 的监控,我们发现新版本的 CPU 利用率波动更大,并且已经已经接近了 50%,我们就猜测它应该是导致写入性能波动变大的一个原因。
然后我们就拿出了我们的“大杀器”,去排查了线程池 CPU 利用率的监控,这个很直白地就能够找到原因,就是我们发现,新版本的 CPU 利用率波动更大,是因为 1.2.0 RC5 版本中后台执行的合并线程池的利用率更大。左图是新版本,它的利用率会在 0% 到 18% 之间产生明显的波动;右图是老版本,它主要稳定在 8% 左右。
更进一步地,我们也去排查了存储引擎 TsFile 层级的监控,我们发现新版本存储引擎的合并速度会更快,文件状态也会更健康。
因此我们就检查那一段时间合入的代码,发现是新版本修复了之前版本中合并模块 I/O 大小预估偏大的一个问题。这个问题可能会导致之前受 I/O 限速不能执行的合并任务,之前不能够被执行,现在能够被执行了,所以系统可以做更多的工作,从而使得 CPU 产生更大的一个波动。
至此我们也明确了该现象的根因。考虑到改进后文件合并得更为健康,其实是对于查询更为友好的,所以我们也没有进一步去修改这一块默认的限速参数。但如果是对于写入性能波动有要求的场景,我们也可以进一步去降低合并模块的限速,从而达到与之前版本近似的一个效果。
这个案例主要是想说明 IoTDB 对于 CPU 利用率的观测和掌控能力。
最后一个场景是某钢铁厂场景的写入性能周期性下降 5% 的排查。随着我们的可观测性做得不断深入,很多用户也开始对我们的监控面板越来越感兴趣,有事没事每天都会上来翻一翻。那在翻的过程中,如果发现监控面板有一些不影响业务的异样,但是技术上又感觉解释不清楚,那是否有必要深挖呢?其实我们是非常欢迎并鼓励这样的行为的,因为这个案例就是由于我们细心的用户的深挖,反而促进了我们后续内核迭代的进一步演进,从而达到了双赢的一个效果。
这个场景是一个 3C3D 的集群,客户端会定期地攒批写入 IoTDB,也会定期地去查询单设备过去一天的全量数据。用户在日常巡检监控面板的过程中,发现了一个有趣的现象,就是每七天会出现一次持续一天的 5% 的性能下降。这个问题其实可大可小,如果没有我们的监控面板的话,可能业务都不会感知到这件事情的存在,但是细心的用户还是给我们进行了一个反馈,于是我们也抽出了不少精力去进行了排查。
我们首先排查了系统及进程的 CPU 监控,发现写入性能下降 5% 的时候,系统的 CPU 的占用率也增加了 5%,并且到了 80% 左右,已经接近瓶颈了。那我们就怀疑 CPU 利用率的升高应该是写入性能下降 5% 的根因。
然后我们排查了写入延迟的拆解监控,发现写入性能刚下降的时候是存在跨节点的转发的,这就代表了客户端的缓存失效。同时我们也观测到,写入性能下降的时候,调度执行阶段的 P99 耗时增加,但是平均耗时没有增加,这基本就说明是由于 CPU 接近瓶颈了,后台任务的繁忙导致 P99 会增加,但 AVG 增加得不太明显。通过这两个点基本可以确认,写入性能下降的时候,IoTDB 切换了新的时间分区,导致数据需要被写到新的节点。这个会对我们最终的性能下降 5% 会有什么影响呢?
接着,我们也对查询进行了分析。因为前面也提到,查询的需求是查询最近一天的数据,尽管它的逻辑数据量始终是最近一天的数据,可能逻辑上和具体物理上最终查询的数据量都是一样的,但如果进行了跨分片的查询,那就会涉及到更多的 operator 算子和跨界点的序列化、反序列化的开销,这也会对 CPU 造成更大的消耗。
至此,该问题的原因就找到了,主要是因为产生了跨时间分区的查询,消耗了更多的 CPU,导致整个系统的 CPU 利用率增加了 5%,最终导致写入性能也在这一段时间稳定下降了 5%。当这一天的时间过去之后,这个性能下降 5% 的问题又缓解了,又消失不见了。
这个问题的原因找到了,它也催生了 IoTDB 内核后续的两个优化。一个是尽量使得同一设备的数据保留在同一个分片中,这样既可以避免该现象的出现,同时对于一些聚合查询来说也更为友好。另一个就是线程池 CPU 利用率的监控,有了它的话,我们就可以直接去观察到,这增加了 5% 的 CPU 都是在查询线程池里面导致的,而不用再去看 OPS 之类的其他一些信息,这样排查的效率会更高。
这个案例主要说明了 IoTDB 对这种很细微的、业务感知不到的波动,也具备诊断和内核迭代的能力。
最后我们对以上五个案例做一个总结。IoTDB 的可观测性目标是能够高效定位遇到的一切性能问题。虽然相比 Oracle 这种比较成熟的商业数据库,我们还有很长的路要走,但大家已经能够看到我们这一年产生的质变。据我们的经验而言,针对硬件环境和业务负载进行调优,一般可以获得 50% 到 1000% 的性能提升。所以我们非常欢迎大家来试用我们的商业版 IoTDB,也非常欢迎大家在使用监控模块的过程中,对想不通的性能问题来与我们沟通交流,我们也期待与用户一起获得业务和技术上的共同成长。
我的分享就这么多,谢谢大家。
可加欧欧获取大会相关PPT
微信号:apache_iotdb