OPENTSDB接入grafana

最近有个需要场景:查看每分钟,每小时,每天的订单数量,监控告警。据说tsdb存储量大,查询效率高,可能适用于这种场景…

入坑

是否适用场景

阿里云文档适用的场景举了3个实用例子:物联网设备监控分析,电力化工及工业制造监控分析,系统运维和业务实时监控。例子都是定时记录数据,比如每5秒钟采集一次设备信息,本业务是监听mq的消息进行写入,然后接grafana渲染,每秒钟都有数据写入,有一些区别。

基本概念

再看一下名词解释:https://help.aliyun.com/document_detail/55709.html?spm=a2c4g.11186623.6.547.2f3d2fd8vYoCVt
在这里插入图片描述

有几个应该比较关键的概念,把它往数据库上靠,nosql相似的就是mongo(实际上不一样)。
Metric可能就是mongo的表名,Tags是个标签,Vale存储的值
Data Point 数据点,Time Series 时间序列
还有很多其它概念,不甚理解,也没人告诉说对不对,先搞台服务器,搞些数据再说。

买机器

在这里插入图片描述

写数据

有库了,就是写数据,先看怎么连接,再看需要什么样的数据,然后再写入实践
根据文档,可以通过http api的方式写入,java可以通过SDK写入。考虑到用sdk,速度应该快一些,就用sdk。

1.gradle引包获取sdk

compile group: ‘com.aliyun.openservices’, name: ‘aliyun-log’, version: ‘0.6.60’

2.创建客户端
// 为 HiTSDBConfig 配置参数,并创建HiTSDBConfig实例。
// example.hitsdb.com 表示域名或地址。8242 表示 HiTSBD 的网络端口。您实际的域名地址和网络端口可到控制台获取。
// tsdbuser和password 表示用于用户认证的用户名和密码。TSDB用户可在实例管理控制台创建。如果实例未启用用户鉴权功能,则创建Config对象时无需调用basicAuth()方法
HiTSDBConfig config = HiTSDBConfig.address("example.hitsdb.com",8242).basicAuth("tsdbuser", "password").config();
// 通过 HiTSDBClientFactory 生成一个 HiTSDB 对象。
HiTSDB tsdb = HiTSDBClientFactory.connect(config);
3.写入数据
Point point = Point.metric("test").tag("V", "1.0").value(System.currentTimeMillis(), 123.4567).build();
            Thread.sleep(1000);  // 1秒提交1次
tsdb.put(point);
tsdb.close()

步骤很简单,但是目前的例子直接用肯定不放心的,要多看一看,然后进行改造。
发现两种写入方式,同步阻塞和异步非阻塞写入,然后异步写入的响应也有好几种,putCallBack有三种。同步阻塞写入自己封装了数据点,500条记录写一次,自己的需求,是每秒都有数据写入,不一致。
继续看文档,发现出现个多值写入,它也是分了同步和异步写入,然后异步回调响应有三种:https://help.aliyun.com/document_detail/109755.html?spm=a2c4g.11186623.6.612.751a76c65xrmjK

// 阿里云样例
        /*
         *  通过 multiFieldPutSync() API。
         *  该 API 支持单点和多点(List)写入。
         *  
         *  下面是必须写入时必须提供的信息:
         *  Metric: 数据指标的类别。相当于 influxdb 的 Measurement。
         *  Fields: 数据指标的度量信息(相当于指标下的子类别)。即一个 metric 支持多个 fields。如 metric 为 wind,该 metric 可以有多个 fields:direction, speed, direction, description 和 temperature。
         *  Timestamp: 数据点的时间戳
         *  Tags: 时间线额外的信息,如"型号=ABC123"、"出厂编号=1234567890"等。
         */
        MultiFieldPoint point1 = MultiFieldPoint.metric("wind").timestamp(1346846400L)
                .fields("speed", 20.0)
                .fields("level", 5)
                .fields("direction", "NE")
                .fields("description", "Breeze")
                  .fields("temperature", "18.9")
                .tag("sensor", "IOTE_1988_0001")
                  .tag("district", "Yuhang")
                  .tag("city", "Hangzhou")
                  .tag("province", "Zhejiang")
                  .build();
        // 同步写入
        tsdb.multiFieldPutSync(point1);
        // 同步写入并且获取写入成功失败总结
        tsdb.multiFieldPutSync(point1, SummaryResult.class);
        // 同步写入并且获取详细的写入成功或者失败信息
        tsdb.multiFieldPutSync(point1, DetailsResult.class);

就按官方的例子,需要写入metric,timestamp,tags和field 四个内容,原文档有笔误(写入的是point1,不是multiValuedPoint)。如下:

 /**
     * 多值模型同步写入
     */
    @Override
    public void insertDataToTsdb(TsdbReceiveDataDTO tsdbReceiveDataDTO) {
        if (null== tsdbReceiveDataDTO.getBizOrderId() || null==tsdbReceiveDataDTO.getOrderAmount()){
            logger.error("多值模型数据写入失败,原因是filedValue值缺失:insertMqDataToTsdb Error,HandleTsdbServiceImpl#tsdbReceiveDataDTO={}", JSON.toJSONString(tsdbReceiveDataDTO));
            return;
        }
        MultiFieldPoint point = MultiFieldPoint
                .metric(tsdbReceiveDataDTO.getMetric()).timestamp(System.currentTimeMillis()/1000)  //读取需要加上+0800
                .field("order_amount", tsdbReceiveDataDTO.getOrderAmount()==null? StringUtils.EMPTY :tsdbReceiveDataDTO.getOrderAmount()) // 订单金额
                .field("sub_biz_count",tsdbReceiveDataDTO.getSubBizCount()==null? StringUtils.EMPTY :tsdbReceiveDataDTO.getSubBizCount()) //子订单数量
                .tag("biz_order_id", tsdbReceiveDataDTO.getBizOrderId()==null? StringUtils.EMPTY :tsdbReceiveDataDTO.getBizOrderId()) // 订单id
                .tag("sub_biz_type", tsdbReceiveDataDTO.getBizType() == null ? StringUtils.EMPTY : tsdbReceiveDataDTO.getBizType().toString())  //业务线
                .tag("pay_channel", tsdbReceiveDataDTO.getPayChannel() == null ? StringUtils.EMPTY : tsdbReceiveDataDTO.getPayChannel().toString())  //支付渠道
                .tag("order_type", tsdbReceiveDataDTO.getOrderType() == null ? StringUtils.EMPTY : tsdbReceiveDataDTO.getOrderType().toString())  //订单类型 #
                .tag("order_source", tsdbReceiveDataDTO.getOrderSource())  //订单来源
                .tag("gmt_create", tsdbReceiveDataDTO.getGmtCreate() == null ? StringUtils.EMPTY :tsdbReceiveDataDTO.getGmtCreate().toString())  //处理时间
                .build();
        try {
            // 同步写入
            // tsdbClient.multiFieldPutSync(point);
            // 同步写入并且获取写入成功失败总结
            // SummaryResult summaryResult = tsdbClient.multiFieldPutSync(point, SummaryResult.class);
            // 同步写入并且获取详细的写入成功或者失败信息
            MultiFieldDetailsResult detailsResult = tsdbClient.multiFieldPutSync(point, MultiFieldDetailsResult.class);
            List<MultiFieldErrorPoint> errorPointList =  detailsResult.getErrors();
            if (errorPointList.size()>0){
                logger.error("数据写入tsdb失败:HandleTsdbServiceImpl#insertDataToTsdb Error detailsResult={}", JSON.toJSONString(detailsResult));
            }
            // tsdbClient.close();
        } catch (Exception e) {
            logger.error("多值模型数据写入失败:HandleTsdbServiceImpl#insertDataToTsdb Error,tsdbReceiveDataDTO={}", JSON.toJSONString(tsdbReceiveDataDTO), e);
        }
    }

上面的代码可以发现,定义了6个tag和2个field,运行代码就实现了数据插入。

问题1:tsdb客户端关闭和重连问题。

由于我的写入是每秒有不定的数据写入,而不是每隔10s或1min写一次这样,这就想到大量的写入会导致网络耗时和客户端关闭重连问题。搜索发现文档很少,也没有可用链接池,很明显不可能写一次关闭客户端,再写入的时候重新链接。后来资讯阿里的技术支持,回应在应用生命周期内不用关闭TSDB实例,直到应用结束时关闭即可。因此暂时不考虑使用tsdbClient.close()来关闭客户端。这里由于异常导致的客户端链接断开,自动重链需要自己封装优化。

查数据

数据写入了 度量当中,开始查询,阿里云提供了控制台,可以输入tsql进行查询,值得注意的是查询必须选择时间。因为查询涉及的时间线数量越少,查询的速度会很快。
在这里插入图片描述
数据写的很快,时间线快速增长,不久就到一百多万的时间线总量了。
在这里插入图片描述

问题2:接入granfa无数据加载出来

这个问题是由于自己的疏忽造成的,由于查询的时候看到渲染出来的数据是utc时间,因此写入的时候给加了8小时,如图:
在这里插入图片描述
最初:int timestamp = (int) (System.currentTimeMillis() / 1000);通过tsql查询timestamp显示为utc区的时间,比北京时间晚8小时;
后来:int timestamp = (int) (System.currentTimeMillis() / 1000+8*3600);通过tsql查询时间显示正常,但是grafana查询不到数据了,grafana默认的starttime是当前时间,可选过去多久,end时间是当前时间附近,查不到未来的8小时时间,因此返回为空。
改正过后,int timestamp = (int) (System.currentTimeMillis() / 1000),注意这里最好使用秒级时间,否则会影响查询效率。

问题3 查询响应极慢,时间跨度稍微大点,就会导致崩掉,查询不出来

如果查询中只指定了metric,而没有任何tag条件,导致这个查询实际上会首先去查询度量下的全部时间线,时间线的数量很大,时间线还只是TSDB中组织数据的最基本维度,之后还要扫数据点。因此这样的查询实际上是非常耗时的。

1.优化查询方式

建议尽量减少对一个metric跨越全量时间线的类分析型查询,查询时带上tag条件,减少查询命中的时间线数量。
减少查询时间范围
https://help.aliyun.com/document_detail/106112.html?spm=a2c4g.11186623.6.701.231917fewtgx8i
在这里插入图片描述

2.表设计有问题–重新设计时间线提升查询效率

https://help.aliyun.com/document_detail/106115.html?spm=a2c4g.11186623.6.702.5fba6822LfjQtc
在这里插入图片描述
在这里插入图片描述
按下面的最佳实践重新设计度量表,原则就是尽可能的减少时间线数量,因此要减少tagValue值,而多值字段还要减少field字段的值(有疑问这样的话,多值字段岂不是很鸡肋–阿里技术支持未回应,暂时不搁置)。
重新设计后使用单值模型写入,只有3个tag,订单量当做value存储,因此时间线数量很少。查询效率确实得到了提升,查询1天的数据也基本是秒响应

 /**
     * 单值模型的同步写入
     */
    @Override
    public void insertDataToTsdbBySingle(TsdbReceiveDataDTO tsdbReceiveDataDTO) {
        if (null== tsdbReceiveDataDTO.getSubBizCount()){
            logger.error("单值模型数据写入失败,原因是Value值缺失:insertDataToTsdbBySingle Error,HandleTsdbServiceImpl#tsdbReceiveDataDTO={}", JSON.toJSONString(tsdbReceiveDataDTO));
            return;
        }
        logger.info("HandleTsdbServiceImpl#insertDataToTsdbBySingle#tsdbReceiveDataDTO={}", JSON.toJSONString(tsdbReceiveDataDTO));
        int timestamp = (int) (System.currentTimeMillis() / 1000);
        Point point = Point.metric(tsdbReceiveMqDataDTO.getMetric())  //默认库,需要修改
                .tag("pay_channel", tsdbReceiveDataDTO.getPayChannel() == null ? StringUtils.EMPTY : tsdbReceiveDataDTO.getPayChannel().toString())  //支付渠道
                .tag("order_type", tsdbReceiveDataDTO.getOrderType() == null ? StringUtils.EMPTY : tsdbReceiveDataDTO.getOrderType().toString())  //订单类型 #
                .tag("order_source", tsdbReceiveDataDTO.getOrderSource())  //订单来源
                .timestamp(timestamp).value(tsdbReceiveDataDTO.getSubBizCount()==null? StringUtils.EMPTY :tsdbReceiveDataDTO.getSubBizCount())
                .build();
        tsdbClient.putSync(point);
    }
最佳实践–度量设计

• 减少时间线数量在TSDB中,唯一决定时间线的有如下因素:
• metric相同
• tags数量相同
• 每一个tag的tagKey和tagValue都相同
• 字段名 (仅在使用多值模型时)
因此,时间线数量的上限等于metric和tag的集合的笛卡尔积(若是多值模型时,还需要加上field的集合)。因此,建议用户在设计时间线时可以尽量减少metric或者tag的变化,以免时间线不断膨胀,超过规格限制。
其中比较容易被忽视的是tagValue,**在设计时间线的tag的tagValue,也应当让同一tag的Value值变化尽可能少。比如对于一个被监控的进程所对应的时间线而言,若将其进程ID作为一个TagValue,则不是一个好的设计,因为同一个被监控进程的进程ID也可能因为进程重启而发生变化;此外,将时间戳设计为TagValue也需要谨慎。**TagValue如果设计得有误,即便metric,TagKey都没有显著变化时,时间线数量也有可能飞速膨胀。
• 减少单个Tag 标签索引时间线量
避免在每一条时间线上都设置一个相同的标签,以免该标签索引的时间线数量过多,影响查询时的性能。
• 减少查询时间线覆盖
如果标签1索引的时间线的集合是标签2的子集,那么查询时可以只使用标签1,以提升查询性能。例如,在上面的例子中,若我们想查询时间线1的数据,有如下两种查询方式:
{“metric”: “cpu”,”tags”: {“site”: “et2”, “ip”: “1.1.1.1”, “app”: “TSDB”}{“metric”: “cpu”,”tags”: {“ip”: “1.1.1.1”}第2中查询方式只使用了1个标签,但是因为”ip=1.1.1.1”这个标签只索引了1条时间线,因此,相比查询1,查询2性能反而是更好的。

接入grafana

1.添加open tsdb插件

在这里插入图片描述

2.授权连接tsdb

在这里插入图片描述

3.添加面板

在这里插入图片描述
在这里插入图片描述

4.配置指标

问题4:接入grafana只显示散点图

这里需要理解下聚合与降采样:
聚合( Aggregation):当同一个度量(Metric)的查询有多条时间线产生(多个指标采集设备),那么为了将空间的多维数据展现为成同一条时间线,需要进行合并计算,例如,当选定了某个城市某个城区的污染指数时,通常将各个环境监测点的指标数据平均值作为最终区域的指标数据,这个计算过程就是空间聚合。
降采样(Downsampling):当查询的时间区间跨度较长而原始数据时间精度较细时,为了满足业务需求的场景、提升查询效率,就会降低数据的查询展现精度,这就叫做降采样,比如按秒采集一年的数据,按照天级别查询展现。

5.最终效果

由于tsdb接入grafana无法使用tsql语句,因此只能通过面板来配。如本例当中配置的是每小时订单量求和(小时降采样)。
在这里插入图片描述
本文涉及阿里云tsdb的基本运用和碰到的问题,内容很浅显,如果你也碰到问题,欢迎留言,若有有高质量文章,欢迎推荐。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值