云原生数据库 (Cloud Native Database) 是指充分利用了云计算平台以及分布式系统的优势而设计的数据库。云原生数据库提供了按照实际使用资源量来计费的能力,以降低运营成本。此外,它提供了快速开发原型、研发、测试以及部署新的应用的能力,可以大大缩短新的应用从设计开发到进入市场的时间。
作为一个云原生,而不只是能在云上运行的时序数据库(Time Series Database),TDengine 具备云原生数据库的几大特点:水平扩展性(Scalability)、弹性(Elasticity)、韧性(Resiliency)、可观测性(Observability)以及运维自动化(Automation)。
在我们讨论这些特性之前,可以先简单了解一下 TDengine 的分布式设计。
分布式设计
在 3.0 版本之后,TDengine 具备完全的分布式架构,提供高可用的水平扩展能力。如果需要获得更高的处理能力,只需要多增加节点即可。
一个完整的 TDengine 系统运行在一到多个物理节点(一台服务器、虚拟机或容器)上的。逻辑上,它包含数据节点(dnode)、TDengine 应用驱动(taosc)以及应用(app)。系统中存在一到多个数据节点,这些数据节点组成一个集群(cluster)。应用通过 taosc 的 API 与 TDengine 集群进行互动。下面对每个逻辑单元进行简要介绍。
数据节点(dnode): dnode 是 TDengine 服务器侧执行代码 taosd 在物理节点上的一个运行实例,一个工作的系统必须有至少一个数据节点。dnode 包含零到多个逻辑的虚拟节点(vnode),零或者至多一个逻辑的计算节点(qnode),零或者至多一个逻辑的管理节点(mnode)。
虚拟节点(vnode): 为更好地支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode,图中 V2,V3,V4 等)。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)的数据,包括时序数据和元数据。一个 vnode 就是一个片 (shard)。
计算节点(qnode):计算节点仅仅负责计算。当一个查询执行时,依赖执行计划,调度器会安排一个或多个 qnode 来一起执行。qnode 能从 vnode 获取数据,也可以将自己的计算结果发给其他 qnode 做进一步的处理。一个集群里允许有多个 qnode,它的启动和停止完全由 mnode 根据系统资源和性能要求来决定。通过引入独立的计算节点,TDengine 实现了存储和计算分离。
管理节点(mnode): 一个虚拟的逻辑单元,负责所有数据节点运行状态的监控和维护,以及节点之间的负载均衡(图中 M)。同时,管理节点也负责元数据(包含用户、数据库,但不包含表、静态标签等)的存储和管理,因此也称为 Meta Node。一个集群里只有一个管理节点,但为提高管理节点的高可用性,允许有三个副本。
taosc:taosc 是 TDengine 给应用提供的驱动程序(driver),负责处理应用与集群的接口交互,提供 C/C++ 语言原生接口,内嵌于 JDBC、C#、Python、Go、Node.js 语言连接库里。应用都是通过 taosc 而不是直接连接集群中的数据节点来与整个集群交互的。
水平扩展性(Scalability)
TDengine 是通过数据采集点以及时间两个维度,对大数据进行切分,实现水平扩展的,它既支持分片,也支持分区。
分片:在 TDengine 的设计与实现里,一个集群有多个数据节点,每个数据节点可以有一个或多个虚拟节点(vnode),每个虚拟节点里存储了一定数量的数据采集点的数据,而一个数据采集点的时序数据和元数据永远只存放在一个 vnode 里。这样如果有很多数据采集点,通过一致性 Hash,这些数据采集点的数据将会分布在多个 vnode 上,分布在多个节点里。
分区:除将数据分片之外,TDengine 还将一个 vnode 里存储的时序数据按照时间段进行切分。每个时间段的数据都一定保存在一起,不同时间段的数据不会有交集,时间段可以是一天,几天,一周,由用户自己定义。按照时间段切分时序数据有很多好处,查询数据时,根据时间段,可以直接定位要查找的文件,从而加快查询速度。另外一方面,可以高效地实现数据保留策略。超过最长保留时间的数据,直接删除一个时间段对应的文件即可。而且按照时间段切分数据,还可以方便实现多级存储,冷热数据放在不同存储介质上,进一步降低存储成本。
高基数:在 TDengine 的设计中,每个数据采集点的元数据没有存放在中心点,而是分布在各个 vnode 里。当应用要将数据插入到一张表或对一张表做查询操作时,基于表名的 hash 值,插入或查询请求将被直接发送到对应的 vnode,由于没有中心点,不会有任何瓶颈。对于多个点的聚合计算,查询的请求将被同时发往相关的 vnode,每个 vnode 都会执行对应的聚合操作,然后将结果返回给 taosc 或 qnode,做进一步的聚合。
TDengine 具有超强的水平扩展能力,为获得更多的数据处理能力,只需要加入更多的数据节点即可。通过测试,我们可以验证,在 10 亿时间线,100 个数据节点的情况下,整个 TDengine 性能还能得到很好的保证。时序数据处理里的“高基数”问题完全得到了解决。
弹性(Elasticity)
在 IoT 或 IIoT 情况下,系统需要快速将最新数据返回给应用程序。例如,车队管理系统总是想知道每辆卡车的当前 GPS 位作为一个云原生数据库,TDengine 不仅提供 scale up 的能力,也提供 scale down 的能力,从而让系统具有弹性。
为支持存储的弹性,如果插入的延时已经超过一定阈值或者性能不够,TDengine 会将一个 vnode 拆分成两个,从而分配更多的系统资源给数据写入操作。另一方面,在能够保证延时与性能的情况下,TDengine 也可以把多个 vnode 合并成一个,以节省系统资源。
为支持计算的弹性,TDengine 引入了计算节点 qnode。对于简单的查询,比如获得某张表的原始数据或卷曲数据(rollup data),对应的 vnode 将完成所有的操作,无需 qnode 的参与。但对于一个需要排序、分组或其他需要计算资源的操作,查询的执行过程中,一个或多个 qnode 将被调用。在具体的部署中,qnode 可以运行在容器里,它的启停完全由 mnode 根据系统负载情况决定。
通过引入 qnode,TDengine 成为一个理想的时序数据分析平台,包括实时数据分析和批分析。在云计算的环境里,计算资源是近似无限的,但又可以弹性的伸缩。
韧性(Resilience)
TDengine 的韧性是通过其高可靠与高可用设计来实现的。
对于数据库,存储的高可靠是最高优先级的。TDengine 采用 Database 实现中传统的方法 WAL(Write Ahead Log) 来保证即使系统宕机,数据仍然可以恢复而不会丢失,从而实现数据的高可靠。在设计中,新写入的数据总是先写入 WAL,然后写入内存,转发给其他节点,再给应用发回确认的。写入流程如下图所示:
TDengine 通过多副本以及 RAFT 一致性协议,保证 vnode 和 mnode 的高可用性。对于元数据,TDengine 采取的是强一致性,而对于时序数据,TDengine 采取的是弱一致性,这样保证时序数据的写入效率。
在不同数据节点上的 vnode 可以形成一个虚拟节点组。虚拟节点组里,数据是通过 RAFT 协议来保证数据一致的,一般虚拟节点组有 3 个虚拟节点。数据写入操作总是在 Leader 节点上进行,但是查询可以在 Leader 和 Follower 节点上同时进行,以提升查询能力。当 Leader 节点失败,系统将自动选择新的 Leader,只要虚拟节点组里超过半数以上的节点仍然工作,那么这个虚拟节点组就可以继续提供数据写入和查询操作,从而保证系统的高可用。
对于 mnode,为保证其高可用,整个集群会部署三个 mnode,这三个 mnode 形成一个虚拟节点组,它们之间的数据一致性通过 RAFT 来实现,而且是强一致。只要超过半数的 mnode 工作,mnode 就可对外服务。
可观测性(Observability)
TDengine 采集了各种指标来监测系统是否运行正常,这些指标包括 CPU、内存、磁盘、流量、请求次数、延时等。它提供了 Grafana 的看板 TDinsight,以实现这些指标的可视化与报警。关于更多 TDinsight 的信息,请看文档。
TDengine 还提供了一个模块 taosKeeper,它能够将采集的指标发送到其他监测工具,比如 Prometheus,这样便于将 TDengine 的监测集成到已有的可观测系统。
运维自动化(Automation)
TDengine 可以用二进制包或 Docker 镜像进行安装。TDengine 集群可以在 Kubernetes 环境通过 kubectl 命令或 helm chart 来部署。数据节点的增加或删除可以通过执行 kubectl 或 heml 命令进行。TDengine 集群的管理完全可以通过脚本自动化进行,让运营和维护变得简单。关于 Kubernetes 部署,请参考相关文档。
总结
通过分布式设计、分区分片、存储和计算分离,RAFT 一致性协议等手段,TDengine 这个时序数据处理平台具备水平扩展性、弹性与韧性。通过支持容器、Kubernetes 部署、全面的指标监测和自动化脚本,TDengine 可以方便地运行在私有云、公有云或混合云上,从而充分利用云平台的优势。因此 TDengine 是一个云原生时序数据库(Cloud Native Time-Series Database),而不只是一个能在云上运行的数据库。