> 原文出处:http://www.infoq.com/cn/articles/database-timestamp-01
> 作者: 陶文
什么是时间序列数据?最简单的定义就是数据格式里包含timestamp字段的数据。比如股票市场的价格,环境中的温度,主机的CPU使用率等。但是又有什么数据是不包含timestamp的呢?几乎所有的数据都可以打上一个timestamp字段。时间序列数据更重要的一个属性是如何去查询它。在查询的时候,对于时间序列我们总是会带上一个时间范围去过滤数据。同时查询的结果里也总是会包含timestamp字段。
## 选择什么样的时间序列数据库
时间序列数据无处不在。而几乎任意数据库都可以存时间序列数据。但是不同的数据能支持的查询类型并不相同。按照能支持的查询类型,我们可以把时间序列数据库分为两类,第一类的数据库按照关系型数据库的说法,其表结构是这样的:
~~~
[metric_name] [timestamp] [value]
~~~
其优化的查询方式是:
~~~
SELECT value FROM metric WHERE metric_name=”A” AND timestamp >= B AND timestamp < C
~~~
也就说这类数据库是什么样子的数据存进去,就什么样子取出来。
![](https://box.kancloud.cn/2015-09-06_55ebe8cd16ed7.jpg)
在这种模式下,首先要知道你需要的图表是什么样子的。然后按照这个图表的数据,去把数据入库。查询的字段,就是数据库存储的字段。然后再按照数据库存储的字段,去从原始数据里采集上报。存储什么字段,就上报什么字段。这种模式很容易优化,可以做到非常快。但是这种模式有两个弊端。
* 无法快速响应变化:如果需要的图表有变更,需要从上报的源头重新来一遍。而且要等新数据过来之后,才能查看这些新数据。
* 存储膨胀:总有一些数据是需要从不同维度查询的要求。比如广告点击流数据,需要按省份聚合,按运营商聚合,按点击人的喜好聚合等。这些维度的交叉组合会产生非常巨大的组合数量,要预先把所有的维度组合都变成数据库里的表存储起来会很浪费空间。
这类时间序列数据库最多,使用也最广泛。一般人们谈论时间序列数据库的时候指代的就是这一类存储。按照底层技术不同可以划分为三类。
* 直接基于文件的简单存储:[RRD Tool](http://oss.oetiker.ch/rrdtool/),[Graphite Whisper](http://graphite.wikidot.com/whisper)。这类工具附属于监控告警工具,底层没有一个正规的数据库引擎。只是简单的有一个二进制的文件结构。
* 基于K/V数据库构建:[opentsdb](http://opentsdb.net/)(基于[hbase](http://hbase.apache.org/)),[blueflood](http://blueflood.io/),[kairosDB](https://kairosdb.github.io/)(基于[cassandra](http://cassandra.apache.org/)),[influxdb](https://influxdb.com/),[prometheus](http://prometheus.io/)(基于[leveldb](http://leveldb.org/))
* 基于关系型数据库构建:[mysql](https://www.mysql.com/),[postgresql ](http://www.postgresql.org/)都可以用来保存时间序列数据
另外一类数据库其表结构是:
~~~
[timestamp] [d1] [d2] .. [dn] [v1] [v2] .. [vn]
~~~
其优化的查询方式不限于查询原始数据,而是可以组合查询条件并且做聚合计算,比如:
~~~
SELECT d2, sum(v1) / sum(v2) FROM metric WHERE d1 =
“A” AND timestamp >= B AND timestamp < C GROUP BY d2
~~~
![](https://box.kancloud.cn/2015-09-06_55ebe8d4f40d8.jpg)
我们希望时间序列数据库不仅仅可以提供原始数据的查询,而且要支持对原始数据的聚合能力。这种聚合可以是在入库阶段完成的,所谓物化视图。也可以是在查询阶段完成,所谓实时聚合。根据实际情况,可以在这两种方式中进行取舍。
想要在在查询阶段做数据的聚合和转换,需要能够支持以下三点。
* 用索引检索出行号:能够从上亿条数据中快速过滤出几百万的数据。
* 从主存储按行号加载:能够快速加载这过滤出的几百万条数据到内存里。
* 分布式计算:能够把这些数据按照GROUP BY 和 SELECT 的要求计算出最终的结果集。
![](https://box.kancloud.cn/2015-09-06_55ebe8d610081.jpg)
要想尽可能快的完成整个查询过程,需要在三个环节上都有绝招。传统上说,这三个步骤是三个不同的技术领域。
* 检索:这是搜索引擎最擅长的领域。代表产品是[Lucene](https://lucene.apache.org/core/)。其核心技术是基于高效率数据结构和算法的倒排索引。
* 加载:这是分析型数据库最擅长的领域。代表产品是[C-store](http://db.csail.mit.edu/projects/cstore/)和[Monetdb](https://www.monetdb.org/Home)。其核心技术是按列组织的磁盘存储结构。
* 分布式计算:这是大数据计算引擎最擅长的领域。代表产品是[Hadoop](https://hadoop.apache.org/)和[spark](http://spark.apache.org/)。其核心技术是sharding 和 map/reduce等等。
前面提到的时间序列库(比如[opentsdb](http://opentsdb.net/))有不少从功能上来说是没有问题。它们都支持过滤,也支持过滤之后的聚合计算。在数据量小的时候勉强是可用的。但是如果要实时从十亿条里取百万记录出来,再做聚合运算,对于这样的数据量可能就勉为其难了。满足海量数据实时聚合要求的数据库不多,比较常见的有这么几种:
* 基于[Lucene](https://lucene.apache.org/core/)构建的“搜索引擎”:[Elasticsearch](https://www.elastic.co/products/elasticsearch), [Crate.io](https://crate.io/)(虽然是基于[Elasticsearch](https://www.elastic.co/products/elasticsearch),但是聚合逻辑是自己实现的),[Solr](http://lucene.apache.org/solr/);
* 列式存储数据库:[Vertica](http://www.vertica.com/)([C-store](http://db.csail.mit.edu/projects/cstore/)的后裔)[Actian](http://www.actian.com/products/analytics-platform/)([Monetdb](https://www.monetdb.org/Home)的后裔)等;
* Druid.io。
其中Elasticsearch是目前市场上比较很少有的,能够在检索加载和分布式计算三个方面都做得一流的数据库。而且是开源并且免费的。它使用了很多技术来达到飞一般的速度。这些主要的优化措施可以列举如下。
* Lucene的inverted index可以比mysql的b-tree检索更快。
* 在 Mysql中给两个字段独立建立的索引无法联合起来使用,必须对联合查询的场景建立复合索引。而lucene可以任何AND或者OR组合使用索引进行检索。
* Elasticsearch支持nested document,可以把一批数据点嵌套存储为一个document block,减少需要索引的文档数。
* Opentsdb不支持二级索引,只有一个基于hbase rowkey的主索引,可以按行的排序顺序scan。这使得Opentsdb的tag实现从检索效率上来说很慢。
* Mysql 如果经过索引过滤之后仍然要加载很多行的话,出于效率考虑query planner经常会选择进行全表扫描。所以Mysql的存储时间序列的最佳实践是不使用二级索引,只使用clustered index扫描主表。类似于Opentsdb。
* Lucene 从 4.0 开始支持 DocValues,极大降低了内存的占用,减少了磁盘上的尺寸并且提高了加载数据到内存计算的吞吐能力。
* Lucene支持分segment,Elasticsearch支持分index。Elasticsearch可以把分开的数据当成一张表来查询和聚合。相比之下Mysql如果自己做分库分表的时候,联合查询不方便。
* Elasticsearch 从1.0开始支持aggregation,基本上有了普通SQL的聚合能力。从 2.0 开始支持 pipeline aggregation,可以支持类似SQL sub query的嵌套聚合的能力。这种聚合能力相比Crate.io,Solr等同门师兄弟要强大得多。
后面我们分为两篇文章用科普的方式,具体来看看Elasticsearch是基于什么原理如何做到比mysql和opentsdb更快地查询和聚合时间序列数据的。
## 作者简介
**陶文**,曾就职于腾讯IEG的蓝鲸产品中心,负责过告警平台的架构设计与实现。2006年从ThoughtWorks开始职业生涯,在大型遗留系统的重构,持续交付能力建设,高可用分布式系统构建方面积累了丰富的经验。