赵运周 / 转转
在转转的业务中,我们使用了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自带的函数DIFF。官方从2.1.3.0 版本开始,DIFF 函数可以由GROUP BY 划分出单独时间线的情况下用于超级表(也即 GROUP BY TBNAME)。而且TDengine的超级表极大程度上简化了查询代码,其分片特性也保证了同时查询多个域名能够做到充分地多核并发。
- 容量规划
在落地过程中,我们发现数据类型、数据规模对TDengine的性能影响比较大,最好根据每个场景的特性进行容量规划,影响因素包括:
- 表数量
- 数据长度
- 副本数
- 表活跃度等
从这些因素出发调整配置参数能够确保最佳性能,在与涛思数据工程师沟通后,我们确定了现在的容量规划计算模型。值得注意的是,TDengine容量规划的难点在于内存的规划,需要在内存的使用和读写性能之间进行平衡。 连接TDengine后,我们目前系统的拓扑结构如下:
使用TDengine完成改造后,线上的监控状态达到预期,满足当前业务需求,目前运行非常稳定。且配合Grafana后,每个域名的流量、连接数、响应时间等信息都能够实时监控到。
写在最后
总而言之,无论是在成本和性能层面,还是在使用的便利性方面,TDengine Database都具有非常大的优势,在我们的实践中也得到了证明,尤其是成本管控上效果非常显著。同时,也非常感谢涛思数据的小伙伴们提供的专业、及时的帮助,我们也希望未来TDengine Database能够开拓出更多更加优秀的新特性。当然,作为TDengine的使用者,我们也会在GitHub上为TDengine做代码贡献。
此外,从自身项目和实践出发,我们也有一些针对于TDengine Database期盼改进的功能点:
- 对表名支持更友好:能够减少对特殊字符的屏蔽(据说在后续版本中会实现,这样就更贴近场景,省去了应用端的特殊处理)
- 支持更加丰富的SQL语句:能够针对少有的场景,提供更加灵活的SQL语句,便于做更加复杂的计算分析,这也是AIOps的进阶部分
- 灰度平滑升级:目前TDengine保持着2周一次的发版节奏,还是期望能够快速用上新的特性。但是每次停机升级又会是一个麻烦的事情,期待官方早日支持滚动升级
- 可实现自定义聚合方法:由于时间问题,没赶上官方的UDF特性。期望官方的UDF能够早日发布,好实现更加复杂的聚合计算
- 子表自动清理功能:由于域名会存在下线问题,目前的TTL策略只是针对数据而不是Table本身,淘汰子表还需要人工运维介入
尽管还存在不足,但作为首次尝试,TDengine的表现可以说是相当不错了,我们也很期待未来能够在更多场景中和TDengine展开合作,包括更多监控项以及业务时序数据库需求的接入尝试。
最后,衷心祝愿TDengine越来越好!