关于时间序列数据库的思考

更新时间:2015-07-14 10:29:23点击次数:659次

近期网络上出现了有关catenabenchmarking boltdb等时间序列存储方式的介绍,Go社区也有类似的讨论话题,出现了serieslyinfluxDBprometheus等优秀项目。原文作者Jason moiron目前从事Datadog相关工作,文中他针对时间序列数据库发表了一些看法,(网友们在Hacker News上也有精彩的评论)我们一起来认识下。

时间序列模型和图模式先于计算机出现,但是直到90年代初伴随着MRTG的出现才逐渐发展壮大。这些系统的不断成熟是有原因的:“大数据”的“岂止于大”;虚拟化和容器化带来了可观的分布式应用节点增量;云服务对摩尔定律提出了挑战。

时间序列存储层(Time-series Storage Layer)

人们常常会这样问自己:


  • 有对写入进行优化吗?

  • 有对读取进行优化吗?

  • 数据库要支援哪种语义学才能使用户得到想要的查询结果呢?


如果前两个问题的答案是“是”,存储层就务必同时对写入和读取进行优化。

如果你试图对一个已索引的数据库进行多个序列写入,想象下这是一个含有主键,时间戳,值等数据的SQL表,那么当要处理读请求时就不得不处理庞大的数据。如果你把序列分开写入,那么这或许是最差劲的写入模式:高频跨数据写入。

我看到有的开发者的做法是:


  • 使用文件(例如:RRD,Whisper)

  • 使用LSM树来备份(例如:LevelDB,RocksDB,Cassandra)

  • 使用B-树排序和k/v存储(例如:BoltDB,LMDB)


这些做法有优点也有缺点。这里不妨尝试别的方法,时间序列是一个多点矢量,因此要做的其实很简单:


  • 创建一个新矢量

  • 找出一个矢量(不同的)

  • 附加到矢量上(0(1))

  • 从矢量读取数据


使用文件

显然所有数据库都是“文件式”的,但我这里所指的是文件即序列(file-as-a-series)的方式。透过该方式,你可以得到高效的缓冲附加并可进行自由的缓存线性读取。对于高级文件系统如zfs,ext4,xfs等来说,目录记录搜索是基于hash表的,也即意味着是0(1)。

文件系统越来越复杂而变得难于理解,因此需要一些黑艺术来对它进行调节。尽管有些系统的设计目的是为了处理百万级的文件,但是还没有专门针对小文件而做的优化处理。这样就导致了对于少量数据查询来说,很多时候都会存在空页浏览的情况。最后,会遍历整个数据集,这对于备份,批量作业等操作都是有重要影响的。

使用基于树的存储方式

当使用基于树的KV存储方式时,可以有几种实现方法。键可以识别一个单序列,或一个单点,或一个序列中的某个时间片。无论你的键结构甚或树类似的选择是什么,键查找都会是O(log n),从而在表上会留有很多空白。

对于大规模存储时,如果点的数量过于庞大,这对于CPU缓存是个灾难;因此把数据看成是{series, timestamp} -> {point}的KV对是不会工作的。对于小规模数据,开源时间序列存储是尝试使用类似的方式来处理,但是我很有信心地说,换用文件会更加简单和快捷,除非你要经常读取很大量的数据集。对于O(log n)查找,我可不想在读取上花费很多时间。

所以接下来要关注写入端,这是一个难点。使用双B树的COW方法看起来不适合于大量的随机写入。在实际中这不妨做进一步的研究。所有这些使用mmap及频繁微量写入时,会对你的数据库造成大量页阅读,从而导致f/msync的I/O处于高负荷状态。附加在LSM上可能情况会好些,但是树合并压缩操作也是一个高强度作业。

总的来说,树对于写入和读取都是可以兼顾的,这是很好的互补方式。

查询语义

我认为时间序列存储所需的唯一语义支持应该是可以进行批量和并行读取;而其他的如压缩读取规模,更好的外部实现,还有对时间序列存储的约束等都是加分项。

维度

根据我的开发经验,发现维度是至关重要的;从产品层面看,缺乏对它的支持将会使你的产品被取代。

把所有的元数据都存入度量键中,然后以键/值(K/V)查找的方式来进行查询不最优的处理方式;你至少要拥有or/and和逻辑操作来实现跨维度;例如:eg: az=us-east-1 AND role=db-master, version=2.1 OR version=2.3,若有更丰富操作的语言则更好。但对于存储层来说需要支援任何的额外语义来实现这些优化吗?在维度存储中,查询中的索引要么指向了大量无关数据的元组,要么包含了数据本身从而导致写入缓慢。SSD方式或许好些,但仍需读取所有数据页才有效;数据位置仍是个问题。

聚合,失效等

时间聚合和数据失效在轮询调度中的重要性不言而喻,这仍是开源的标准。例如rrdtool,graphite等。

此外还有几个重要问题亟需解决。RRD是连续空间,但时间序列的使用往往包含了大量的稀疏序列。另外,对于写入的压缩/聚合会使写入操作变得庞大。聚合和失效看起来很像维度:可以在一个独立策略层中异步实现。


  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息