Influxdb自定义数据采样(CQ)

来源:互联网 发布:东北林大网络教学平台 编辑:程序博客网 时间:2024/06/05 20:48

Influxdb提供了两种方式来进行数据的整合Continuous Queries (CQ:连续查询) 和 Retention Policies (RP:保留策略)。这两种方式结合使用,在降低精度的前提下,既可以保留很久以前的数据,又可以减轻数据所占用系统资源。一般都把连续查询成为数据采样,相信看这篇文章的朋友都对这些有所了解。

官网上提供的数据采样精度最大只能到周,如果我想实现按月采样,按季度采样后者按年采样,使用Influxdb的CQ是无法实现的,必须要自己来处理。

这里我们说下用JAVA来实现数据的自定义时间采样,其它语言也是同样道理,也可参考。

首先我们说下需求,对现有数据进行按天采样和按月采样。

先造数据:
从现在开始往前几百个小时,每小时一条,其中myname和address是tag,age是field,这里按照平均年龄采样。
这里写图片描述

注:name在Influxdb中类似于关键字的存在,所以最好别用,如果使用的话,一般查询的时候需要加引号。

首先按照日采样,这个功能Influxdb的CQ就能完成,这里我们采用代码的方式来实现,方便下面月的采样的理解。
先说明下采样执行的sql:

select mean(age) as age into database.rp.day_test from database.rp.mytest where time >=startTime and time <endTime group by time(1h),*

注:
1、这个sql可以在CQ执行的时候通过influx的日志获取到。
2、其中被采样的表我在执行的时候有时加上database和rp的时候查询不到,下面我把被采样的表的数据库和保留策略都省掉。但是采样之后的数据插入表必须要说明数据库和保留策略。
3、group by time(1h)这里的时间是根据自己的采样区间设定的,设定的时候如果时间不大于1h可以直接设定单位,如果大于1h,如1d就需要考虑时间偏移了。而我们这里的startTime和endTime就是我们的采样区间,直接就确定了这里time中的时间,所以我们把time()给省掉,这也是我们能够实现月采样或者季度年等实现的关键。去掉后startTime会成为被采样之后所有数据的时间,整合符合需求。
4、group by中的*表示所有的tag,如果只需要其中某些tag可以单独列出来。

日采样,我们设定每天执行一次(或者几次),这个跟CQ逻辑一样。看代码:

    public void dayCQ(){            HashMap<String,String> hashMap=this.yesterdayTimeZone();            String url="http://localhost:8086/query?db=billingrecord";            MultiValueMap<String,String> postParameter=new LinkedMultiValueMap<>();            String q="SELECT mean(age) as age INTO billingrecord.rp_90d.day_test FROM mytest WHERE time >="+hashMap.get("startTime")+" AND time < "+hashMap.get("endTime")+" group by *";            System.out.println("q:"+q);            postParameter.add("q",q);            RestTemplate restTemplate=new RestTemplate();            restTemplate.postForObject(url,postParameter,Object.class);    }    //获取起始时间和结束时间,为了避免时间差,我这里直接使用的时间戳public static HashMap yesterdayTimeZone(){        long zero = System.currentTimeMillis()/(1000*3600*24)*(1000*3600*24) - TimeZone.getDefault().getRawOffset();        long startTime=zero-3600*24*1000;        long endTime=zero;        HashMap<String,String> hashMap=new HashMap();        hashMap.put("startTime",startTime+"000000");        hashMap.put("endTime",endTime+"000000");        return hashMap;    }

执行之后的day_test表:
这里写图片描述

所有的时间点都落在了Utc时间的24日16点,转成东八区时间正好是15日0点。

做一下特殊处理,对mytest中所有数据都分别进行日采样,以便我们执行月采样:

public void dayCQ(){        for(int i=1;i<100;i++){            HashMap<String,String> hashMap=this.yesterdayTimeZone(i);            String url="http://localhost:8086/query?db=billingrecord";            MultiValueMap<String,String> postParameter=new LinkedMultiValueMap<>();            String q="SELECT mean(age) as age INTO billingrecord.rp_90d.day_test FROM mytest WHERE time >="+hashMap.get("startTime")+" AND time < "+hashMap.get("endTime")+" group by *";            System.out.println("q:"+q);            postParameter.add("q",q);            RestTemplate restTemplate=new RestTemplate();            restTemplate.postForObject(url,postParameter,Object.class);        }    }public static HashMap yesterdayTimeZone(int num){        long zero = System.currentTimeMillis()/(1000*3600*24)*(1000*3600*24) - TimeZone.getDefault().getRawOffset();        long startTime=(zero/1000-3600*24*num)*1000;        long endTime=(zero/1000-3600*24*(num-1))*1000;        HashMap<String,String> hashMap=new HashMap();        hashMap.put("startTime",startTime+"000000");        hashMap.put("endTime",endTime+"000000");        return hashMap;    }

这里在正常采样中是用不到的,只是为了测试写的,另外注意处理时间数据,很可能会出现数值越界。

现在我们在日采样的基础上进行月采样:

public void monthCQ(){            HashMap<String,String> hashMap=this.lastMonthTimeZone();            String url="http://localhost:8086/query?db=billingrecord";            MultiValueMap<String,String> postParameter=new LinkedMultiValueMap<>();            String q="SELECT mean(age) as age INTO billingrecord.rp_90d.month_test FROM rp_90d.day_test WHERE time >="+hashMap.get("startTime")+" AND time < "+hashMap.get("endTime")+" group by *";            postParameter.add("q",q);            RestTemplate restTemplate=new RestTemplate();            restTemplate.postForObject(url,postParameter,Object.class);    }public static HashMap lastMonthTimeZone(){        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM");        Calendar cal=Calendar.getInstance();        String  endTime=sdf.format(cal.getTime());        cal.add(Calendar.MONTH,-1);        String  startTime=sdf.format(cal.getTime());        try {            startTime=sdf.parse(startTime).getTime()+"000000";            endTime=sdf.parse(endTime).getTime()+"000000";        }catch (Exception e){            System.err.println("InfluxDB time deal error");        }        HashMap<String,String> hashMap=new HashMap();        hashMap.put("startTime",startTime);        hashMap.put("endTime",endTime);        return hashMap;    }

执行的sql为:

SELECT mean(age) as age INTO billingrecord.rp_90d.month_test FROM rp_90d.day_test WHERE time >=1498838400000000000 AND time < 1501516800000000000 group by *

注:不知道为啥,这次测试的时候time< 1501516800000000000这个一直没法使用,在其它表中都是ok的,只要加上time<这个判断就没数据,不过每月的第一天执行其实不加time<,对日进行采样,获取的数据都是一样的。这个我会继续测试找出具体原因。

看下结果:
这里写图片描述

换算成东八区之后时间都落在7月1号的零点。

按照季度采样和按照年采样也按这种方式执行就OK。