今天这篇文章接续上篇文章,主要介绍分布式时序数据库读写流程及相关模块。
如果说分布式时序数据库SilverDB-技术架构1,搭建好了一个分布式集群的环境,有了一个基本的“骨架”。那么,这里的读写流程、存储模块则是SilverDB的“血与肉”。
一、
01、SilverDB写数据流程
首先来看一下,在这个P2P的对称网络分布式架构下的写流程:
对于基于P2P对称网络模型的分布式时序数据库SilverDB来说,这里提供了client封装读写操作。具体流程说明如下:
1、客户端会负责封装用户写数据请求,包括database、table、tags set、metric、timestamp,这里如果不熟悉时序数据库存储模型的,可以参考我的另外一篇介绍时序数据库-存储初始的文章,也是在以上专栏中。在这里基于TCP使用了自定义的写请求协议格式,并且基于protobuf对写数据请求进行了序列化。
2、客户端通过请求负载均衡机制,会将写请求发送给SilverDB集群中的任意节点,此处如上图所示,客户端将写请求发送到节点Node1。
3、Node1接受客户端写请求,交给Handler写请求处理器进行处理,在这个处理器内部会进行反序列化操作,根据解析出来的database、table、tag set 生成seriesKey,这里主要是为了基于Hash一致性算法,进行数据的Hash分片,避免造成数据写热点问题。
4、Node1根据Hash一致性算法计算seriesKey,获取该数据分片的节点List。如果该数据分片地址与Node1节点地址相同,则将数据写入本地节点。如果,该数据分片地址与Node1节点地址不同,则会初始化一个代理客户端,将数据分片写到代理节点。
5、如上图所示,Node2节点接受到代理写请求,同样进行写请求的处理及解析,然后直接写到本地数据节点,写成功的之后,这里会根据Node2本地元数据信息,将database、table等元数据信息注册到SilverDB的元数据服务。同时,会返回写成功或者失败的响应信息给代理客户端。
6、代理客户端接受到写响应之后,再将响应信息透传给节点的处理写请求响应的协程进行处理。注意:因为SilverDB是基于Golang开发,所以这里是协程的概念。通过异步通信的模式,将写请求的响应交给该节点处理写请求响应的协程。最终,返回给客户端。
至此,分布式时序数据库SilverDB的写流程完成,在这里主要介绍整体分布式写流程,并没有涉及到数据在每个节点的写操作流程。但是,这部分内容会随着不断的该分布式时序数据库SilverDB技术专题的不断深入,会一步步向大家介绍。
02、SilverDB读数据流程
接下来,就具体介绍一下SilverDB的读数据流程。整个读数据流程其实是要比写流程要稍微复杂一点,具体如下图所示:
1、与写请求类似,客户端会封装用户的读请求数据,包括database、table、tags组合、metirc组合、时间范围这些比如的参数信息。同时也会初始化一个空的metric value map,用来存放符合条件的、查询出来的时序数据。
2、如上图所示,Node1接受到客户端的读数据请求,然后交给Handle read请求处理器,负责进行读请求的处理和解析,根据解析出来的database、table信息,去访问Node1的内存元数据信息。
3、如果待查询的数据在本地节点,则根据解析出来的查询条件查询Node1本地数据索引,根据索引信息,我们可以得到满足查询条件的时序数据key,进而可以找到满足固定时间范围的时序数据。
4、如果待查询的数据不在本地节点,则会初始化配置代理客户端,代理客户端根据查询的元数据信息,将读请求发送到对应数据节点上进行处理。如上图所示,这里是Node2数据节点。
5、Node2节点接受代理客户端的读数据请求,交给Handle read请求处理器,查找本地索引,根据查询的索引数据找到满足查询条件的时序数据Key,然后根据查询条件(时间范围),在本地进行数据查询。
6、读代理客户端将符合条件的数据返回给节点Node1,Node1负责将查询结构进行merge合并,最终返回给读客户端。
7、这里的查询响应,也是通过一个异步的响应处理协程,进行处理。这样可以增大每个SilverDB处理读请求和响应的吞吐,提升SilverDB的整体性能。
注意:此处客户端写请求和读请求是类似,都是通过负载均衡机制,将请求发送给任意节点,因为我们的silverDB是P2P对称网络的分布式架构。
总结
分布式时序数据库SilverDB读写流程的设计思路就是这么多,更多细节的东西还需要具体的模块设计层面来介绍。
二、
01、数据写操作
现在对于整个SilverDB的读写流程应该有个逻辑上的认知了。那么具体来看看,对于SilverDB的每个数据节点的每个模块,是如何进行本地数据读写的呢?同样,我们还是用图来说明一下。
如上图所示,这里面涉及到的Wal模块、Buffer模块、Index模块、元数据模块、数据存储模块在后面的专题文章中,都会一一介绍,这里主要看一下在数据进行具体写操作的时候,都做了那些事?
1、当本地节点Node1进行具体数据写的时候,首先为了保证数据的可恢复性和写容错,这里将数据写到了WAL文件,在Wal文件中会为每次写操作分配一个sequenceId,记录本次写入的数据块。其中,WAL文件大小可配,如果WAL文件大小超过预阈值,则会顺序编号生成新的WAL文件。
2、同时,时序数据会写入到buffer和index中。当buffer中的数据缓存的数据点达到一定阈值时,默认是5000。会批量刷写到数据文件。同时,在index管理中,会根据tags set的维度组合,生成tags 与 metric的倒排索引,并将生成的索引数据持久化到索引数据文件。
3、重点,可以关注一下,在存储管理部分的数据文件。当buffer中的数据刷写到具体的数据文件的时候,SilverDB会根据时序数据点的时间属性,对数据进行时间范围的分区排列,并根据所设置的数据条数或者固定的数据块大小,进行数据切片。
4、整个数据切片是根据时间维度进行切片,这里SilverDB会根据时序数据点时间特征进行数据的排序和压缩,并设置数据块头的数据存储结构。
5、最终将进行时间切片的数据写到对应的数据文件,并且将对应的数据块头信息和数据块数据持久化到数据文件。
6、这里为什么要进行时间范围数据切片,可以想一下。在分布式数据写入的时候,我们根据Hash一致性算法对数据进行了Hash分片,这样可以均衡写负载,避免造成写热点问题。但是,与此带来的问题就是数据太散列了,反而在进行时间范围查询的时候,影响查询性能。
7、最终,这里还会根据写入的新的数据信息,更新本地元数据和远程的元数据服务信息。
所以,在最终进行数据持久化的时候,SilverDB又对数据进行了时间范围的分片,即解决了Hash一致性算法带来的时间范围查询的性能问题,又解决了写热点问题。
至此,对于SilverDB存储引擎底层的写数据流程有了一个基本的介绍和说明。
02、数据读操作
接下来,再看一下SilverDB存储引擎底层的读数据流程:
对于读操作就比较简单了,因为写操作会涉及很多的数据更新。
1、本地数据读,会根据查询的tag set(注意,此处tag set可以为空)和 metric 组合,查询本地维护的数据索引,根据内存索引数据查找出可以唯一表征一个数据点的tag set和metric。
2、获取的tag set 和 metric信息,查询条件指定的database、table、时间范围信息,可以直接去获得在指定时间范围内的数据文件。
3、扫描数据文件内容,查询满足条件的时间序列数据,并解码时序数据、解压时序数据,并返回查询结果。
4、最终,根据分布式数据读流程,进行时序数据的合并,返回给客户端。
总结
对分布式时序数据库SilverDB的分布式读写流程、本地数据读写机制的设计思路和基本原理做了一个介绍。
来源:freebuf.com 2020-09-10 09:36:12 by: 龙渊实验室LongYuanLab
请登录后发表评论
注册