阿里云InfluxDB®目前已经商业化,专注于处理高写入和查询负载的时序数据,用于存储大规模的时序数据并进行实时分析,包括来自DevOps监控、车联网、智慧交通、金融和IOT传感器数据采集。
本文首先介绍时序数据库的发展,然后具体介绍阿里云InfluxDB®的功能。由于金融中股票交易具有高频和时间属性,非常符合InfluxDB的应用场景,最后提供了一个在阿里云InfluxDB® 创建的TIG(Telegraf/InfluxDB/Grafana)实例信息,采集了部分沪深股票交易信息,供用户登陆操作实践,更深入地了解时序数据和阿里云InfluxDB®。
时序数据
时序数据发展
时序数据发展到当下,主要经历了三个阶段:基于关系型数据库的存储,基于通用大数据的存储系统,以及垂直型时序数据库系统。每个阶段均出现代表性产品,如RRDTool, OpenTSDB, InfluxDB等。
- 基于关系型数据库存储:这类系统处理的数据模型比较单一,缺乏针对时间的特殊优化,单机容量受限,处理时序数据的效率相对不高;
- 基于通用大数据存储:伴随着大数据和Hadoop的发展,借助HBase等存储系统,利用时序的特性规避部分通用存储的劣势,在数据模型设计方面做了很多的改进,但时序的发展与存储发展路线不统一,缺乏定制化,往往会受制于底层存储,如索引技术,查询过滤机制等。
- 垂直型时序数据库:随着kubenetes, 微服务,IOT市场的崛起,针对时序数据的存储产品慢慢涌现,如InfluxDB专门针对时序的TSM存储引擎,Gorilla压缩,以及面向时序的窗口计算函数p99等。
当前时序数据库呈现百家争鸣的状态,传统数据库朝着分布式方向发展,如TimeScaleDB;OpenTSDB紧跟大数据的存储计算分离架构;InfluxDB TICK集成数据采集、分析和告警等。上图是DB Engine给出的关于时序数据库的排名,InfluxDB位于最受喜爱的时序数据库行列,也是阿里云将其开源托管的重要原因。
时序数据模型
下面将针对于InfluxDB数据模型来介绍时序数据。
- 时间线(time series): 测量值(measurement)与标签(tagset)的组合
- 时间点(time series point): 一个具体的时间戳下,某条时间线中,某些field的值。
时间线用于在写入和查询时对数据做快速匹配,关联时序数据索引,因此时间线的膨胀会引起时序索引结构膨胀,占用过多内存空间和匹配效率降低。在股票交易数据结构中,由于股票代码(code), 股票名称(name), 所属行业(industry)并不经常改变,因此在模型设计的过程中,可以将这些设为tagkey;成交价格(price),成交手(volume),成交金额(amount)这些要记录的值随时间会不断变动,我们将其放入field中,进行数值存储和函数计算。上图中:
时间线:
tick_data,industry=银行,name=平安银行,code=000001.SZ
时间点:
tick_data,industry=银行,name=平安银行,code=000001.SZ price=10.86,volume=2295,amount=2492370 2015-09-23T09:25:01Z
tick_data,industry=银行,name=平安银行,code=000001.SZ price=10.85,volume=128,amount=138847 2015-09-23T09:30:32Z
时序数据写入
关于阿里云InfluxDB®实例的购买和时序数据的写入具体可以参考阿里云官方文档。
如上图所示的存储结构中,为了更好管理历史数据,InfluxDB推出了数据保留策略(Retention Policies),用来定义数据的保留时间。数据保留策略(RP) 用来定义数据在InfluxDB中存放的时间,或者定义保存某个期间的数据。在阿里云InfluxDB®管理控制台,我们可以自定义历史数据的保留时长。
下面为采集A股日成交(daily)和日行情(tick_data)的时间线情况:
> use stock
Using database stock
> show series limit 10
key
---
daily,area=上海,industry=IT设备,list_date=19901219,name=方正科技,symbol=600601,code=600601.SH
daily,area=上海,industry=IT设备,list_date=20170607,name=恒为科技,symbol=603496,code=603496.SH
daily,area=上海,industry=专用机械,list_date=20150320,name=创力集团,symbol=603012,code=603012.SH
daily,area=上海,industry=专用机械,list_date=20150527,name=华铭智能,symbol=300462,code=300462.SZ
daily,area=上海,industry=专用机械,list_date=20150630,name=沃施股份,symbol=300483,code=300483.SZ
daily,area=上海,industry=专用机械,list_date=20160607,name=上海沪工,symbol=603131,code=603131.SH
daily,area=上海,industry=专用机械,list_date=20160812,name=上海亚虹,symbol=603159,code=603159.SH
daily,area=上海,industry=专用机械,list_date=20161018,name=古鳌科技,symbol=300551,code=300551.SZ
daily,area=上海,industry=专用机械,list_date=20170113,name=至纯科技,symbol=603690,code=603690.SH
daily,area=上海,industry=专用机械,list_date=20170314,name=克来机电,symbol=603960,code=603960.SH
> use tick
Using database tick
> show series limit 10
key
---
tick_data,area=上海,industry=区域地产,list_date=19961210,name=荣丰控股,symbol=000668,code=000668.SZ
tick_data,area=上海,industry=区域地产,list_date=19970925,name=三湘印象,symbol=000863,code=000863.SZ
tick_data,area=云南,industry=中成药,list_date=19931215,name=云南白药,symbol=000538,code=000538.SZ
tick_data,area=云南,industry=全国地产,list_date=19961205,name=美好置业,symbol=000667,code=000667.SZ
tick_data,area=云南,industry=房产服务,list_date=19940202,name=我爱我家,symbol=000560,code=000560.SZ
tick_data,area=云南,industry=机械基件,list_date=19990415,name=云内动力,symbol=000903,code=000903.SZ
tick_data,area=云南,industry=铜,list_date=19980602,name=云南铜业,symbol=000878,code=000878.SZ
tick_data,area=云南,industry=铝,list_date=19980408,name=云铝股份,symbol=000807,code=000807.SZ
tick_data,area=内蒙,industry=化工原料,list_date=19970131,name=远兴能源,symbol=000683,code=000683.SZ
tick_data,area=内蒙,industry=煤炭开采,list_date=19970606,name=平庄能源,symbol=000780,code=000780.SZ
函数分析
InfluxDB提供了丰富的计算函数帮助我们进一步挖掘数据的价值。InfluxQL函数分为四大类:聚合(aggregate)、选择(select)、转换(transform)和预测(predict)。
聚合
阿里云InfluxDB®支持的聚合函数有:[COUNT()]、[DISTINCT()]、[INTEGRAL()]、[MEAN()]、[MEDIAN()]、[MODE()]、[SPREAD()]、[STDDEV()]、[SUM()]。
查询某支股票2019-10-21日共成交了多少手:
> select sum(volume) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z'
name: tick_data
time sum
---- ---
2019-10-21T00:00:00Z 945952
查询某支股票2019-10-21日每小时的成交额(元):
> select sum(amount) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z' group by time(1h)
name: tick_data
time sum
---- ---
2019-10-21T00:00:00Z
2019-10-21T01:00:00Z
2019-10-21T02:00:00Z
2019-10-21T03:00:00Z
2019-10-21T04:00:00Z
2019-10-21T05:00:00Z
2019-10-21T06:00:00Z
2019-10-21T07:00:00Z
2019-10-21T08:00:00Z
2019-10-21T09:00:00Z 363351603
2019-10-21T10:00:00Z 531834212
2019-10-21T11:00:00Z 88167896
2019-10-21T12:00:00Z
2019-10-21T13:00:00Z 207135153
2019-10-21T14:00:00Z 372167894
2019-10-21T15:00:00Z 26619332
2019-10-21T16:00:00Z
2019-10-21T17:00:00Z
2019-10-21T18:00:00Z
2019-10-21T19:00:00Z
2019-10-21T20:00:00Z
2019-10-21T21:00:00Z
2019-10-21T22:00:00Z
2019-10-21T23:00:00Z
选择
阿里云InfluxDB®支持的聚合函数有:[BOTTOM()]、[FIRST()]、[LAST()]、[MAX()]、[MIN()]、[PERCENTILE()]、[SAMPLE()]、[TOP()]。
查询某支股票2019-10-21日P99的成交价格:
> select percentile(price, 90) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z'
name: tick_data
time percentile
---- ----------
2019-10-21T10:27:42Z 16.91
随机对某支股票2019-10-21日9:00am~3:00pm成交价格进行平均采样:
> select sample(price, 10) from tick_data where code='000001.SZ' and time >= '2019-10-21T09:00:00Z' and time <= '2019-10-21T15:00:00Z'
name: tick_data
time sample
---- ------
2019-10-21T10:27:18Z 16.93
2019-10-21T11:02:48Z 16.82
2019-10-21T11:11:36Z 16.87
2019-10-21T13:19:21Z 16.78
2019-10-21T13:22:24Z 16.77
2019-10-21T13:41:09Z 16.78
2019-10-21T13:44:18Z 16.78
2019-10-21T14:19:15Z 16.83
2019-10-21T14:19:21Z 16.83
2019-10-21T14:22:30Z 16.85
转换
阿里云InfluxDB®支持的转换函数有:[ABS()]、[ACOS()]、[ASIN()]、[ATAN()]、[ATAN2()]、[CEIL()]、[COS()]、[CUMULATIVE_SUM()]、[DERIVATIVE()]、[DIFFERENCE()]、[ELAPSED()]、[EXP()]、[FLOOR()]、[HISTOGRAM()]、[LN()]、[LOG()]、[LOG2()]、[LOG10()]、[MOVING_AVERAGE()]、[NON_NEGATIVE_DERIVATIVE()]、[NON_NEGATIVE_DIFFERENCE()]、[POW()]、[ROUND()]、[SIN()]、[SQRT()]、[TAN()]。
计算某支股票2019-10-21日每15分钟成交价格的变化:
> select derivative(mean(price)) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z' group by time(15m)
name: tick_data
time derivative
---- ----------
2019-10-21T09:30:00Z 0.18167224080267985
2019-10-21T09:45:00Z 0.049361092530652684
2019-10-21T10:00:00Z 0.13140000000000995
2019-10-21T10:15:00Z 0.10268372352284771
2019-10-21T10:30:00Z 0.02356981183064022
2019-10-21T10:45:00Z -0.02005673170050315
2019-10-21T11:00:00Z -0.056542594898782994
2019-10-21T11:15:00Z 0.02164948213387774
2019-10-21T11:30:00Z -0.0037370242214223026
2019-10-21T13:00:00Z -0.009116331096188665
2019-10-21T13:15:00Z -0.026667201136095997
2019-10-21T13:30:00Z -0.010301478953412158
2019-10-21T13:45:00Z 0.03505827505824399
2019-10-21T14:00:00Z 0.029964323811768168
2019-10-21T14:15:00Z 0.012986349675813358
2019-10-21T14:30:00Z 0.02997171129489118
2019-10-21T14:45:00Z 0.026839533796877646
2019-10-21T15:00:00Z -0.013153526970953067
计算某支股票2019年10月的月K线情况:
> SELECT moving_average(last("close"), 30) FROM "daily" WHERE ("code" = '000001.SZ') AND time >= '2019-09-01T00:00:00Z' and time <= '2019-10-31T00:00:00Z' GROUP BY time(1d) fill(previous)
name: daily
time moving_average
---- --------------
2019-10-01T00:00:00Z 14.985999999999994
2019-10-02T00:00:00Z 15.023999999999994
2019-10-03T00:00:00Z 15.066999999999991
2019-10-04T00:00:00Z 15.105333333333325
2019-10-05T00:00:00Z 15.13899999999999
2019-10-06T00:00:00Z 15.16499999999999
2019-10-07T00:00:00Z 15.19099999999999
2019-10-08T00:00:00Z 15.237333333333321
2019-10-09T00:00:00Z 15.289333333333323
2019-10-10T00:00:00Z 15.345666666666656
2019-10-11T00:00:00Z 15.420666666666655
2019-10-12T00:00:00Z 15.491666666666655
2019-10-13T00:00:00Z 15.562666666666654
2019-10-14T00:00:00Z 15.64733333333332
2019-10-15T00:00:00Z 15.730666666666654
2019-10-16T00:00:00Z 15.808666666666655
2019-10-17T00:00:00Z 15.890666666666654
2019-10-18T00:00:00Z 15.960666666666652
2019-10-19T00:00:00Z 16.01633333333332
2019-10-20T00:00:00Z 16.05533333333332
2019-10-21T00:00:00Z 16.10699999999999
2019-10-22T00:00:00Z 16.14299999999999
2019-10-23T00:00:00Z 16.177666666666656
2019-10-24T00:00:00Z 16.21899999999999
2019-10-25T00:00:00Z 16.241333333333323
2019-10-26T00:00:00Z 16.264999999999993
2019-10-27T00:00:00Z 16.282333333333327
2019-10-28T00:00:00Z 16.29966666666666
2019-10-29T00:00:00Z 16.316999999999997
2019-10-30T00:00:00Z 16.344666666666665
2019-10-31T00:00:00Z 16.372333333333334
预测
阿里云InfluxDB®支持的预测函数有:[HOLT_WINTERS()]。
HOLT_WINTERS采用季节性方法返回N个预测的Field Value。HOLT_WINTERS可用于:
- 预测时间什么时候会超过给定的阈值
- 将预测值与实际值进行比较,检测数据中的异常
对降雨量和气温的预测我们常常可以采用此方法,由于股票交易周期性不明显,在此便不再赘述。
报表展示
Grafana接入阿里云InfluxDB®可以参考文档,下图在Grafana上展示了某支股票近5年的交易情况:
数据告警
Grafana告警
如下图所示,Grafana支持简单的数据告警,用户可以根据需求自定义告警规则:
Kapacitor告警
Kapacitor作为TICK生态中的数据处理框架,用于告警、ETL(Extract-Transform-Load)和检测异常。主要特性包含:
- 既可以处理流数据(streaming data),也可以处理批量数据(batch data)
- 定期从InfluxDB查询数据,执行InfluxQL支持的函数,并将处理结果存回InfluxDB
- 支持添加用户自定义的函数检测异常
- 与HipChat、OpsGenie、Alerta、Sensu、PagerDuty和Slack等集成
下面的price.tick脚本以batch的方式处理某支股票的成交价格并设置告警提示:
batch
|query('''
SELECT max(price) AS mprice
FROM "tick"."rp1"."tick_data"
WHERE "code" = "000001.SZ"
''')
.period(1d)
.cron('*/5 * * * *')
|alert()
.warn(lambda: "mprice" >= 18.5)
|influxDBOut()
.database('alert')
.measurement('price')
.tag('kapacitor', 'true')
运行kapacitor define max-price -tick price.tick -type batch -dbrp tick.rp1 和kapacitor enable max-price便会每5分钟以batch的方式获取股票最高成交价格并告警提示,在alert中可以添加email或者hook推送报警。
总结
目前集成TIG(Telegraf/InfluxDB/Grafana)生态的阿里云InfluxDB®已经上线,用户可以在阿里云官网直接购买。当前我们采集了一些沪深股票的日线、历史交易日的分笔数据,以及实例运行主机的cpu、磁盘、内存监控,提供展示Demo供用户实践更好地了解时间序列数据和阿里云InfluxDB®。
- 阿里云InfluxDB® Grafana Demo入口:https://ts-bp177y62rheis2c5j-grafana.influxdata.rds.aliyuncs.com:3000
- Username/Password: test/grafanaTest01
- 沪深股票历史日成交分析:https://ts-bp177y62rheis2c5j-grafana.influxdata.rds.aliyuncs.com:3000/d/tJj8CY0Zz/gu-piao-ri-cheng-jiao-fen-xi?orgId=1
- 沪深股票历史分笔交易:https://ts-bp177y62rheis2c5j-grafana.influxdata.rds.aliyuncs.com:3000/d/NAhu3p0Wz/gu-piao-ri-xing-qing-fen-xi?orgId=1&from=now-1y&to=now
- 运行主机的基础监控:https://ts-bp177y62rheis2c5j-grafana.influxdata.rds.aliyuncs.com:3000/d/8H_jExAWk/linux-system-overview?orgId=1
现在,我们开启了首月一元购活动欢迎大家试用: