时序数据库全称为时间序列数据库。时间序列数据库指主要用于处理带时间标签(按照时间的顺序变化,即时间序列化)的数据,带时间标签的数据也称为时间序列数据。
时间序列数据主要由电力行业、化工行业、气象行业、地理信息等各类型实时监测、检查与分析设备所采集、产生的数据,这些工业数据的典型特点是:产生频率快(每一个监测点一秒钟内可产生多条数据)、严重依赖于采集时间(每一条数据均要求对应唯一的时间)、测点多信息量大(常规的实时监测系统均有成千上万的监测点,监测点每秒钟都产生数据,每天产生几十GB的数据量)。基于时间序列数据的特点,关系型数据库无法满足对时间序列数据的有效存储与处理,因此迫切需要一种专门针对时间序列数据来做优化的数据库系统,即时间序列数据库。
InfluxDB是一个开源的、高性能的时序型数据库,在时序型数据库DB-Engines Ranking上排名第一。
InfluxDB是一个由InfluxData开发的开源时序型数据。它由Go写成,着力于高性能地查询与存储时序型数据。
名称 | 说明 |
organization | 组织 |
Member | 用户,可设置权限 |
API TOKEN | 调用API时使用的token |
bucket | 数据桶(数据库) |
measurement | 数据表 |
point | 数据点,表示单条数据记录,point由时间戳(time)、数据(field)、标签(tag)三类字段组成 |
retention policy | 数据保留策略,可以定义数据保留的时长,每个数据库可以有多个数据保留策略,但只能有一个默认策略 |
time | 代表每条数据的时间字段,是measurement中的数据主键,因此time字段具有索引属性。一条point只能有一个time |
field | 代表各种数据的字段,例如气温、压力、股价等。field字段没有索引属性,一条point可以包括多个field |
tag | 代表各类非数据字段,例如设备编码、地区、姓名等。tag字段有索引属性,一条point可以包括多个tag(tag只能为字符串类型) |
InfluxDB与常用的关系型数据库(MySQL)的概念对比:
MySQL | InfluxDB | |
数据库 | database | bucket |
表名 | table | measurement |
记录 | rows | point |
字段 | columns | time+tag+field |
本人使用的是influxDB V2.3.0版本,想下载的朋友点这里:influxDB V2.3.0 镜像包
docker load -i influx.tar.gz >/dev/null
docker run -d --name influxdb \
-p 8086:8086 \
--restart=always \
-v /mnt/influxdb/data:/var/lib/influxdb2 \
-v /etc/localtime:/etc/localtime \
influxdb >/dev/null
接下来是重点喽!!!
(1)influxDB 1.x 版本,配置文件在 etc/influxdb/influxdb.conf,而到了 2.x 版本,进入容器中,通过执行 influxd print-config 命令查看 influxdb 的默认配置。当然也可以手动创建一个名为 config.*的文件,config支持json,toml,yaml格式,将config.*文件放到 etc/influxdb2/文件夹下,启动容器时修改环境变量,指定配置文件读取的路径 --env INFLUXD_CONFIG_PATH=/etc/influxdb2,influxdb启动时会自动检测这个文件。同样可以进入容器,执行 influxd print-config 查看你自定义的配置是否生效;
docker run -d --name influxdb \
-p 8086:8086 \
--restart=always \
--env INFLUXD_CONFIG_PATH=/etc/influxdb2 \
-v /opt/mountdir/influxdb/config/:/etc/influxdb2 \
-v /mnt/influxdb/data:/var/lib/influxdb2 \
-v /etc/localtime:/etc/localtime \
influxdb >/dev/null
com.influxdb
influxdb-client-java
3.1.0
org.jetbrains.kotlin
kotlin-stdlib
1.3.70
spring:
influx:
ip: 192.3.1.21
port: 8086
user: admin
password: admin
database: text
token: oTC_PvFCAnojh8xjX7Rz53B2O9ogneggDWNTZ76VkWS9fgveUODgpOFiFvmoDLoxK7vuSNPstA0SJOWKbvlLsw==
【注】该 token 值来源:安装 InfluxDB 后,浏览器登录 http://ip:8086 ,找到 [API Tokens]
--> [ admin's Token] 中复制出来;
@Component
@Slf4j
@Data
public class InfluxDbConfig {
@Value("${spring.influx.ip:''}")
private String ip;
@Value("${spring.influx.port:''}")
private Integer port;
@Value("${spring.influx.user:''}")
private String userName;
@Value("${spring.influx.password:''}")
private String password;
@Value("${spring.influx.database:''}")
private String database;
@Value("${spring.influx.token}")
private String token;
public static final String MY_BUCKET = "my_bucket";
public static final String ORG_NAME = "my_org";
public static final int TIME_OUT = 60;
public static String ORGANIZATION_ID= "";
public static final ConnectionPool CONNECTION_POOL = new ConnectionPool(10, 3L, TimeUnit.MINUTES);
public static OkHttpClient getHttpClient() {
return new OkHttpClient().newBuilder()
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.connectionPool(CONNECTION_POOL)
.addInterceptor(new NetInterceptor()).build();
}
/**
* 创建连接
*
* @return
*/
@Bean
public InfluxDBClient influxDBClient() {
InfluxDBClientOptions influxDBClientOptions = new InfluxDBClientOptions.Builder()
.url(getInfluxDBUrl(ip, port))
.authenticateToken(token.toCharArray())
.bucket(ORG_NAME)
.okHttpClient(getHttpClient().newBuilder()).build();
InfluxDBClient influxDBClient = InfluxDBClientFactory.create(influxDBClientOptions);
if (checkHealth(influxDBClient)) {
log.info("【influxdb】connect influxdb success!");
}
return influxDBClient;
}
/**
* 组合 influxdb 的 URL
*
* @param ip
* @param port
* @return
*/
public String getInfluxDBUrl(String ip, Integer port) {
StringBuilder stringBuilder = new StringBuilder();
return stringBuilder.append("http://").append(ip).append(":").append(port).toString();
}
/**
* 检查健康
*
* @param influxDBClient
* @return
*/
public boolean checkHealth(InfluxDBClient influxDBClient) {
HealthCheck health = influxDBClient.health();
return "pass".equals(health.getStatus().getValue());
}
/**
* 获取 organizationId
*
* @param influxDBClient
* @param orgName
* @return
*/
public static String getOrganizationId(InfluxDBClient influxDBClient, String orgName) {
List organizations = influxDBClient.getOrganizationsApi().findOrganizations();
String orgId = "";
for (Organization organization : organizations) {
if (organization.getName().equals(orgName)) {
orgId = organization.getId();
}
}
return orgId;
}
}
(1)创建bucket
public BaseResult createDataBase(InfluxDBClient influxDBClient) {
// 这里先按名称查询 MY_BUCKET 数据库,如果存在就不创建
Bucket bucket = influxDBClient.getBucketsApi().findBucketByName(MY_BUCKET);
if (ObjectUtils.isEmpty(bucket)) {
// 创建 bucket 并设置数据保留策略为1年
bucket = influxDBClient.getBucketsApi().createBucket(MY_BUCKET, new BucketRetentionRules().everySeconds(3600 * 24 * 365), ORGANIZATION_ID);
if (ObjectUtils.isEmpty(bucket)) {
return BaseResult.fail("influxdb create measurement failed……");
}
}
return BaseResult.success();
}
(2)写入数据
/**
* 数据插入
*
* @param bucket 数据库名称
* @param orgName 组织名称
* @param measurement 表名
* @param tag 索引字段
* @param fields 字段
* @param time 查询事件
*/
public void insert(String bucket, String orgName, String measurement, String tag, Map fields, long time) {
Point point = Point.measurement(measurement);
point.addTag("tag", tag);
point.addFields(fields);
// 纳秒值
point.time(TimeUnit.MILLISECONDS.toNanos(time), WritePrecision.NS);
writeApi.writePoint(bucket, ORGANIZATION_ID, point);
}
(3)数据查询,得到List
/**
* 数据查询
*
* @param bucket
* @param orgName
* @param measurement
* @param flux
* @return
*/
public static List query(String bucket, String orgName, String measurement, String flux) {
// 构建FLUX语句
List fluxTables = new ArrayList<>();
try {
String organizationId = getOrganizationId(influxDBClient, orgName);
fluxTables = influxDBClient.getQueryApi().query(flux, organizationId);
} catch (Exception e) {
log.error("【influxdb】 query influxdb data failed:", e);
}
return fluxTables;
}
Flux 是 InfluxData 的功能性数据脚本语言,设计用于查询、分析和处理数据,它是InfluxQL 和其他类似 SQL 的查询语言的替代品。
1、from() :选择数据源 bucket
from(bucket: "hrecord_bucket")
2、range():限定时间范围
// 配合InfluxDB客户端中的时间控件使用
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
//查询具体时间段
|> range(start: 2022-09-26T01:14:00.000Z, stop: 2022-09-30T01:14:00.000Z)
// 查询过去一小时
|> range(start: -1h)
3.filter():条件筛选
// 筛选数据表
|> filter(fn: (r) => r["_measurement"] == "tbl_hrecord")
//筛选车牌号
|> filter(fn: (r) => r["plate_code"] == "京FGS002")
4. sort():排序
// 按照时间降序排列,desc:false 为升序
|> sort(columns:["_time"], desc: true)
5.limit():限制返回结果条数,可用于分页查询
//查询前十条
|> limit(n: 10, offset: 0)
6.pivot():将列值转为行
|> pivot(columnKey: ["_field"], rowKey: ["recordId","_time"], valueColumn: "_value")
7.group():分组
// 按车牌号码和车牌颜色分组
|> group(columns:["plate_code","plate_color"] , mode: "by")
8.更多查看:官方文档:Flux的基本使用