实现时序数据库(Time Series Database)在特定场景下“远超”通用数据库的难点

做一个 Prototype 或者 Demo 很简单,但做出一个真正好的时序数据库(Time Series Database,TSDB)产品却很难。

之所以说做 Prototype 简单,是因为 TSDB 天生就不擅长处理一些数据,比如带事务的交易数据。基于此,我们可以大刀阔斧地砍掉一些在通用型数据库中很重要的特性,例如事务、MVCC、ACID(在 Facebook 的 Gorilla 中甚至提出不需要保证 Duration)。某些 TSDB 的存储引擎,甚至不能处理乱序数据,在无乱序的前提下,存储引擎几乎可以退化为带 Index 的 Log。所以,从这个角度来看,Time Series Databse 可以做得很简单。

但是,从另一方面来说,做一个好的 TSDB 产品又很难。试想一下,在时序数据库的设计上,我们大刀阔斧地砍掉了比如事务、ACID 等特性之后,如果依然不能使其在时序场景下的表现远超通用型数据库,那做一个专门的 TSDB 就毫无意义了。这样的话,还不如不做,就直接用通用型数据库好了。

所谓“在时序场景下的远超”,应该是全方位的,比如写入的延迟与吞吐量、查询性能、处理的实时性、甚至包括集群方案的运维成本等,都应该有一个跨越式的提升。另一方面,从时间序列数据量大、价值偏低等特点出发,压缩率就显得比较重要了,而通用型数据库却很少强调压缩率,由此可见,压缩率是在时序场景下真实生长出来的需求。

高压缩率的实现没有什么黑科技,也不需要自己重新发明压缩算法——无非就是列存并对各个类型使用其最好的压缩算法;更多是工程实现的问题——好好写代码,认真做优化,平衡好写入性能与压缩比之间的关系。

此外,在时间序列数据场景下的“远超”是建立在时序数据的写入与查询分布特点极其明显的基础上,当数据本身 key的特征分布十分明显时,自然可以充分利用其特征来打造截然不同的存储引擎与索引结构。

先说写入。Time Series Database 的吞吐量远超一般的通用型数据库,尤其是 IoT 设备,其设备规模可能达到千万甚至上亿,数据均为自动生成,假设 1s 采样一次,那每秒就能产生千万、亿级别的数据写入,这并不是普通数据库能承受的,在这样大的吞吐量的情况下,数据如何分区分片、如何实时地构建索引,都是具有挑战性的问题。在写入链路上,TSDB 在时序场景下替代的是 OLTP 数据库的位置,而后者在事务与强一致的模型下产生的读写延迟很难支撑时序数据库的高吞吐量写入。

再说查询。在大写入吞吐量的情况下,数据对实时性的要求也很高。例如,我们将时序数据的统计量关联做监控、报警,能容忍的延迟可能在秒级。查询的模式通常是聚合查询,例如某时间段内的统计值,而不是精确的单条记录。总的来说,TSDB 的查询模式通常是交互式分析,这不同于 T+1 的离线数仓,也区别于经常运行数小时的 OLAP 查询,交互式分析查询的响应时间通常是秒级、亚秒级。

可见,实现时序数据库(Time Series Database)在特定场景下“远超”通用数据库,还是存在非常多的难点,在明确了写入与查询需求的同时,接下来的文章我们会以 TDengine 为例,来看一看一个Time Series Database 的某一个部分应该如何设计。