2023 年终总结:从清华 Apache IoTDB 组到创业公司天谋科技
本文最后更新于:7 个月前
前言
兜兜转转又是一年,不知不觉 2023 已经结束。回想自己过去一年的成长与感悟,依然觉得是收获满满。今年工作之后闲余时间相比学生时代少了许多,到了除夕才有时间来写今年的年终总结。好在自己还是下定决心将这个习惯坚持下去,希望这些年终总结不仅能够在未来的时光里鞭策自己,也能够获得更多大家的反馈来修正自己。
首先依然是自我介绍环节,我叫谭新宇,清华本硕,师从软件学院王建民/黄向东老师。目前在时序数据库 Apache IoTDB 的商业化公司天谋科技担任内核开发工程师。我对分布式系统、可观测性和性能优化都比较感兴趣,2023 年也一直致力于提升 Apache IoTDB 的分布式能力、可观测性和写入性能。
接下来介绍一下我司:
天谋科技的物联网时序数据库 IoTDB 是一款低成本、高性能的时序数据库,技术原型发源于清华大学,自研完整的存储引擎、查询计算引擎、流处理引擎、智能分析引擎,并拓展集群管理、系统监控、可视化控制台等多项配套工具,可实现单平台采存算管用的横向一站式解决方案,与跨平台端边云协同的纵向一站式解决方案,可方便地满足用户在工业物联网场景多测点、多副本、多环境,达到灵活、高效的时序数据管理。
天谋科技由全球性开源项目、Apache Top-Level 项目 IoTDB 核心团队创立。公司围绕开源版持续进行产品性能打磨,提供更加全面的企业级服务与行业特色功能,并开发易用性工具,使得 IoTDB 的读写、压缩、处理速度、分布式高可用、部署运维等技术维度领先多家数据库厂商。目前,IoTDB 可达到单节点每秒千万级数据写入、10X 倍无损压缩、TB 数据毫秒级查询响应、两节点高可用、秒级扩容等性能表现,实现单设备万级点位、多设备亿级点位管理。
目前,IoTDB 能够为我国关键行业提供一个国产的、更加安全的、性能更加优异的选择。据不完全统计,IoTDB 已服务超 1000 家以上工业企业,在能源电力、钢铁冶炼、航空航天、石油石化、智慧工厂、车联网等行业均成功部署数十至数百套,并扩展至期货、基金等金融行业。目前已投入使用的企业包括华润电力、中核集团、国家电网、宝武钢铁、中冶赛迪、中航成飞、中国中车、长安汽车等。
2023
介绍完背景后,在这里回顾下 2023 年我们系统组的主要工作,可分为高扩展性、高可用性、可观测性、性能优化、技术支持和技术沉淀 6 个方面。
在高扩展性方面,我们主要做了以下工作:
- 计算负载均衡: Share Nothing 架构面临的主要挑战之一是扩展性问题,扩缩容过程中需要迁移大量数据,这不可避免地消耗系统资源,进而影响现有的读写性能。为了解决这个问题,Snowflake 带头在业界推广了存算分离的架构设计,近年来的 Serverless 架构则进一步追求了更极致的弹性。尽管存算分离架构能够避免在扩缩容时迁移大量数据的问题,但它仍面临着冷启动问题。也就是说,当一个计算节点宕机后,从对象存储服务恢复宕机节点数据的过程可能会比较耗时,这对于对 SLA 要求极高的应用场景构成了挑战。那要如何解决这一问题呢?在 VLDB 2019 的论文中,ADB 介绍了其架构解决方案,其中一个值得注意的点是,它对本应无状态的 ReadNode 实施了热备份。虽然论文没有解释为何采取这种做法,但很明显,这种方案可以通过增加机器资源消耗来确保 SLA 指标,从而进一步说明了面对不同业务场景和问题时,不同架构可以找到更加适合的 trade-off。针对 Share Nothing 架构的扩展性问题,乔老师今年引导我们探讨了在时序场景中是否可能避免扩缩容时的数据迁移。我们发现,相比传统的 TP/AP 场景,时序场景有几个不同之处:首先,读写负载相对更加稳定可预测;其次,大部分情况下数据的时间戳会呈现正态分布,并随着时间不断递增。这为我们提供了结合场景进行优化的可能性。我们通过将数据划分为不同的时间分区,并在新的时间分区到来时进行实时负载均衡分配,从而实现了无需迁移数据即可达到计算资源均衡的效果,甚至在运行 TTL 时间后,还能进一步实现存储和计算资源的双均衡。回顾我们的设计,通过牺牲新节点立即提供服务的能力,我们避免了扩容时的数据迁移,这在大多数负载可预测的时序场景下取得了良好的效果。当然,对于一些特殊场景,我们也提供了手动 Region 迁移的指令,以便运维人员根据业务需求,在存储和计算资源的平衡时间上进行手动调整。
- 分片分配算法:在今年上半年针对某用户的 12 节点 2 副本场景进行高可用性测试时,我们遇到了一个问题:当我们故意使一个节点宕机后,发现另一个节点出现了 OOM 现象。深入分析后,我们明白了问题所在:由于整个集群仅有 6 个副本集合,一个节点的宕机导致约 1/6 的 Region Leader 被迫迁移到了同一节点上,这导致了该节点过载,进而出现 OOM。其实这一问题是一个典型的分片分配问题。我们调研学习了来自 Stanford 的 ATC 2013 Best Paper Copysets 论文以及该作者两年后在扩缩容场景对 Copysets 算法的补充,并决定将该算法应用到 IoTDB 中。通过这一改动,客户场景中的节点散度从 1 增加到了 5.11,这意味着当单个节点宕机时,多个节点能够分摊待迁移 Leader 的压力,有效避免了 OOM 现象的发生。此外,集成 Copysets 算法还带来了其论文提到的对于数据丢失概率和副本恢复速度的提升。回顾这项工作,最让人印象深刻的是陈老师的深厚算法功底。在我们努力理解论文理论证明的过程中,陈老师补充了论文中遗漏的公式证明。当陈老师引入泊松过程的概念时,我们尚能跟上步伐;然而当陈老师引入指数型随机变量和连续马尔可夫链的概念时,我们只能赞叹:天不生陈老师,飞书 Latex 公式功能万古如长夜了。
- 企业版激活:对于企业版软件实现可信授权,我们面临多项挑战:如何在不依赖网络的情况下部署,同时通过硬件绑定来防止许可证的滥用?如何设计一个系统,让激活次数不再受节点数量的限制,以提高整个集群的激活效率?我们还需要引入一系列的使用限制,包括许可证的有效期、节点数、CPU 核心数、序列号和设备数等等。此外,还需考虑如何防止各种潜在的破解尝试,比如回调系统时间、复制文件目录、在使用相同机器码的云平台上部署等,同时保证这些安全措施不会影响到商业用户的使用体验,例如支持非 root 用户激活、提供一键激活功能等。面对这些问题,我们逐一制定了解决方案并加以设计实现。在这个过程中,我和宇衡对各种 Corner Case 进行了深入的分析和讨论,这段经历让我受益匪浅。
在高可用性方面,我们主要做了以下工作:
- IoTConsensus:在过去的一年里,我们针对基于异步复制思路的 IoTConsensus 共识算法,在性能、稳定性、鲁棒性和可观测性方面做出了显著提升。如今,在线上的大部分场景中,该共识算法已经被优化至接近实时同步的效果,基本上不会再出现因为同步速度跟不上写入速度而导致的 WAL 堆积现象。接着我们开始思考一个命题:在异步复制系统中,不考虑节点宕机等异常情况,是否能在任何写入负载下都保持同步速度与写入速度同步?通过对 MySQL binlog 异步复制等类似场景的观察,湘鹏和我通过排队论的论证和性能实测发现,这个假设是错误的。这一发现促使我们开始进一步探索和设计基于操作变更到状态变更的共识算法。尽管理论上 Leader 侧的 WAL 堆积问题似乎无解,但在实际工程应用中,我们找到了解决办法。我们不仅在多个方面迭代优化以减少 WAL 堆积的可能性,还特别总结了导致 WAL 堆积的八大潜在原因及其解决策略。目前,我们团队已有许多成员能够独立地诊断并解决这一问题,有效地消除了这一单点瓶颈。
- RatisConsensus:今年,我们对 Apache Ratis 社区做出了显著贡献,包括引入了基于 Read-Index 和 Lease Read 的线性一致性读功能,以及若干状态机易用的 API。我们还提高了 Snapshot 传输的稳定性,并提交了超过 30 个 patch,涵盖了各种 bug 修复。除此之外,宋哥不仅多次担任 Ratis 社区的 Release Manager,近期还荣幸被邀请成为 Apache Ratis 社区的 PMC 成员。宋哥作为目前 Ratis 社区 Top3 活跃的开发者,已经时常被我们开玩笑称为 Ratis 社区 Vice PMC Chair 了。
- 共识层:去年,IoTDB 共识层参考了 OSDI 2020 Best Paper Delos 的思路进行了设计和实现,支持了多种具有不同一致性和性能特性的共识算法。今年,我们在性能与一致性级别这两个维度上对其支持的不同共识算法进行了深入的对比分析,为 IoTDB 的实施及用户在选择共识算法时提供了重要参考。我们还广泛调研了多种数据库的共识算法实现,通过文档阅读、代码走读和性能实测等多种方法,从共识算法的功能和性能开销等多个角度进行了细致地对比,并取其精华,去其糟粕。此外,今年我们在一些内存紧张的特殊场景下,发现 IoTConsensus 可能会出现副本不一致的问题。经过排查,我们认识到问题并非出在共识算法本身,而是由于状态机执行的不确定性导致的。虽然理论上根据 RSM 模型,所有副本应当达到一致状态,但在实际工程实践中,许多问题都可能使得 RSM 模型不完全适用,比如 Leader 的磁盘写满而 Follower 的磁盘未写满,可能引发执行的不确定性。针对这一问题,我们咨询了曾在 OB 工作的剑神,并在知乎上发问探寻大佬们的解决思路。收到的许多反馈都倾向于“Fail Fast”的处理原则,这可能是因为对许多 TP 系统而言,一致性比可用性更为重要。然而,对于时序场景,可用性往往比短暂的不一致性更加重要。因此,我们认为在遇到此类问题时直接退出进程并不是一个合适的解决方案。为此,我们通过在共识层捕获此类异常并采取有限重试的策略,以避免让业务感知到这种现象,从而保证了系统的高可用性和一致性。
在可观测性方面,我们主要做了以下工作:
- 监控面板:今年,我们借鉴了火焰图作者在《性能之巅》中的思路,从用户视角和资源视角出发,构建并完善了四个监控面板,共计近四百个 panel。这些面板的建设旨在提供全面的性能监控和分析能力,帮助我们更有效地诊断和解决性能问题。首先,我们设立了 Performance Overview 面板,该面板汇总了集群信息,不仅能帮助我们判断性能瓶颈是否存在于 IoTDB 中,还能进一步拆解并统计不同类型请求的延迟分布,从而精确定位到 IoTDB 内部读写流程的具体瓶颈环节。其次是 System 面板,它聚焦于系统资源,包括网络、磁盘、CPU、线程池利用率、JVM 内存和 GC 等多个维度的监控数据。这个面板为系统资源瓶颈的分析提供了丰富的数据支持,使我们能够从资源层面进行深入分析。接下来,我们还有包含集群节点状态、分区信息等的 ConfigNode 面板,以及涵盖存储、查询、元数据、共识和流计算等引擎监控的 DataNode 面板。这两个面板从不同角度提供了 IoTDB 集群的详尽状态和性能信息,为我们提供了全面的监控视图。在这个过程中,我们团队中也涌现出了包括吾皇,彦桑在内的多位 Grafana 艺术家。他们运用 Grafana 的高级功能,创造了许多既美观又实用的监控面板,所有这些都是各位艺术家精心设计的作品。
- 监控模块:在过去一年中,随着 IoTDB 各模块可观测性的显著提升,监控指标数量从 100 多个增加到了 900 多个。尽管监控指标数量增加了近 10 倍,但监控模块在火焰图中的 CPU 开销却从 11.34% 下降到了 5.81%,实现了显著的开销节省。这一成就主要归功于俊植、洪胤和我对监控模块的持续迭代和优化。我们不仅对 IoTDB 自身的监控框架进行了大量优化,还结合了 Micrometer 和 Dropwizard 这两个 Metric 库,通过白盒调参或自研选择了对写入操作最友好的实现方式,并针对不同监控指标类型进行了精细化管理。此外,今年雨峰、洪胤和我还持续完善了线上 IoTDB 的巡检文档、告警文档以及面板快照的导出方法等,进一步提升了运维工作的效率和便捷性。通过整个团队一年的共同努力,我们的监控模块不仅大幅提高了问题排查和性能调优的效率,而且已经成为运维 IoTDB 不可或缺的工具。现在我也可以非常自豪地说,IoTDB 现在的可观测性水平已经接近 2022 年暑假我在 PingCAP 实习时体验到的 TiDB 的可观测性水平,在时序数据库中也处于领先地位,这对于我个人和我们组来说是一个巨大的成就。
- 日志精简:今年,我们注意到 IoTDB 线上环境中日志打印量较大,这在一定程度上影响了问题排查的效率。随着监控面板的日益完善,许多原本需要通过日志记录的性能统计信息已经能够通过监控模块以更高的信息密度进行记录,这使得部分日志变得不再必要。因此,吾皇和我针对 36 个用户和测试场景进行了深入的日志挖掘分析,筛选出了 62 条出现频率较高的日志记录。经过与各模块负责人的逐一讨论,我们对其中 23 条日志进行了降级(例如从 info 降至 debug)或直接删除的优化处理。此外,团队内部就如何打印性能调优、系统关键行为、SQL 执行错误等异常情况的日志达成了共识。通过这次日志精简工作,在不同场景下我们总共减少了约 37% 到 74% 的日志打印量,取得了明显的效果。其实这项工作可大做可小做,但我们还是非常认真地编写了日志分析脚本进行分析,并进行了量化的数据统计和效果预估。完成这项工作后,有一次我和在北大读博做可观测性研究的张先生闲聊,居然发现我们的工作思路与他们领域内腾讯和中山大学在 2023 ICSE 上发表的顶会论文 LogReducer 非常相似。这种巧合让我感到非常有成就感。我们的工作不仅提升了 IoTDB 的运维效率,还与学术前沿领域的研究工作不谋而合,证明了我们的方向和方法是具有前瞻性和实际应用价值的。
在性能优化方案,我们主要做了以下工作:
- 某知名测试场景性能调优及打磨:今年后半年,我和刚上博一对 IoTDB 几乎 0 基础的谷博共同投入到了某知名测试场景的瓶颈分析、性能调优和内核迭代中。在短短三个月的时间里,谷博迅速成长为一个具备系统思维和深度 IoTDB 调优能力的专家。我们的努力最终获得了显著成果,不仅在该测试场景中取得了第一名的成绩,还通过了第三方的评测。这一成就不仅证明了 IoTDB 1.x 架构的出色性能,也让我们对于 2024 年能够实现更进一步的成绩充满期待。
- 写入性能优化预研:IoTDB 之前主要集中在列式写入接口的性能迭代,而对行式写入接口的关注不足。鉴于今年许多用户由于各种原因必须使用行式接口,我们迫切需要对行式写入接口进行深入的瓶颈分析和性能优化。借助于我们目前的可观测性能力,以及对各种性能分析工具(如 JProfile、Arthas)的熟练使用,旭鑫和我对可能的性能提升方案进行了大量的 demo 级别预研。针对典型场景,我们已经找到了 5 个主要的优化点,预计完成这些优化后性能将提升一倍以上。当然,性能优化是一项需要持续投入的工作。当把目前发现的主要优化点做进去后,我们也会基于新的 codebase,继续探索新的瓶颈和优化方案。在这个过程中,我们意识到最重要的是积累理论建模能力和系统思维。如何针对任何系统分析当前的瓶颈并提出有效的优化方案,成为了我们在这项工作中积累的最宝贵财富。
在技术支持方面,我们主要做了以下工作:
- IoT-Benchmark 基准测试工具的发展:IoT-Benchmark 在过去一年中实现了显著的功能提升,特别是在写入能力(跨设备写入)、查询能力(align by device/desc/limit 查询)和元数据建模能力(支持不同 TagKey 层级设置 TagValue 个数)方面。通过持续的迭代更新(50+ commits),我们不仅增强了工具的功能和稳定性,还吸引了其他时序数据库社区的贡献者,如 CnosDB 的开发者就在最近为我们贡献了 CnosDB Client Driver 的代码。我们期待 IoT-Benchmark 能够成为时序数据库领域内公认的基准测试工具,为不同的时序数据库提供一个公平竞技的平台。
- POC:今年我们组参与了 10+ POC 项目,覆盖了海、陆、空、天等多个领域,并成功部署上线了 95 节点的 IoTDB 集群,实现了 62.6 GB/s 的最大吞吐量和 0.8 以上的集群线性比。参与这些带有挑战性的项目并最终成功落地还是非常让人有成就感的。
- DBA 宝典:在乔老师的带领下,我们逐步构建了面向 IoTDB 的 DBA 宝典。通过梳理异常排查方案和问题导图,我们为 33 个常见问题提供了原因分析和解决策略。DBA 宝典的存在大大降低了实施团队处理异常的难度,有效减轻了产研团队的 Oncall 负担。
- Oncall:今年,我个人承担了组内 80% 以上的 Oncall 工作,这不仅是一次对个人能力的极大考验,也是一次成长和学习的机会。通过不断地思考和解决问题,我对 IoTDB 的各个模块有了更深入的了解,并明确了可观测性建设的推进思路。值得一提的是,尽管项目数量还在增加,我的 Oncall 效率已经显著提升,感受到的压力也在逐渐减轻,这与 DBA 宝典的不断完善和实施团队技术支持团队的建立息息相关。
在技术沉淀方面,我们主要做了以下工作:
- 技术工具:今年我们梳理了常用的 JDK 和 Linux 命令,也用熟了问题排查工具 JProfile 和 Arthas。回想之前看一个 Runtime 的值还需要使用 UDF 去 hack,现在我们直接用 Arthas 就可以了,技术工具的进步极大地提升了我们的生产力。在性能调优方面,除了常见的 JProfile 线程耗时分析和 Arthas 火焰图,权博带领我们探索了 Intel vTune 工具,用于观测高性能机器上的跨 NUMA 访问比例和 CPU 前后端执行效率等。随着 IoTDB 性能优化进入深水区,需要不断将硬件性能进一步压榨,学会使用这些原本 HPC 才可能需要的工具也就非常重要了。
- 论文讨论班:今年我们组组织了 6 次工程讨论班和 6 次论文讨论班,对 6 个方向的共 15 篇论文进行了分享介绍,其中一些论文已经提供了写入性能的优化思路并 demo 实测有效。这中间最让我印象深刻的还是旭鑫的存算分离讨论班,我们对若干友商的云服务版本进行了计价统计,发现某些号称云原生时序数据库的系统定价显著高于其他时序数据库,我猜测是因为系统架构用了 EBS 而非对象服务吧,那么高成本就只能让用户买单了。
- JVM:今年我们对 JVM 有了一些深入的探索和技术沉淀。俊植和我细致调研了 Java 的内存分类和观测手段,通过使用 NMT 等工具,我们发现堆外内存分类居然有 19 种之多,这是我在外面的八股中从没看到的结论。在 GC 方面,俊植和我不仅完善了 GC 的可观测性指标,例如不同 GC cause 的次数和耗时以及 GC 占据 Runtime 的比例等等。我们还针对 JDK 8/11/17 的默认 GC 算法 PS 和 G1,分析学习其原理并列举其所有可调参数,搜索优质 GC 调优博客并积累 GC 调优经验。目前我们已经基本具备了对 GC 深度调优的能力,在 GC 严重场景通过调优甚至能带来 60%+ 吞吐的提升,今年我们也会不断细化沉淀这里的方法论并择机分享。在向量化 API 方面,今年旭鑫实测了 JDK21 的 Vector API,在部分场景下能够带来最大 13.5 倍的性能提升,这也是 IoTDB 未来进行性能演进的技术储备之一。
- IoTDB 磁盘文件地图:今年我们参照 Oracle/IBM 等数据库绘制了 IoTDB 的磁盘文件地图。通过该地图,我们不仅发现了一些可以潜在优化的点,还理顺了不同模块落盘文件的逻辑关系。
- 压缩算法性能测试:今年我们针对若干用户场景的真实数据进行了压缩算法的对比测试,发现大多数场景下 LZ4 相比 Snappy 有更好的压缩效果,这也促使了 IoTDB 默认压缩算法的更改。
- 难点预研:今年我们组还针对多个复杂问题,如共识组数与集群性能、线程模型、集群滚动升级方案和大 Text 值类型访存瓶颈优化方案等进行了深入的调研和测试,虽然部分工作尚未得出最终结论,但已经为未来的深入研究奠定了基础。
今年我在 Apache IoTDB 社区提交并被合并了 119 个 PR, Review 了 387 个 PR。从 PR 数量上来说相比去年和前年有了显著提升,可能是由于更加专注于工作,并且 scope 也在不断扩大吧。此外我也于今年 9 月受邀成为了 Apache IoTDB 社区的 PMC 成员,感谢社区对我的认可。
因时间所限,我今年在知乎等社交平台的活跃度有所下降。但回顾这一年,我觉得我们团队完成了许多既有趣又深入的工作,并且几乎都有相应的文档沉淀下来。这些宝贵的积累完全可以与业界分享以交流学习。我期待在 2024 年,我们团队能够更频繁地分享我们的技术沉淀,并吸引更多对技术有兴趣的同学加入 IoTDB 社区或我们的实验室进行交流!
一些感悟
性能优化:体系结构和操作系统是基本功
在深入研究和优化数据库系统在各种硬件环境及业务负载下的性能过程中,我越发认识到掌握体系结构和操作系统知识是进行性能优化的基础。今年,我在这两方面补充了许多知识,并阅读了《性能之巅》的部分章节。然而,令人感到有些沮丧的是,随着知识的增加,我反而越来越感觉到自己的无知。但我仍然希望,在 2024 年能够跨越这段充满挑战的绝望之谷,登上开悟之坡。
对于有意向学习 CMU 15-418 课程的朋友,我非常期待能够一同学习和进步!如果有经验丰富的大佬愿意指导,我将不胜感激!
GC 算法:追求吞吐还是延迟?
今年,我们组深入研究了 JDK 的垃圾回收(GC)算法,包括但不限于 Parallel Scavenge(PS)、Concurrent Mark Sweep(CMS)、Garbage-First(G1)和 Z Garbage Collector(ZGC)。我们还对 IoTDB 在相同业务负载下采用不同 GC 算法的吞吐量和延迟性能进行了比较测试,结果表明在不同的负载条件下,各 GC 算法的性能表现排序也有所不同。
在 GC 算法的选择上,我们面临着内存占用(footprint)、吞吐量(throughput)和延迟(latency)三者之间的取舍,类似于 CAP 定理,这三者不可能同时被完全满足,最多只能满足其中的两项。通常情况下,高吞吐量的 GC 算法会伴随较长的单次 STW 时间;而 STW 时间较短的 GC 算法往往会频繁触发 GC,占用更多的线程资源,导致吞吐量下降。例如,PS GC 虽然只有一次 STW,但可能耗时较长;G1 的 Mixed GC 在三次 STW 中的 Copying 阶段可能造成几百毫秒的延迟;而 ZGC 的三次 STW 时间都与 GC Roots 数量有关,因此 STW 延迟可以控制在毫秒级别。
JDK GC 算法的发展趋势似乎是在尽量减少 GC 对业务延迟的影响,但这种优化的代价是消耗更多的 CPU 资源(JDK 21 引入的分代 ZGC 有望大幅降低 ZGC 的 CPU 开销)。在 CPU 资源本身成为瓶颈的场景下,使用 ZGC 和 G1 等 GC 算法的吞吐量可能会低于 PS。GC 算法目前的演进具有两面性,例如 Go 语言就由于其默认 GC 与 Java 相比 STW 时间较短而被赞扬,但其 CPU 资源消耗大也会被批评,我们需要根据不同的目标选择合适的 GC 算法。
然而,GC 算法朝低延迟方向的不断演进仍具有重要意义,因为吞吐问题可以通过增加机器进行横向扩展来解决,而延迟问题则只能依赖于 GC 算法的改进。因此,在调优时应该有针对性,分别针对吞吐和延迟进行优化,而不是同时追求两者。如果追求吞吐量,可以优先考虑使用 PS;如果追求低延迟,可以考虑使用 G1/ZGC,并为之准备额外的机器资源以支付低延迟的代价。
全局成本:C/C++ 相比 Java 性能更好?
今年,我参与了许多问题修复和优先级排序的工作,同时深入思考了编程语言对软件开发总成本的影响。
在 PingCAP 实习期间的一次闲聊中,有些同事提出 TiDB 应该用 Rust 或 C++重写,理由是用 Go 语言编写的性能较差。然而,我的 mentor 徐总认为,采用 Go 语言后显著减少了大家的 OnCall 次数,从而节约了大量研发成本。
从纯技术的角度看,C/C++ 在极限优化下确实能比 Java 更好地发挥硬件特性。但工程开发,尤其是内核开发,不仅仅是技术问题,它更多涉及到软件工程的广泛议题。现实中,我们经常面临着无休止的问题修复和需求实现,性能优化往往未能充分利用硬件能力。我认为,尽管开发团队采用的编程语言可能影响理论上的性能上限,但在大多数工程实践中,项目成功的关键并不仅仅在于将性能优化到极致。更重要的是,在有限资源下如何优先追求满足用户需求的产品特性、如何持续保证产品的稳定性和可维护性、如何提升系统的横向扩展能力、以及如何在现有代码基础上持续进行性能优化。我相信,这些因素比起编程语言的选择所带来的潜在收益要重要得多。
因此,除了少数极特别的场景(例如追求超低延迟 or 边缘端等),选择一个团队熟悉且学习成本较低的编程语言就足够了。
工程难题:不是所有技术问题都能够立即找到解决方案
今年,我们面对并快速解决了许多棘手的问题,但同时也遇到了一些难以快速找到原因的疑难杂症。这些问题涵盖了多个方面,例如 DataNode 进程在 OOM 后仍能响应心跳但无法处理新的读写请求(这是因为 JVM 在 OOM 后随机终止了一些线程,导致监听线程被终止无法响应新连接而心跳服务线程仍在运行),以及 Ratis consensusGroupID 编码错误导致的 GroupNotFound 错误(使用 Arthas 监控后问题消失,我们怀疑这是 JVM JIT 的 bug)等。
解决这些问题的过程加深了我们对于设计新功能时对各种异常场景的考虑,有效避免了许多未来可能发生的 Oncall 问题。
在面对问题和解决问题的过程中,我深刻体会到人的认知可以分为四个象限:已知的已知、已知的未知、未知的已知以及未知的未知。其中,最难以应对的是“未知的未知”。我一直在思考工程经验这四个字究竟意味着什么?现在我认为,工程经验的积累不仅意味着将更多的“已知的未知”转化为“已知的已知”,还需要将更多的“未知的未知”变成“已知的未知”,这样才能具有可持续性。
流程体系:软件开发团队的重中之重
今年,我深刻体会到了流程体系在构建一个可持续发展的软件开发团队中的重要性。我认识到只有拥有一流的团队,才能够开发出一流的软件。
在王老师软件工程理念的统筹指导和 Apache 基金会的支持下,我认为我们的产品流程体系已经相对健全,包括但不限于以下几个方面:
- CI/CD:对不稳定的 UT 和 IT 进行持续的修复,确保代码质量和功能稳定性。
- 代码质量静态检测:利用 Sonar 等工具持续提升代码质量,确保软件的健壮性。
- Commit 级别的监控:针对不同的用户和测试场景,实现性能和资源使用量的监测,防止出现非预期的产品回退。
- 定期封版和发版:对每一项 Release Note 进行逐项测试验证,通过多轮的 RC 版本,不断收敛测试范围,确保成功发布。
- 定期的功能和技术评审会议:各模块的核心开发者共同参与,评估产品的功能和技术实现。
- 发版问题同步会:确保团队成员对 RC 验证中发现的问题能够快速响应。
- P0 项目支持任务同步会:对重要项目的支持任务进行同步和讨论。
- 多层级技术支持团队(L0/L1/L2):根据问题的复杂度,提供分层次的技术支持。
- 敏捷开发的支持工具:使用多维表格等工具,支持敏捷开发流程。
- 论文讨论班:持续学习和探索行业内的最新研究成果。
- 竞品功能和技术分析:分析竞争对手的产品,从而不断优化自身产品。
- 安全漏洞感知和修复:及时发现和修补安全漏洞,保证产品的安全性。
通过在这样的团队中工作,我对如何打造一个可持续的软件工程体系有了更深地理解。
工作管理:一键生成总结是好是坏?
随着我们组负责的模块和同学数量的增加,我逐渐发现,仅仅通过飞书文档记录工作内容的做法,虽然实现了工作的“记录”,却缺乏了有效的“管理”。例如,我们组面临的任务琐碎而多样,大家都经常会忘记一些计划中的任务;同时,我们的业务需求变化迅速,虽然大家都在同时推进多项任务,但仍然跟不上需求的变化速度。这就要求我们能够及时调整任务的优先级,以便灵活应对并优先完成 ROI 最高的任务。此外,我们以前的月度总结并没有持续进行,我分析的原因是任务汇总本身就是一种成本,导致月度总结难以持续,从而失去了很多总结沟通的机会。
为了解决这些问题,我开始学习并使用飞书的多维表格来管理团队的任务。通过多维表格,我们不仅可以清晰地看到每位成员当前的工作任务,还可以在团队会议上根据业务需要灵活调整任务优先级,甚至能够一键生成甘特图来明确不同优先级任务的时间线。在进行每周和每月总结时,我们也能够通过筛选日期快速生成任务汇总。
一开始,多维表格似乎完美地解决了我们之前的问题。然而,随着时间的推移,我发现这种方式也存在缺陷。由于总结能够一键生成,我不再每周花费一小时来统计和规划我们的周报和下周计划,甚至我们的月度总结也鲜少举办。这反而导致我们的日常开发缺乏规划,显得有些随波逐流。在东哥的点拨下,我重新开始在飞书文档中记录周报,并且连续三个月组织了月度总结会。通过定时的每周和每月汇总与沟通,团队的工作变得更加有序和明确。现在如果让我去说上半年做了什么主要工作,我可能还需要看多维表格筛选半天,但如果问我后 3 个月做了什么,我只需要看每月的月度总结就可以了。
现在,我们通过多维表格来管理任务的优先级,同时利用飞书文档来汇总周报和月报。通过对我们组流程管理的持续迭代和优化,我意识到有时候追求速度反而会拖慢进度,而适当地放慢脚步思考反而能够使我们更加高效。
团队协作:分布式系统的高扩展性和高可用性
在技术方面,我最开始深入了解的就是分布式系统,我一直在学习如何实现系统的高扩展性和高可用性。随着时间的推移,我发现这些分布式系统的理念同样适用于团队协作中。
为了实现高扩展性,关键在于让所有团队成员并行工作,而不是仅依赖于“主节点”或关键个体,这要求每个成员都能独自完成任务并持续提高自己的工作效率,这样才能提升整个团队的整体性能。同时,团队还需要能够支持成员的动态调整,如新成员的加入和旧成员的离开,确保团队结构的灵活性和适应性。
为了满足高可用性,就需要在关键任务或数据上实施冗余策略,以防止暂时的不可用状态对团队工作造成影响。这可能意味着需要在某些区域投入额外的资源,确保信息、知识或工作负载能够在多个成员之间共享,保持一致性。
这一年来,我们团队负责的模块不断增加,但每个模块都至少有 3 位以上的成员熟悉,上半年我的感受是每天从早忙到晚,连半天假都请不了。但到后半年我感觉偶尔请一两天假也不会对外产生可感知的影响了,这代表了我们组的高可用性出现了显著提升。针对我们组负责的模块,我们维护了详尽的功能和技术设计文档,以及改进措施的追踪记录,这不仅加速了新成员的融入,也保持了团队知识的一致性。此外,我们通过引入自动化工具,如飞书激活解密机器人、各类测试脚本、木马清理脚本等,有效提升了团队的工作效率,体现了我们组在高扩展性方面的进步。
希望 24 年我们组能在高扩展性和高可用性方面继续取得显著进步,为实现更加高效和稳定的团队协作模式而不断努力。
时间管理:可观测性
今年我们组的主要工作之一便是打造 IoTDB 的可观测性,目前已经显著提升了问题排查和性能调优的效率,成为线上运维 IoTDB 的必备工具。回到时间管理上,我发现可观测性的很多理念也同样适用。
随着组内同学越来越多,scope 越来越大,沟通协调的成本已经不容忽视,我自己的时间越来越不够用,逐渐成为了单点瓶颈。在向东哥请教后,我开始按照半小时为单位记录自己每天的工作内容,并定期反思每半小时的工作是否满足了高效率。
通过整理自己工作日一天 24 小时的时间分配,我发现自己实际可用于工作的时间并不超过 11 小时,因为每天基本要包括睡眠 8 小时、起床和就寝的准备及洗漱时间 1 小时、通勤 1 小时、餐饮和午休 2 小时以及运动 1 小时(有时会被娱乐消遣取代)。11 月份的数据显示,我的平均工作时间约为 10 小时(没有摸鱼时间),已经接近饱和每天都十分充实。这促使我思考如何提升自己和团队单位时间的工作效率,比如在协调任务时明确目标和截止日期,实行更细致的分工以解决我作为单点瓶颈的问题等。通过这些措施,到了 12 月份,我的平均每日工作时间减少到了 9.5 小时,而感觉团队的整体产出反而有所提升。不过,到了 1 月份,由于一些新的工作安排尚未完全理顺,我的平均工作时间又回升到了 10 小时,这需要我持续进行优化。
总的来说,定期统计和评估自己的时间分配及其 ROI,我觉得对于提高工作效率具有重大意义。
心态变化:职业发展和生活的关系
在经历了半年学生生活和半年职场生活后,我对职业发展与生活的关系有了新的认识和感悟。之前我是那种职业动机极强以至于生活显得相对单调的人。对我来说,除了那些能带给我快乐的少数娱乐活动外,生活中的许多琐碎事务如做饭洗碗,都被视为时间的浪费,不如将这些时间用于创造更多的价值。在地铁和高铁上不学习,我也会感到是对时间的浪费。我认为既然职业发展对我而言十分重要且能从中获得快乐,那么我应该将所有可用的时间都投入其中。
然而今年我的心态发生了显著的变化。我逐渐意识到,即使职业发展很重要,即使我能从中获得快乐,它也只是生活的一部分。我开始挤出更多的时间来陪伴家人,也开始与各行各业的老朋友新朋友进行交流。我不再认为生活中的全部琐事是对时间的浪费。我更加注重如何在有限的工作时间内提升效率完成超出预期的工作,而不是简单地用更多的时间去完成这些工作。
这种心态的转变对个人来说不一定是坏事。如果我的心态没有这些变化,可能会投入全部可用的时间于职业发展中,但这样的状态不确定能够持续多久。如果我的心态发生了变化,那我可能会更加注重工作效率和生活体验感,也许能达到职业发展和生活的双赢。
总的来说,每个人在不同的年龄阶段对这种平衡的感悟都会有所不同。我目前的想法是,顺应我们不断成熟的心态,选择让我们感到最舒适的状态,这不仅能让我们的心理状态更加健康,也能更好地平衡职业发展和生活的关系。
任务分配:兴趣驱动,效率优先
马克思指出社会分工是生产力发展的结果和需要,这种分工具有历史的必然性。对于创业公司而言,追求指数型增长是生存和发展的关键,因为即使是线性增长,在激烈的市场竞争中也可能面临被淘汰的风险。如何实现这种增长,是一个复杂且多维的问题,我在这里只从任务分配的角度分享一些个人理解。
在创业团队中,自上而下的任务繁多,而自下而上每个成员的兴趣和专长也各不相同。如何最大化团队的价值?关键在于沟通和了解每个成员的兴趣点和擅长点,尽可能让他们大部分时间都在做自己感兴趣和擅长的工作。虽然总有一些额外的任务需要团队共同承担,但是优先保证成员大部分时间能够从事自己感兴趣且擅长的工作是非常重要的。只有这样,每个人才会带着兴趣和专长去挖掘提升效率的可能,从而可能产生指数级的复利效应,并最终影响整个团队的产出。在现有的权力结构体系下,无论是企业还是更广泛的社会,我觉得自上而下的人员任命也基本遵循这一原则。
基于这样的理解,我在分配我们组的任务时,尽可能根据我对团队成员的了解,分配给每个人感兴趣和擅长的任务,并与大家一起探索提升效率和价值的途径。这一年里,我一直在寻求任务分配的全局最优解,并坚信找到合适的人做他们感兴趣的工作,能够产生的复利远远超过随机或平均分配工作所能带来的效益。
个人发展:更广还是更深?
在创业团队的初期阶段,各方面的需求和缺口(技术,市场,运行,销售等等)很多。从公司的角度看,这就非常需要大家能够主动承担额外的职责。从个人的角度看,我们不论是承担更多的职责还是在自己所做的工作上做得更突出,都是对公司的贡献,也都能收获成长。然而人的精力总是有限的,一个人不可能完美地做完所有事情,总是要把有限的精力投入到有限的事情上。面对这样的环境,每个人都面临着如何在工作的广度和深度之间做出选择的问题。
对于这个问题,我今年有了一番思考和探索。个人觉得对于职场新人来说,寻找一个自己擅长且能从中获得乐趣和成就感的领域至关重要,并且需要与领导进行积极的沟通,以获得相应的支持和资源。每个人的选择可能不同,领导的任务就是在团队成员之间找到一个平衡点,不仅能够完成所有任务,还要尽量让每个人能在其擅长的领域内发挥最大的复利效应。
就我个人而言,我目前更倾向于追求工作的深度,希望能够深入学习并掌握我目前尚不擅长但团队需要的技术知识。通过专注于深度,我希望能够在专业领域内取得更大的进步,并为团队带来更具影响力的贡献。当然,这也并不意味着就完全抛弃广度,随着时间不断推移,我在广度上投入的精力也会越来越多。
协作理念:以人为本,真诚坦率
今年,通过阅读《跳出盒子——领导与自欺的管理寓言》和李玉琢老师的《办中国最出色企业:我的职业经理人生涯》,我对管理有了初步的理解和感悟。这两本书分别代表了不同的管理理念,一种强调以人为本,另一种则是以结果为导向的雷厉风行。对于我目前的心态而言,我认同后者的评价体系,但从个人性格上我自己的风格更像前者。
在日常的产品迭代和团队管理中,我始终认为把人放在第一位是非常重要的。通过团结所有可以团结的力量,关注每个成员的工作态度、能力、心理状态以及需求和期望,找到大家适合的方向,往往能比反复推动大家完成不情愿的工作更加高效。
当然,在工作过程中难免会遇到与某些人的争执和冲突。面对这些情况,我常采取的做法是换位思考。我会设身处地地想,如果我是对方,我是否也会做出同样的选择?如果答案是肯定的,那么这往往是角色之间的冲突,而非个人情感的问题,我就不会在情感层面上过多消耗精力。如果答案是否定的,我则会进一步探索解决分歧的方法。我是一个性格相对温和的人,我通常不倾向于与人争执,而是尽可能地通过和平的方式解决问题。今年,我几乎都是这样处理冲突的。
然而,我也逐渐意识到,过分的忍让并不会赢得他人的尊重和理解,反而会被得过且过。有些原则和理念是需要坚持的底线,绝不能妥协。希望在未来的一年里,我能够在保持真诚坦率的同时,也能够坚持自己的原则和底线。
人生成就:小赢靠智,大赢靠势
今年在工作之余也读了《新程序员》杂志,深入了解了很多大佬的成长经历,也获得了不少启发。一个很深刻的感悟还是江同志的一句话:一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的进程。
自从 ChatGPT 爆火以来,周围已经涌现出许多彻底成功的案例,这些故事不仅激励着我,也让我对未来充满了好奇和期待。尽管对于自己未来的方向,我目前还没有一个清晰的规划,甚至只能对未来 1 到 2 年内的工作做出一些预测,3 年后会做什么我还没有确切的答案。
但在这样的不确定性中,我坚信的一点是,只要相信自己当前的工作富有意义和前景,并且能够在其中找到快乐,那么就值得坚持下去,全力以赴。关于未来命运将我们带往何方,或许可以交给时间和命运去安排。在这个快速变化的时代,保持学习和成长的心态,积极面对每一次机遇和挑战,可能就是我们能做的最好的准备了。
来年展望
经过一天多的思考,我终于完成了今年的年终总结。回顾这一年,我在技术和管理方面取得了一些进步,但同时也深刻意识到,在让企业成功的方方面面,我还有太多不了解不擅长需要学习的地方。
展望新的一年,我为自己和我们组设定了以下几点期望:
- 做深:希望能够系统地学习体系结构、操作系统以及《性能之巅》中的相关知识,并将这些知识应用到实践中,不断提升 IoTDB 的技术水平和性能表现。
- 做广:除了在分布式和可观测性方面的投入之外,希望能深入学习时序数据存储引擎和流处理引擎的知识,向优秀的同事和业界前辈学习。
- 做好:持续努力提高 IoTDB 的稳定性、鲁棒性和易用性,确保它成为用户信赖的时序数据库。
- 做响:寻找机会将我们团队的工作成果和经验分享给外部,与更多的同行进行技术交流,不断增强 IoTDB 的知名度和技术影响力。
最后,感谢您的阅读。欢迎各位读者批评指正。
在新的一年里,祝愿大家身体健康、家庭幸福、梦想成真。希望我们都能在新的一年中取得更大的进步!