Master Vnode遵循下面的写入流程:

    图 3 TDengine Master写入流程

    1. Master vnode收到应用的数据插入请求,验证OK,进入下一步;
    2. 如果有多个副本,vnode将把数据包转发给同一虚拟节点组内slave vnodes, 该转发包带有数据的版本号(version);
    3. 写入内存,并将记录加入到skip list;
    4. Master vnode返回确认信息给应用,表示写入成功。
    5. 如果第2,3,4步中任何一步失败,将直接返回错误给应用。

    对于slave vnode, 写入流程是:

    TDengine Slave写入流程

    1. Slave vnode收到Master vnode转发了的数据插入请求。
    2. 如果系统配置参数walLevel大于0,vnode将把该请求的原始数据包写入数据库日志文件WAL。如果walLevel设置为2,而且fsync设置为0,TDengine还将WAL数据立即落盘,以保证即使宕机,也能从数据库日志文件中恢复数据,避免数据的丢失;

    与Master vnode相比,slave vnode不存在转发环节,也不存在回复确认环节,少了两步。但写内存与WAL是完全一样的。

    从上述Master和Slave流程可以看出,TDengine采用的是异步复制的方式进行数据同步。这种方式能够大幅提高写入性能,网络延时对写入速度不会有大的影响。通过配置每个物理节点的IDC和机架号,可以保证对于一个虚拟节点组,虚拟节点由来自不同IDC、不同机架的物理节点组成,从而实现异地容灾。因此TDengine原生支持异地容灾,无需再使用其他工具。

    另外一方面,TDengine支持动态修改副本数,一旦副本数增加,新加入的虚拟节点将立即进入数据同步流程,同步结束后,新加入的虚拟节点即可提供服务。而在同步过程中,master以及其他已经同步的虚拟节点都可以对外提供服务。利用这一特性,TDengine可以实现无服务中断的IDC机房迁移。只需要将新IDC的物理节点加入现有集群,等数据同步完成后,再将老的IDC的物理节点从集群中剔除即可。

    但是,这种异步复制的方式,存在极小的时间窗口,丢失写入的数据。具体场景如下:

    1. Master vnode完成了它的5步操作,已经给APP确认写入成功,然后宕机;
    2. Slave vnode收到写入请求后,在第2步写入日志之前,处理失败
    3. Slave vnode将成为新的master, 从而丢失了一条记录

    理论上,只要是异步复制,就无法保证100%不丢失。但是这个窗口极小,mater与slave要同时发生故障,而且发生在刚给应用确认写入成功之后。

    Vnode会保持一个数据版本号(Version),对内存数据进行持久化存储时,对该版本号也进行持久化存储。每个数据更新操作,无论是采集的时序数据还是元数据,这个版本号将增一。

    一个vnode启动时,角色(master、slave) 是不定的,数据是处于未同步状态,它需要与虚拟节点组内其他节点建立TCP连接,并互相交换status,其中包括version和自己的角色。通过status的交换,系统进入选主流程,规则如下:

    1. 如果只有一个副本,该副本永远就是master
    2. 所有副本都在线时,版本最高的被选为master
    3. 在线的虚拟节点数过半,而且有虚拟节点是slave的话,该虚拟节点自动成为master

    更多的关于数据复制的流程,请见。

    对于数据一致性要求更高的场景,异步数据复制无法满足要求,因为有极小的概率丢失数据,因此TDengine提供同步复制的机制供用户选择。在创建数据库时,除指定副本数replica之外,用户还需要指定新的参数quorum。如果quorum大于一,它表示每次Master转发给副本时,需要等待quorum-1个回复确认,才能通知应用,数据在slave已经写入成功。如果在一定的时间内,得不到quorum-1个回复确认,master vnode将返回错误给应用。

    采用同步复制,系统的性能会有所下降,而且latency会增加。因为元数据要强一致,mnode之间的数据同步缺省就是采用的同步复制。