从落地效果看,转转选择 TDengine 的三个理由

赵运周 / 转转

在转转的业务中,我们使用了Nginx作为我们的反向代理,为保证代理层可用性,需要对Nginx进行实时状态监控。在服务器的基础监控的选择上,我们将OpenFalcon逐步替换为夜莺,对Nginx 的reqstat监控最初也使用了这两种。但是这两大监控都有一个共同缺点,即在展示时有条数限制,导致域名数量和机器数量相乘后数据量增多的情况下,无法满足需求。

为了解决这个问题,我们考虑对现有监控模块进行升级改造,重新进行数据库选型,在预研和分析阶段,根据当前的业务需求我们从开源的数据库中选择了两款时序数据库(Time-Series Database),分别是InfluxDB和TDengine,这两款都可以实现高性能地查询与存储时序性数据,但TDengine相比于InfluxDB还存在三点优势:

  • 集群功能已经开源,支持横向扩展以及高可用
  • 性能和成本之间的平衡达到最优化,运维难度显著降低
  • 其超级表特别适合我们这个以域名为维度的监控方案

通过综合对比,我们初步选定TDengine作为监控模块的数据库。此外,涛思官方的测试结果显示,TDengine的写入速度高于InfluxDB,这一点也更加坚定了我们选择TDengine的决定。

而且TDengine支持多种数据接口,包含C/C++、Java、Python、Go和RESTful等。转转之前的服务用的是Python,所以这也方便我们依旧延续使用Python connector。

使用TDengine进行数据库建模

作为一款结构化的时序数据库,为了能够达到最优的性能表现,TDengine在接入数据前需要根据数据的特性设计schema。 Nginx监控功能数据特性如下:

  • 数据格式固定:配置好req_status_zone之后,日志文件就固定住了,但总的字段数是有限的,样例如下:
zone_name   key max_active  max_bw  traffic requests    active  bandwidth
server_addr 192.168.187.164 2    432    17K 18  1    0
server_name 192.168.187.164 2    432    17K 18  1    0
server_url  192.168.187.164/    1    0   0  8   0    0
server_url  192.168.187.164/index.html  1    0  11K 8   0    0
server_url  192.168.187.164/req-status  1    0   0  1   1    0
server_url  192.168.187.164/req_status  1    0   5680   1   0    0
  • 数据极少需要更新或删除
    • 属于服务访问的事实数据,只要不是脏数据,就不会删除
  • 需要采集的数据标签不多,而且固定
  • 单条数据量约在1KB
  • 保存6个月以上

此外,TDengine的文档显示:

TDengine 对每个数据采集点单独建表,但在实际应用中经常需要对不同的采集点数据进行聚合。为高效的进行聚合操作,TDengine 引入超级表(STable)的概念。超级表用来代表一特定类型的数据采集点,它是包含多张表的表集合,集合里每张表的模式(schema)完全一致,但每张表都带有自己的静态标签,标签可以有多个,可以随时增加、删除和修改。

按照其建议的数据模型,我们需要建立一个超级表。结合我们的数据特点和使用场景,创建数据模型如下:

  • 超级表:以指标作为超级表
  • 子表:每个域名做一个子表
    • 标签tag:直接将标签信息作为超级表的标签列
  • 列column:监控数据本身除去标签部分

具体示例如下:

TDengine Database

落地实施和最终效果展示

因为是融合一个全新的数据库,在真正的落地实施时不可避免会遇到一些问题点,以下三点是我们汇总的实施经验,放在本篇文章中给大家做参考:

  • 数据写入

在数据写入的阶段,我们开始设计的是一个域名一个子表,但是直接使用域名做表名不符合保留字符的规范,所以需要将域名转换一下。

  • 查询问题

由于写入的数据是实时的值,而监控业务更多地是需要获取前后差值,因此需要用上TDengine自带的函数DIFF。官方从2.1.3.0 版本开始,DIFF 函数可以由GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY TBNAME)。而且TDengine的超级表极大程度上简化了查询代码,其分片特性也保证了同时查询多个域名能够做到充分地多核并发。

  • 容量规划

在落地过程中,我们发现数据类型、数据规模对TDengine的性能影响比较大,最好根据每个场景的特性进行容量规划,影响因素包括:

  1. 表数量
  2. 数据长度
  3. 副本数
  4. 表活跃度等

从这些因素出发调整配置参数能够确保最佳性能,在与涛思数据工程师沟通后,我们确定了现在的容量规划计算模型。值得注意的是,TDengine容量规划的难点在于内存的规划,需要在内存的使用和读写性能之间进行平衡。 连接TDengine后,我们目前系统的拓扑结构如下:

TDengine Database

使用TDengine完成改造后,线上的监控状态达到预期,满足当前业务需求,目前运行非常稳定。且配合Grafana后,每个域名的流量、连接数、响应时间等信息都能够实时监控到。

TDengine Database

写在最后

总而言之,无论是在成本和性能层面,还是在使用的便利性方面,TDengine Database都具有非常大的优势,在我们的实践中也得到了证明,尤其是成本管控上效果非常显著。同时,也非常感谢涛思数据的小伙伴们提供的专业、及时的帮助,我们也希望未来TDengine Database能够开拓出更多更加优秀的新特性。当然,作为TDengine的使用者,我们也会在GitHub上为TDengine做代码贡献。

此外,从自身项目和实践出发,我们也有一些针对于TDengine Database期盼改进的功能点:

  • 对表名支持更友好:能够减少对特殊字符的屏蔽(据说在后续版本中会实现,这样就更贴近场景,省去了应用端的特殊处理)
  • 支持更加丰富的SQL语句:能够针对少有的场景,提供更加灵活的SQL语句,便于做更加复杂的计算分析,这也是AIOps的进阶部分
  • 灰度平滑升级:目前TDengine保持着2周一次的发版节奏,还是期望能够快速用上新的特性。但是每次停机升级又会是一个麻烦的事情,期待官方早日支持滚动升级
  • 可实现自定义聚合方法:由于时间问题,没赶上官方的UDF特性。期望官方的UDF能够早日发布,好实现更加复杂的聚合计算
  • 子表自动清理功能:由于域名会存在下线问题,目前的TTL策略只是针对数据而不是Table本身,淘汰子表还需要人工运维介入

尽管还存在不足,但作为首次尝试,TDengine的表现可以说是相当不错了,我们也很期待未来能够在更多场景中和TDengine展开合作,包括更多监控项以及业务时序数据库需求的接入尝试。

最后,衷心祝愿TDengine越来越好!