文章目录

  • 一、时序数据库介绍
  • 1.1 时序数据
  • 1.2 时序数据与关系型数据差异
  • 1.3 时序数据库
  • 1.4 时序数据库特征
  • 1.4.1 写入特征
  • 1.4.2 数据查询和分析的特点
  • 1.4.3 数据存储的特点
  • 二、安装influxdb以及基本操作、命令
  • 三、influxdb studio可视化界面
  • 四、influxdb常见问题
  • 五、InfluxDb中的数学运算
  • 数学运算符
  • 1.1 查询中的基本计算
  • 1.2 计算查询中的百分比
  • InfluxDb中的函数
  • InfluxDb中的数据查询语法Group子句
  • 1.GROUP BY tags
  • 2.GROUP BY时间间隔
  • 六、influxdb中的增删改查
  • InfluxDb中的数据查询语法
  • InfluxDb中的数据插入语法
  • InfluxDb中的数据删除语法
  • 七、springboot连接influxdb完成增删查



一、时序数据库介绍

1.1 时序数据

把按照时间戳的大小顺序排列的一系列记录值的数据称为时间序列数据(Time Series Data)。比如,汽车的位置定位,在一段时间内某辆特定汽车的其他属性,包括型号、颜色、车牌号、所有者等都是不变的,但它的位置数据是随着时间变化不断在变化的,那么根据时间确定的位置值及其他属性所组成的一系列数据就是一组时序数据,当我们驾驶汽车开启导航时,就需要根据这一组时序数据判断接下来到达目的地的路线以及存储驾驶记录,在即将到来的无人驾驶中更是必不可少的。

1.2 时序数据与关系型数据差异

  • 最明显的特征是时序数据都存在唯一的时间戳,并且以时间戳大小进行排序,以时间戳作为唯一标识进行区分,而关系型数据通常有其他字段作为标识,比如学生的数据通常使用学号作为唯一标识进行区分。
  • 时序数据并不关心关系,在汽车定位中,我们不需要了解这辆汽车的所有者的其他属性,例如年龄、职业等等,也就不存在对汽车所有者的表的关联。
  • 时序数据的数据量持续呈线性增长,每隔一定时间粒度就会产生新的数据,将会持续产生海量数据,因此数据量庞大。而关系型数据的增长通常不是随着时间持续增长的,比如一所学校的学生数据量在一段时间内都是相对稳定的。
  • 时序数据很少会有更新操作,在某个时刻的测量值产生将不会发生变化,所以几乎不需要对时序数据进行更新。对于关系型数据,则是已存在的数据经常发生更新,比如学生的个人信息,包括年龄、身高等属性。

1.3 时序数据库

时序数据库全称为时间序列数据库。时间序列数据库指主要用于处理带时间标签(按照时间的顺序变化,即时间序列化)的数据,带时间标签的数据也称为时间序列数据。 ----百度百科

1.4 时序数据库特征

1.4.1 写入特征

  • 写入平稳、持续、高并发高吞吐:时序数据的写入是比较平稳的,这点与应用数据不同,应用数据通常与应用的访问量成正比,而应用的访问量通常存在波峰波谷。时序数据的产生通常是以一个固定的时间频率产生,不会受其他因素的制约,其数据生成的速度是相对比较平稳的。
  • 写多读少:时序数据上95%-99%的操作都是写操作,是典型的写多读少的数据。这与其数据特性相关,例如监控数据,你的监控项可能很多,但是你真正去读的可能比较少,通常只会关心几个特定的关键指标或者在特定的场景下才会去读数据。
  • 数据写入无更新:在时间这个维度上,随着时间的推进,每次数据都是新数据,不会存在旧数据的更新,不过不排除人为的对数据做订正。

1.4.2 数据查询和分析的特点

  • 按时间范围读取:通常来说,你不会去关心某个特定点的数据,而是一段时间的数据。
  • 最近的数据被读取的概率高
  • 历史数据粗粒度查询的概率搞

1.4.3 数据存储的特点

  • 数据量大:拿监控数据来举例,如果我们采集的监控数据的时间间隔是1s,那一个监控项每天会产生86400个数据点,若有10000个监控项,则一天就会产生864000000个数据点。在物联网场景下,这个数字会更大。整个数据的规模,是TB甚至是PB级的。
  • 冷热分明:时序数据有非常典型的冷热特征,越是历史的数据,被查询和分析的概率越低。
  • 具有时效性:时序数据具有时效性,数据通常会有一个保存周期,超过这个保存周期的数据可以认为是失效的,可以被回收。一方面是因为越是历史的数据,可利用的价值越低;另一方面是为了节省存储成本,低价值的数据可以被清理。

二、安装influxdb以及基本操作、命令

influxdb在一些语法方面也和mysql非常类似:

# 查看所有数据库
show  databases
# 建库
create database dbname
# 删库
drop database daname
# 切换使用数据库
use dbname
# 查看所有表
show measurements
# 建表+插入数据,无需单独建表,插入数据的同时建表
insert xxx,tag1=1,tag2=www.aaa.com,tag3=2 field1=12i,field2="hhh",field=true
# 删表
drop measurement xxx
#简单查询
select * from xxx  where tag1='1' and field2='hhh'

64bit:🔎https://dl.influxdata.com/influxdb/releases/influxdb-1.7.4_windows_amd64.zip 安装很简单,下载好的压缩包解压一下就行。

三、influxdb studio可视化界面

influxDB+studio安装和使用


四、influxdb常见问题

database:数据库;
measurement:数据库中的表;
points:表里面的一行数据。
Point由时间戳(time)、数据(field)和标签(tags)组成。

Point属性

传统数据库中的概念

time(时间戳)

每个数据记录时间,是数据库中的主索引(会自动生成)

fields(字段、数据)

各种记录值(一般情况下存放的是会随着时间戳的变化而变化的数据)也就是记录的值:温度, 湿度

tags(标签)

各种有索引的属性:地区,海拔

🔺measurement的特殊性
measurement平时使用时需要注意:

  1. measurement无需单独创建,在第一次插入数据时自动创建。
  2. measurement中无数据,表也就不存在了。
  3. measurement没有update语句,无法修改measurement以及tags-keyfields-key名称,实际中如有需要只能删除整个measurement重新插入数据。不过也可以通过select * into newxxx from oldxxx,从旧表中查出数据导入到新表。

🔺关于时间戳(time)

在写入数据点的时候如果没有提供时间戳,InfluxDB会在获取该点时,把本地当前时间分配给该数据点,作为该数据点的时间戳。在influxdb中使用的时间戳是19位,单位是ns(纳秒)

time格式化显示precision

select查出的数据time是一长串时间戳,可以输入precision rfc3339,然后执行select查询,即可显示格式化好的time:

influxdb2 大于 influxdb update_数据


measurement中time时间戳是UTC时间,与东八区少8小时

可以看到显示的time,比当前时间少了8小时,可以在select语句后面指定时区tz('Asia/Shanghai') 使用tz语法时需要配置golang环境

🔎工作日志,error parsing query: unable to find time zone

🔺保留策略retention policy(RP):
主要包括数据保留时间和备份个数两个信息,但是,备份个数只针对于集群机器,对于单点模式不起作用,需要设置为1。retention policy是数据库级别的属性,也是数据结构的一部分。一个database可以有多个保留策略retention policy,但是只能有一个默认retention policy。
select * from xxx默认查询的是默认RP的数据,需指定RP,即select * from RP.MEASUREMENT。 retention policy连同measurement和tag set定义一个series。
当你创建一个database的时候,InfluxDB会自动创建一个叫做autogen的retention policy,autogen表示数据无限存储,不会过期,副本数是1
shard group的duration设为的七天。

🔎influxdb保留策略(retention policy)

🔺tag设计的原因
tag不是必需的字段,但是在你的数据应该尽量使用tag,因为field是没有索引的, tag是有索引的。这意味着对tag的查询更快,tag是存储常用元数据的最佳选择。有无索引,是tag和field最主要的区别,另外是类型,tag value只能存储字符串,field可以存储任何类型。为什么要设计一个tag呢,其实还是为了查询效率,influxdb对tag做了特殊处理,tag有索引,所以tag会查询快,但是索引也会增加数据写入Influxdb的时间,所以tag也不能太多。

🔺不要使用field作为查询条件
字段field是InfluxDB数据结构所必需的一部分, 在InfluxDB中不能没有field。需要注意的是,field是没有索引的。如果使用field value作为过滤条件来查询,这样的查询非常慢,请不要使用field value作为查询条件。
虽然用field作为查询条件语法不会有错的。

五、InfluxDb中的数学运算

influxdb查询语言中,也可以用运算符,例如有一个工资表,由于节假日,需要将某些日的工资乘以2,这时候就可以用乘法来完成。
运算符一多,就应该有运算顺序,数学运算符遵循标准的运算顺序。也就是说,圆括号优先于除法和乘法,而乘除法优先于加法和减法。例如5 / 2 + 3 * 2 = (5 / 2) + (3 * 2)和5 + 2 * 3 - 2 = 5 + (2 * 3) - 2。

数学运算符

🔎InfluxQL数学运算符 InfluxQL允许计算简单的数学方程式,对field字段进行运算

1.1 查询中的基本计算

SELECT语句支持使用基本的数学运算符,例如,+、-、/、*和()等等。

-- 两个field key相加
SELECT field_key1 + field_key2 AS "field_key_sum" FROM "measurement_name" WHERE time < now() - 15m
-- 两个field key相减
SELECT field_key1 - field_key2 AS "field_key_difference" FROM "measurement_name" WHERE time < now() - 15m
-- 分组计算并将它们连接起来
SELECT (field_key1 + field_key2) - (field_key3 + field_key4) AS "some_calculation" FROM "measurement_name" WHERE time < now() - 15m

influxdb2 大于 influxdb update_数据库_02

执行SELECT A+B AS "sumAB" FROM "math_table" WHERE time > now() - 15m

influxdb2 大于 influxdb update_spring boot_03


执行SELECT B-A AS "diffAB" FROM "math_table" WHERE time > now() - 15m

influxdb2 大于 influxdb update_数据_04


执行SELECT (C+D)-(A+B) AS "res" FROM "math_table" WHERE time > now() - 15m 即(4+5)-(2+3)

influxdb2 大于 influxdb update_时序数据库_05

1.2 计算查询中的百分比

使用基本的数学函数,将两个field字段的值相除并将结果乘以100,可以计算出两个field的百分

SELECT (field_key1 / field_key2) * 100 AS "calculated_percentage" FROM "measurement_name" WHERE time < now() - 15m

执行SELECT (A/B) * 100 AS "calculated_percentage" FROM "math_table" WHERE time < now() - 15m即求A/B

influxdb2 大于 influxdb update_spring boot_06

InfluxDb中的函数

🔎InfluxQL函数

InfluxDb中的数据查询语法Group子句

🔎InfluxDb中的数据查询语法Group子句

1.GROUP BY tags

GROUP BY 后面跟用户指定的tags,可以按照tag进行分组。

语法描述:
GROUP BY * 对结果中的所有tag作group by。
GROUP BY <tag_key> 对结果按指定的tag作group by。
GROUP BY <tag_key>,<tag_key> 对结果数据按多个tag作groupby,其中tag key的顺序没所谓。

2.GROUP BY时间间隔

GROUP BY time()返回结果按指定的时间间隔group by。 语法:SELECT <function>(<field_key>) FROM_clause WHERE <time_range> GROUP BY time(<time_interval>),[tag_key] [fill(<fill_option>)]

influxdb2 大于 influxdb update_时序数据库_07


下面的例子用到的示例数据如下:

> SELECT "water_level","location" FROM "h2o_feet" WHERE time >= '2015-08-18T00:00:00Z' AND time <= '2015-08-18T00:30:00Z'

name: h2o_feet(表名)
--------------
time                   water_level   location
2015-08-18T00:00:00Z   8.12          coyote_creek
2015-08-18T00:00:00Z   2.064         santa_monica
2015-08-18T00:06:00Z   8.005         coyote_creek
2015-08-18T00:06:00Z   2.116         santa_monica
2015-08-18T00:12:00Z   7.887         coyote_creek
2015-08-18T00:12:00Z   2.028         santa_monica
2015-08-18T00:18:00Z   7.762         coyote_creek
2015-08-18T00:18:00Z   2.126         santa_monica
2015-08-18T00:24:00Z   7.635         coyote_creek
2015-08-18T00:24:00Z   2.041         santa_monica
2015-08-18T00:30:00Z   7.5           coyote_creek
2015-08-18T00:30:00Z   2.051         santa_monica

时间间隔为12分钟的group by

> SELECT COUNT("water_level") FROM "h2o_feet" WHERE "location"='coyote_creek' AND time >= '2015-08-18T00:00:00Z' AND time <= '2015-08-18T00:30:00Z' GROUP BY time(12m)

name: h2o_feet
--------------
time                   count
2015-08-18T00:00:00Z   2
2015-08-18T00:12:00Z   2
2015-08-18T00:24:00Z   2

该查询使用InfluxQL函数来计算location=coyote_creekwater_level数,并将其分组结果分为12分钟间隔。每个时间戳的结果代表一个12分钟的间隔。 第一个时间戳记的计数涵盖大于2015-08-18T00:00:00Z的原始数据,但小于且不包括2015-08-18T00:12:00Z。第二时间戳的计数涵盖大于2015-08-18T00:12:00Z的原始数据,但小于且不包括2015-08-18T00:24:00Z。

六、influxdb中的增删改查

对于传统关系型数据库,增删改查应该是必备且常用的功能,而 influxdb 常用的只有 insert 和 select ,没有提供 update 语法,虽然有 delete 可以删除数据( delete 语法和 mysql 相似),但是需求不大。

InfluxDb中的数据查询语法

SELECT语句从一个或多个measurement中查询数据。
语法描述:SELECT语句需要一个SELECT子句和一个FROM子句。

SELECT <field_key>[,<field_key>,<tag_key>] FROM <measurement_name>[,<measurement_name>]

🎈SELECT子句:
SELECT子句支持多种指定数据的格式:

  • SELECT * 返回所有的fieldtag
  • SELECT “<field_key>” 返回一个特定的field
  • SELECT “<field_key>”,“<field_key>” 返回多个field
  • SELECT “<field_key>”,“<tag_key>” 返回一个特定的field和一个特定的tag当SELECT子句包含tag时,它必须至少指定一个field。 如果只包含tagkey将返回空结果
  • SELECT “<field_key>”::field,“<tag_key>”::tag 返回一个特定的field和一个特定的tag。::[field | tag]语法指定了标识符的类型,使用这个语法是为了区分具有相同名字的field key和tag key。

示例:

SELECT "level description"::field,"location"::tag,"water_level"::field FROM "h2o_feet"

name: h2o_feet
--------------
time                   level description      location       water_level
2015-08-18T00:00:00Z   below 3 feet           santa_monica   2.064
2015-08-18T00:00:00Z   between 6 and 9 feet   coyote_creek   8.12
[...]
2015-09-18T21:36:00Z   between 3 and 6 feet   santa_monica   5.066
2015-09-18T21:42:00Z   between 3 and 6 feet   santa_monica   4.938

该查询选择了两个field:level description和water_level,和一个tag:location。::[field | tag]语法明确指出了该标识符是field还是tag。当field key和tag key的名字相同时,请使用::[field | tag]来区分它们。大多数情况下,并不需要使用该语法。

除此之外,SELECT子句支持的功能还有:算术运算、函数、转换操作和正则表达式。

🎈FROM子句:
FROM子句支持多种指定measurement的格式:

  • FROM <measurement_name>从一个measurement中返回数据。
  • FROM <measurement_name>,<measurement_name>从多个measurement中返回数据。
  • FROM <database_name>.<retention_policy_name>.<measurement_name>从一个被完全限定的measurement中返回数据。通过明确指定measurement的数据库和保留策略来完全限定一个measurement。
  • FROM <database_name>…<measurement_name>从用户指定的一个数据库并使用默认保留策略的measurement中返回数据。

如果标识符包含除了[A-z,0-9,_]之外的字符,或者以数字开头,又或者是InfluxQL关键字,那么它们必须使用双引号。虽然并不总是需要,建议为标识符(tag,field)都加上双引号。

🔎InfluxDb中的数据查询语法

InfluxDb中的数据插入语法

向measurement名为car插入数据:

insert car,band=BW,color=green count=123i

一条简单的insert语句,却隐藏多个坑:

  • tags之间用逗号分隔,fields之间用逗号分隔,tagsfields之间用空格分隔
  • 除了必要的空格,insert后面的空格和tagsfields分隔空格,不能再有其他空格,否则会出现missing tag value的语法错误。
  • tags都是字符串类型,不需要用双引号括起来;fields中有字符串类型需要用英文双引号括起来,如果不用英文双引号,会报语法错误invalid boolean,会认为是无效的布尔值,因为布尔类型无需加双引号。
  • tags中设置布尔值就是字符串,fields中有布尔值,可用 t , T , true , True , TRUE,f , F , false , False表示。
  • fields中有整型integer需要在数值后面加i,否则会出现is type float, already exists as type integer dropped=1

经测试,插入数据时,不能没有fields:

influxdb2 大于 influxdb update_数据库_08


经测试,插入数据时,只插入fields,可以没有tags,如何只插入fields呢?insert measurement后面空格接上fields

influxdb2 大于 influxdb update_influxdb2 大于_09


一个measurement里可以没有tags,但是不能没有fields。因为tags只是索引,并不是用于存储测量值的,而fields就是存储测量值的。

  • 有fields无tags,只是没有tags索引,还有主索引time,如果只用到time查询条件,可以没有tags,但是实际情况中需要对数据聚合等复杂性查询,就必须要有tags。fields也可以作为查询条件,但是查询性能很低,至于聚合group by后面只能tagstime
  • tagsfields,一般情况tags不用于存储有价值的策略值,只是用于查询条件,没有fields,measurements也就没有意义了。
  • 若一个measurements中只有fields,此时想把某个field改为tags,是无法修改的,只能删表重来,或者select into 从旧表批量导入数据到旧表

InfluxDb中的数据删除语法

使用DELETE语句,将会删除数据库中一个序列的所有数据点,但是它不会从索引中删除序列,并且支持在WHERE子句中使用时间间隔。
DELETE语句采用以下形式,您必须使用FROM子句或WHERE子句,或者两者都使用:

DELETE FROM <measurement_name> WHERE [<tag_key>=‘<tag_value>’] | []

删除measurement h2o_feet中的所有数据:

> DELETE FROM "h2o_feet"

删除measurement h2o_quality中tag randtag等于3对应的所有数据:

> DELETE FROM "h2o_quality" WHERE "randtag" = '3'

删除数据库中发生在2016年1月1日之前的所有数据:

> DELETE WHERE time < '2016-01-01'

成功执行DELETE后,将返回一个空的结果。
关于DELETE,需要注意的事项:

  • 当指定measurement名字时,DELETE支持在FROM子句中使用正则表达式;当指定tag value时,DELETE支持在WHERE子句中使用正则表达式。
  • DELETE不支持在WHERE子句使用field。
  • 如果需要删除发生在now()之后的数据,您必须指定时间段,因为DELETE SERIES默认运行的时间是time < now()。

七、springboot连接influxdb完成增删查

首先加入以下依赖

<dependency>
            <groupId>org.influxdb</groupId>
            <artifactId>influxdb-java</artifactId>
        </dependency>

配置application.properties

spring.influx.url=http://localhost:8086
spring.influx.user=admin
spring.influx.password=admin
spring.influx.database=myfirstdb

写一个Configuration类,在里边配置InfluxDB的bean
autogen表示数据无限存储,不会过期,副本数是1

import org.influxdb.InfluxDB;
import org.influxdb.InfluxDBFactory;
import org.influxdb.dto.BatchPoints;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

@Configuration
public class InfluxDbConfig {

    @Value("${spring.influx.url:''}")
    private String influxDBUrl;

    @Value("${spring.influx.user:''}")
    private String userName;

    @Value("${spring.influx.password:''}")
    private String password;

    @Value("${spring.influx.database:''}")
    private String database;

    // 数据保存策略
    public static String retentionPolicy = "autogen";

    @Bean
    public InfluxDB influxDB(){
        InfluxDB influxDB = InfluxDBFactory.connect(influxDBUrl, userName, password);
        try {
            /**
             * 异步插入:
             * enableBatch这里第一个是point的个数,第二个是时间,单位毫秒
             * point的个数和时间是联合使用的,如果满100条或者1000毫秒
             * 满足任何一个条件就会发送一次写的请求。
             * 如果不需要异步插入则将后面 .enableBatch 部分注释掉即可
             */
            influxDB.setDatabase(database)
                    .enableBatch(100,1000, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            influxDB.setRetentionPolicy(retentionPolicy);
        }
            influxDB.setLogLevel(InfluxDB.LogLevel.BASIC);
        return influxDB;
    }
}

对于数据的插入操作,写一个controller

@Autowired
    private InfluxDB influxDB;

    @Value("${spring.influx.database}")
    private String database;
    
	@GetMapping("/insert1")
    public String testController() {
        int count = (int) (Math.random() * 100);
        System.out.println("---开始插入数据---");
        influxDB.write(Point.measurement("car")
                .time(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
                .tag("color", "red")
                .tag("band", "BYD")
                .addField("count", count)
                .build());
        return "ok";
    }

分页查询操作

@GetMapping("/query1")
    public void test2() {
        System.out.println("---开始查询数据---");
        // InfluxDB支持分页查询,因此可以设置分页查询条件
        int page = 1; // 起始页(从0开始)
        int pageSize = 10;  // 每页条目数
        String pageQuery = " LIMIT " + pageSize + " OFFSET " + (page - 1) * pageSize;
        String queryCondition = "";  //查询条件暂且为空
        // 此处查询所有内容,如果
        String queryCmd = "SELECT * FROM "
                // 要指定从 RetentionPolicyName.measurement中查询指定数据,默认的策略可以不加;
                // + 策略name + "." + measurement
                + "temperature"
                // 添加查询条件(注意查询条件选择tag值,选择field数值会严重拖慢查询速度)
                + queryCondition
                // 查询结果需要按照时间排序
                + " ORDER BY time DESC"
                // 添加分页查询条件
                + pageQuery;

        // 开始查询
        QueryResult queryResult = influxDB.query(new Query(queryCmd, database));
        System.out.println("原始结果为:" + queryResult);

        // 获取查询结果
        List<QueryResult.Result> results = queryResult.getResults();
        if (results == null) {
            return;
        }
        // 多个sql用分号隔开,因本次查询只有一个sql,所以取第一个就行
        QueryResult.Result result = results.get(0);
        List<QueryResult.Series> seriesList = result.getSeries();

        for (QueryResult.Series series : seriesList) {
            if (series == null) {
                return;
            }
            System.out.println("结果数量为:" + (series.getValues() == null ?
                    0 : series.getValues().size()));
            System.out.println("colums ==>> " + series.getColumns());
            System.out.println("tags ==>> " + series.getTags());
            System.out.println("name ==>> " + series.getName());
            System.out.println("values ==>> " + series.getValues());

        }
    }

对于批量插入数据,现在Configuration类中写BatchPoints的bean

@Bean
    public BatchPoints batchPoints(){
        return BatchPoints.database(database)
                .retentionPolicy(retentionPolicy)
                .build();
    }

在controller中注入,然后写批量插入数据方法

@Autowired
    private BatchPoints batchPoints;
    
	@GetMapping("/betch")
    public void run() throws Exception{
        for (int i = 0; i< 1000; i++){
            Thread.sleep(1);
            Point point = Point.measurement("car")
                    .time(System.currentTimeMillis(), TimeUnit.MILLISECONDS)
                    .addField("site",i+"")
                    .tag("color","blue")
                    .tag("band","BM")
                    .build();
            batchPoints.point(point);
        }
        influxDB.write(batchPoints);
    }

删除操作

@GetMapping("del")
    public String delete(){
        String tagkey="\"key1\""; //根据指定tag的key和value删除
        String tagvalue="'3'";
        //delete from maths where "key1"='3' 此处的双引号和单引号不可少
        String sql="delete from maths where " +tagkey+ "="+tagvalue;
        System.out.println(sql);
        influxDBConnect.query(sql);
        return "删除成功";
    }