数据聚合(一)
在统计学中,聚合的定义指
使用基于多组观测结果的总结的统计替换多组观测结果 — 来自 https://web.archive.org/web/20120112062156/http://www.r-bloggers.com/aggregation-and-restructuring-data-from-%E2%80%9Cr-in-action%E2%80%9D/
预期的功能及实现
实现一个由分钟到年的聚合系统。
一、根据下一级数据聚合
比如小时的数据由分钟聚合,天的数据由小时聚合,周、月的数据由天聚合,年的数据由月聚合。
要求:
- 下级数据存在
- 选择正确的下级数据。比如月数据就不能由周数据聚合,而只能从天来聚合。
好处:
可以减少计算量。
二、数据完整
由于要根据下一级数据聚合,所以每个级别的数据都不能少。所以需要
- 能够检测(各个级别的)数据完整
- 能够幂等地重跑数据
- 自动检测到数据不完整后,能自动地重跑数据
时区问题
一般的聚合都是基于时间的,比如从分钟以下聚合为分钟,然后小时,天,周,月,年等,所以就会有时区存在。
写入与查询
写入数据库的时间要带上时区。因为 MySQL、MongoDB 等数据库会将写入的 datetime 转化为 UTC 时区再储存,所以如果写入的时间没有带时区,数据库就会认为写入时间是 UTC 时区,可能会与你预期的不一致。
同理,查询时也要带上时区,因为数据库默认没有时区的时间为 UTC 时区。
聚合结果的时间储存
由于当前级别的数据是根据下一级数据聚合的,所以需要储存级别–时间类型以及时间。
时间类型为
- minutely
- hourly
- daily
- weekly
- monthly
- yearly
聚合时间为时间段的起点,我们把它叫做 time_start
。
"date_type" : "minutely", "time_start" : ISODate("2018-01-21T05:01:00Z")
"date_type" : "minutely", "time_start" : ISODate("2018-01-21T05:02:00Z")
...
"date_type" : "minutely", "time_start" : ISODate("2018-01-21T05:59:00Z")
// 根据以上的分钟数据聚合小时数据
"date_type" : "hourly", "time_start" : ISODate("2018-01-21T05:00:00Z")
多时区
由于在不同时区的每天的定义是不同的:
北京时间 07月31日为 “07月30日16:00:00 UTC — 07月31日16:00:00 UTC” 而东京则为 “07月30日15:00:00 UTC — 07月31日15:00:00 UTC”。
我目前的想法是再给聚合加上时区参数,但在本文不讨论聚合包含多时区问题,因为能用到的地方比较少。
聚合时机
聚合时机为当下一级别的数据不再变化时。一般来说,过去的数据是不会再变化的,所以我们聚合过去的数据:当前分钟聚合上一分钟的数据,当前小时聚合上一小时的数据,每月一号计算上一月的数据。
如果存在特殊情况导致要等待的久一些:比如由于网络延迟导致数据来的慢一些。我们也要进行调整:比如当前分钟聚合5分钟前的数据。