0

TDengine在蔚来能源系统的落地实践 - InfoQ 写作平台

 2 years ago
source link: https://xie.infoq.cn/article/56f1ba79ff707da5dc03a73c0
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、项目背景

为了给用户提供更好的补能体验,蔚来能源在加电基础设施上进行了大量的投入,截止 2021 年底,已经在全国各地布局了换电站 777 座,超充桩 3404 根,目充桩 3461 根,为用户安装家充桩 96,000+根。 

为了对设备进行更高效的管理,需要将设备采集数据上报至云端进行存储,并提供实时数据查询、历史数据查询等业务服务,用来做设备监控和分析。

在业务诞生之初,我们用作数据存储的选型是 MySQL + HBase,MySQL 存储设备最新实时数据,HBase 存储设备原始数据,大体架构如下:

之所以选择 HBase,有以下几个理由:

  • HBase 在大数据领域应用较为广泛,适合存储海量数据,写入性能好

  • 支持动态添加列,非常方便兼容数据模型变化

  • 底层是键值对存储,数据可以比较稀疏,空数据不占存储空间

  • 团队 HBase 技术使用相对较为成熟

初期因为设备不多,数据量不大,加上查询场景单一,HBase 表现不错,可以满足业务需求。 

随着换电站和超充站等设备在全国的快速布局,设备数量持续增长,积累的数据量越来越多,长时间跨度数据查询效率出现瓶颈,再加上查询场景不断丰富,HBase 已经无法满足当前业务需要。问题主要体现在以下几点:

  • HBase 只支持 Rowkey 索引,有很大的局限性,一些查询场景依赖 Rowkey 设计合理,如果业务调整,无法兼容

  • 可以引入二级索引解决,单独维护查询条件与 Rowkey 关系,查询时先查到 Rowkey 再查数据,不管是引入中间件还是自己实现,都会增加整体架构和实现复杂度

  • HBase 单表随着数据量增大,会触发自动分区,导致写入性能下降,需要通过建表时指定预分区来解决,调整起来很麻烦,需要重新建表生效

  • HBase 不适用于大范围扫描查询,性能比较差

  • HBase 不支持聚合查询,大跨度时间范围查询数据量太大,图表无法渲染

  • HBase 部署需要依赖 ZooKeeper,运维成本高

二、落地方案

为了解决这些痛点,我们将目光投向时下流行并且更适合物联网领域的时序数据库。经过调研,对比多个技术选型,最终决定使用 TDengine 代替 HBase 作为设备原始数据存储。 

在选型时我们考虑过 OpenTSDB,也是一款优秀的时序数据库产品,在部门其他业务中已经有过比较成熟的使用,能解决一部分遇到的痛点:

  • OpenTSDB 在 HBase 基础上做了优化,包括存储元数据映射和压缩机制,使数据存储占用空间大大降低

  • OpenTSDB 提供数据聚合查询功能,可以支持更大时间跨度查询的业务需求

但是 OpenTSDB 底层还是基于 HBase 的,HBase 存在的一些问题,OpenTSDB 依然会有,并且架构并没有变简单,没有摆脱 HBase 的依赖。 

经过对比,我们决定尝试一下 TDengine,其官方给出的性能指标,单节点部署情况下可以达到 14810k/s 读取,和 880k/s 写入,同时 TDengine 具备的一些特点能很好地解决我们遇到的痛点:

  • 引入超级表概念对应设备类型,对每个设备创建子表继承超级表,通常相同设备类型的设备数据模型一定相同,通过超级表管理 schema 直接对子表生效很方便,同时对每个设备建表可以很好地做数据隔离,同时避免互相影响

  • 采用多级存储,不同时间的数据使用不同存储介质,新数据经常访问存 SSD 保证效率,老数据存 HDD,节约成本

  • 不依赖任何第三方软件,集群安装部署方便,支持灵活扩容

  • 提供多种聚合函数,支持对数据的聚合查询

我们使用 TDengine 做了一些简单的性能测试,评估使用 TDengine 是否能满足我们的业务需求。

  • 采用单节点部署

  • 8 核 32G,500G 存储

  • 采用默认配置

  • 采用 RESTful API 方式写入数据

模拟 10000 个设备上报数据,消息并发约 4k 左右。

  • 定义超级表如下

SQL -- 代码示例,非真实代码 CREATE STABLE device_data_point_0 (ts timestamp, d1 nchar(64), d2 nchar(64), ...) TAGS (t1 binary(64));
  • 最初采用每条上报消息进行一次数据写入,性能无法满足,而将单条消息写入改为批量写入,积累一批数据(100 条)后,再批量写入一次,性能可以支撑

采用批量写入数据方式,调整合适的单批次数据量大小,使用单机部署(8 核 32G,500G 存储)默认配置的 TDengine 服务,RESTful API 写入方式,在 4k 并发流量下写入没有问题,同时消费积压数据时峰值达到 7k/s,因为单条消息包含信息量太大,实际处理中会拆分为 30 条写入 TDengine,所以实际写入 QPS 为 210k/s,比满足同样数据流量的 HBase 集群规模要小不少,可以节省成本,再加上 TDengine 本身部署不依赖其他三方软件,也可以同时节省运维成本。

经过测试,我们决定先对部分设备应用 TDengine 时序数据库替代 HBase,同时需要考虑如何在不影响业务功能的情况下平滑过渡并完成迁移。

因为目前没有现成的工具可以直接把数据从 HBase 迁移到 TDengine,如果自己开发一个工具做这件事情,开发成本太高,而且可能是一次性的。 

考虑到不想浪费开发资源,同时我们需要一个过渡期,期间如果 TDengine 出现问题可以迅速切换回 HBase,不影响业务,所以不能马上把 HBase 废掉,所以我们决定先实现 TDengine 写入,并且暂时保持 HBase 和 TDengine 两个数据库双写。

根据前期测试结果,我们选择直接采用批量方式写入数据:

  • 并行处理不同设备类型数据

  • 消费设备上报数据放入队列

  • 当队列长度达到 n 或超过等待时间 t,从队列中取出数据批量写入

经过压测,在 n=1000,t=500ms 情况下,单次写入耗时基本在 10ms 以内,意味着我们可以支持单个设备类型每秒上万的并发写入,并且还有进一步的优化提升空间。

为了保证迁移过程顺利,并且迁移前后不会出现数据不完整的情况,我们做了一个查询开关:

  • 配置 TDengine 功能上线时间

  • 判断查询请求时间范围与配置时间大小,决定查 HBase 还是 TDengine

  • 过渡期结束后,停止 HBase 服务

迁移后架构变为如下所示:

三、实际效果

目前我们已将线上部分设备的数据切换到 TDengine 集群,上线后集群表现稳定。

对比之前使用 HBase:

  • 查询速度提升明显,从使用 HBase 查询单设备 24 小时数据的秒级返回,到使用 TDengine 查询查询相同数据的毫秒级返回

  • 每天增量数据占用的存储空间相当于原来使用 HBase 时的 50%

  • 集群计算资源成本相比使用 HBase 节省超过 60%

  • 总体上说,TDengine 读写性能表现很好,在满足我们业务需求的同时,极大地节省了计算资源和运维成本,目前尝试 TDengine 的业务场景还比较简单,只是单纯的数据写入和时间范围查询,后续可以结合 TDengine 更多进阶功能探索其他可以落地的业务场景

  • 使用上还有一些问题待解决,比如 schema 调整在应用发版过程中对数据写入的影响,产生预期外的写入异常,以及异常定义不明确,无法快速定位问题,尤其是跟 schema 相关数据写入问题

  • 监控方面目前支持的监控指标较少,这个问题据说会在后续版本丰富

  • 数据迁移方面,目前官方支持工具还比较少,不能比较方便的把数据从其他存储引擎迁移到 TDengine,需要进行额外开发

李鹏飞,蔚来汽车能源数字化产品开发部高级工程师,目前负责能源物联平台开发。


✨想了解更多 TDengine 的具体细节,欢迎大家在GitHub上查看相关源代码。✨


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK