存储空间降为 MySQL 的十分之一,TDengine 在货拉拉数据库监控场景的应用
作者:马祥荣
小 T 导读: 作为一家业务遍及多个国家及地区的物流公司,货拉拉面临的技术环境是非常复杂的,在云时代浪潮下基于混合云快速构建环境搭建系统成为其必然的选择。但是在混合云上如何对基于各家云构建的系统进行有效的管理是个挑战,尤其对处于系统最底层的数据库层面临的问题、业务诉求、各方痛点,是货拉拉 DBA 团队现下所需要重点解决的。
目前 DBA 团队管理的数据存储包括 MySQL、Redis、Elasticsearch、Kafka、MQ、Canal 等,为了保证监控采样的实时性,我们自研的监控系统设置的采样间隔为 10 秒,这样每天都会产生庞大的监控数据,监控指标的数据量达到 20 亿+。前期由于管理实例少,监控数据量也少,所有数据都被存放到了 MySQL 中;之后随着管理实例越来越多,使用 MySQL 来存储规模日益庞大的监控数据越发力不从心,急需进行升级改造。结合实际具体需求,通过对不同时序数据库进行调研,最终我们选择了 TDengine,顺利完成了数据存储监控的升级改造。
一、监控系统开发中遇到的问题
从存储路径来看,每种数据存储都分布在多个区域,每个区域将监控采样数据投递上报到消息总线,然后由消费程序统⼀消费,消费程序会将必要的告警数据更新到告警表,同时将监控原始数据存储到 MySQL。比如,针对 Redis 这款数据存储,监控采样间隔设置 10 秒,那么每天实例采样次数就会达到 3000 万+,监控指标累计 6 亿+,数据量之庞大可见一斑。早期为了将数据存储到 MySQL 中,同时也能很好地支持监控绘图的使用,每⼀次实例监控采样时都会计算出该实例全量监控项采样数据,然后将本次采样的结果作为⼀条记录存储下来。
即使通过这样的优化处理,在做时间跨度大的监控绘图时,前端依然会出现延迟卡顿的问题,后端监控数据存储的 MySQL 也压力山大,经常满载,天天被吐槽。因此针对这种时间跨度大的查询,我们专门开发了⼀系列的数据聚合调度任务——按照不同的时间跨度,提前将 10 秒采集间隔的监控数据做好聚合,监控绘图程序再根据不同的时间跨度选取不同的聚合数据表绘图,以此解决长时间跨度监控绘图展示延迟卡顿的问题。
但这仍然治标不治本。为了压缩监控数据存储空间,原始 10 秒间隔的监控数据表只能归档保留 3 天时间的监控数据,但存储大小也将近有 200GB, 加上与之相关的不同时间段的数据聚合表,存储一下子突破 300GB。这还只是 Redis 监控数据的存储大小,加上其它数据存储的监控数据,至少需要 1TB+空间的 MySQL 存储。
数据集合任务管理复杂、后期监控原数据回溯缺失、MySQL 存储空间日益增长带来的隐患,都是亟待解决的问题。
二、时序数据库选型
为了解决 MySQL 监控数据存储的问题,我们把注意力转移到了适合存储监控数据的时序数据库上。市面上各种时序数据库产品琳琅满目,有老牌的,也有后起之秀,经过⼀系列调研选型,我们选择了 TDengine,主要是因为其具备的如下几大优点:
- 采用分布式架构,可支持线性扩展
- 数据存储压缩率超高
- 集群模式支持多副本,无单点故障
- 单机模式性能强悍
- 安装部署维护简单
- SQL⽀持时间维度聚合查询
- 客户端支持 RESTful API
值得一提的是,TDengine 的 SQL 原生语法支持时间维度聚合查询,同时数据存储压缩率⾼存储空间小,这两点直接切中了我们的痛点。落地后实测相同数据的存储空间只有 MySQL 存储空间的十分之⼀甚至更少。 还有⼀个惊喜是,在现有监控数据存储(MySQL)顶不住的情况下,⼀台 8C16GB 的单机版 TDengine 轻松就抗下目前所有监控流量和存储压力, 且运行稳定,基本没有故障。
三、改造过程
TDengine 用于存储监控数据的超级表设计原则就是简单高效,字段⼀般都比较少,每⼀种监控项类型(INT、FLOAT、NCHAR 等)的数据存储都需要单独建立⼀个超级表,超级表⼀般都有关键的 ts、type、value 字段,具体监控项由 type 字段标识,加上必要的 tag 及少量其它字段构成。
之前监控数据存储在 MySQL 的时候,每条数据记录包含了该实例⼀次监控采样的所有监控项(25+)数据。如果采用通用的监控超级表设计原则,就需要改造采样的数据结构,改造方式有两种:
- 改造监控数据投递的数据结构
- 改造消费程序消费逻辑重组数据结构
但消费端有时效性要求,改造难度大,生产端涉及范围大阻力也不小。经过综合考量,最后我们决定采用类似 MySQL 数据存储的表结构来设计超级表,这样的改造对原来系统入侵最少,改造难度系数最低, 改造大致过程如下:
- TDengine,每种数据存储的监控数据单独建库存放
taos> create database redis;
taos> create database es;
... ... ...
- TDengine,建超级表,以 Redis 为例
taos> use redis;
taos> create table redis_node_meters (created_at TIMESTAMP, qps INT, . ..) TAGS (region NCHAR(10), cluster_name NCHAR(50), ip NCHAR(20), port INT, role NCHAR(15));
- 监控数据消费,由于新增的实例节点是不确定的,比如 Redis 的节点资源是由 Agent 自动发现注册后自动进行监控指标采集,这时写入数据并不确定某个子表是否存在,就需要 TDengine 的自动建表语法来创建不存在的子表,若该子表已存在则不会建立新表只会写入数据。
taos > INSERT INTO tablename USING redis_node_meters TAGS ('China', 't est', '127.0.0.3', 9200, 'master') VALUES ('2021-12-02 14:21:14', 6490 , ...)
- 绘图展示,绘图程序没有安装客户端驱动,直接使用了 TDengine 提供的 RESTful API,采用 HTTP 的方式进行数据查询。在 SQL 查询上大量使用了 TDengine 提供的时间维度聚合函数 INTERVAL,长时间跨度的数据查询,只需要合理选择聚合间隔,基本都是毫秒级响应, 保障了前端绘图的流畅稳定。
四、改造后的落地效果
将监控的数据存储由 MySQL 改造为 TDengine 后,不仅顶住了监控数据增长所带来的压力,还节约了存储空间,成本压缩到了原来的十分之⼀甚至更低。历史原生监控数据可回溯时间也变得更长,之前存储 3 天原生数据及聚合数据的空间,现在可供原始数据存储 45 天。
此外,改造之后不再需要维护复杂的数据聚合调度任务,大幅降低了监控系统、监控数据管理复杂度,同时前端绘图数据查询也变得更加简洁高效。
五、写在最后
随着对 TDengine 越发深入的了解及经验累积,后续我们也会逐步考虑将货拉拉大监控系统、业务数据(行车轨迹)等时序数据均迁移至 TDengine。为了方便有需要的朋友更好地使用 TDengine,在此也分享一下我们的一些使用经验:
- 由于 TDengine 不支持太复杂的 SQL 查询语法,在设计超级表 tag 的时候需要充分考虑清楚,目前只有 tag 的字段支持函数运算结果的分组(GROUP BY)查询,普通字段是不支持的。
- 子表 tag 的值是可以改变的,但是同⼀个子表 tag 的值是唯⼀的,建议用子表 tag 的组合值来生成子表名。
在本次项目中,TDengine 很好地帮助我们实现了降本增效,是一款值得尝试的时序数据库产品,未来也希望其能够发挥出越来越丰富的功能和特性,也期待我们之后能有更紧密深入的合作。