时序数据库是非关系型数据库的一种,其全称为时间序列数据库(Time Series Database)。时序数据库主要用于存取具有时间特征的数据。
时序数据是随时间产生的数据,其有着产生频率快、依赖时间、数据量庞大的特点。由于时序数据的数据量非常庞大,传统的关系型数据库在底层数据结构(可参见笔者文章)上导致了其无法对该类数据进行有效的存储和管理。数据数据库通过时序数据的特性,对时序数据采用特殊的存储方式,这使时序大数据得到了有效的管理。
例如,基于HBase的opentsdb,其采用“数据便是索引”的存储方式(关于HBase数据结构,可参加笔者相关文章),使得主键索引的利用更加高效,在大量、高并发的查询时,极大地降低了系统的I/O。
以opentsdb为例:
1)数据写入:
官方文档地址:http://opentsdb.net/docs/build/html/api_http/put.html
数据写入数据结构:
[
{
"metric":"city.temperature",
"value":"24.7",
"timestamp":1572331517,
"tags":
{
"city":"beijing"
}
}
]
以上示例便为各大TSDB常见的数据结构。metric为指标,value为该指标在该时刻的值,timestamp为时间戳,tags便为该指标的标记。
如上图所示,我们这条数据上报成功了。
2)数据查询
官方文档地址:http://opentsdb.net/docs/build/html/api_http/query/index.html
数据查询常用数据结构:
{
"start": 1572331507,
"end": 1572331527,
"queries": [
{
"aggregator": "",
"metric": "city.temperature",
"downsample": "",
"rate": true,
"filters": [
{
"type": "literal_or",
"tagk": "city",
"filter": "beijing",
"groupBy": false
}
]
}
]
}
字段说明:start查询开始时间,end查询结束时间,queries为查询的基本操作,aggregator为聚合方式(如avg、max、min、last、sum等),metric为查询的指标,downsample为采样方式(如1min-avg-null、1h-max-zero等,形式为采样时间-采样方式-补值策略),rate为是否求速率(根据需要决定),filters为根据tag进行过滤查询,type常用的为literal_or(查询的tag值之间为或的关系)和wildcard(使用通配符),groupBy为是否分组,将在下面进一步说明。
时序数据是以时间线(TS)的形式存储在TSDB中的,现在,我们再上报1个城市的数据,并且对城市北京补充一些温度值:
[
{
"metric":"city.temperature",
"value":"24.7",
"timestamp":1572331517,
"tags":
{
"city":"beijing"
}
},
{
"metric":"city.temperature",
"value":"23",
"timestamp":1572331577,
"tags":
{
"city":"beijing"
}
},
{
"metric":"city.temperature",
"value":"21",
"timestamp":1572331637,
"tags":
{
"city":"beijing"
}
},
{
"metric":"city.temperature",
"value":"27",
"timestamp":1572331517,
"tags":
{
"city":"shanghai"
}
},
{
"metric":"city.temperature",
"value":"24",
"timestamp":1572331577,
"tags":
{
"city":"shanghai"
}
},
{
"metric":"city.temperature",
"value":"22",
"timestamp":1572331637,
"tags":
{
"city":"shanghai"
}
}
]
新的全部数据如上所示,其在TSDB中的时间线如下图所示:
在TSDB中,时间线的组织是以metric和tags进行组织的,例如,如果再上报一条:
[
{
"metric":"city.temperature",
"value":"24.7",
"timestamp":1572331517,
"tags":
{
"city":"beijing",
"address":"tiananmen"
}
}
]
此时便会变为三条时间线,因此,只要metric和tag有任何不一样,便会产生一条时间线。
1)aggregator为不同时间线间数据的聚合方式,例如在上面的两条时间线中,我们要求每一时刻的两个城市(所有城市)的温度之和,可以这样请求:
在这个请求中,我们将aggregator设置为sum(当然也可以尝试max、min等),此时便求出了两个城市在不同时间的温度之和,aggregator的时间线操作如下图所示:
2)关于downsample,假如我们一秒一个点,这样会导致数据点繁多,为了解决这个问题,我们可以对1分钟(或5分钟等等)的数据进行采样,比如,我们想对北京这个城市的温度每5分钟进行一个采样,采样方式为去平均值,我们可以这样请求:
在上面的请求中,我们的采样方式为每五分钟进行采样,采样方式为取平均值,没有值时补值为null。downsample的时间线操作如下:
3)关于groupBy,group为对不同的tags是否进行分组,如果值为true,则为分组,如上两条时间线,分组查询会返回两条时间线:
返回如下:
[
{
"metric": "city.temperature",
"tags": {
"city": "shanghai"
},
"aggregateTags": [],
"dps": {
"1572331500": 24.333333333333332,
"1572331800": null,
"1572332100": null
}
},
{
"metric": "city.temperature",
"tags": {
"city": "beijing"
},
"aggregateTags": [],
"dps": {
"1572331500": 22.900000000000002,
"1572331800": null,
"1572332100": null
}
}
]
如果值为false,则为不分组,如上两条时间线,不分组查询会返回一条时间线:
返回如下:
[
{
"metric": "city.temperature",
"tags": {},
"aggregateTags": [
"city"
],
"dps": {
"1572331500": 23.616666666666667,
"1572331800": null,
"1572332100": null
}
}
]
注:本例中我们同时使用了聚合方式和采样方式。