Spring Boot实战:InfluxDB 2.x简单教程
😄生命不息,写作不止
🔥 继续踏上学习之路,学之分享笔记
👊 总有一天我也能像各位大佬一样
🏆 博客首页 @怒放吧德德 To记录领地 @一个有梦有戏的人
🌝分享学习心得,欢迎指正,大家一起学习成长!
转发请携带作者信息 @怒放吧德德(掘金) @一个有梦有戏的人(CSDN)

前言
InfluxDB 2.x是时序数据库的全新迭代版本,相比1.x重构了底层架构,新增Flux查询语言 、任务调度、数据可视化、细粒度权限管控等核心功能,采用Bucket(数据桶)+Org(组织)+Token(令牌)的鉴权模式,完全替代了1.x的Database+RP+User模式。本文基于SpringBoot 2.7.x/3.x + InfluxDB 2.7.x稳定版,从零搭建项目,提供完整的时序数据读写、批量操作、Flux查询案例,解决2.x版本适配痛点。
核心差异提醒:InfluxDB 2.x不兼容1.x的influxdb-java客户端,必须改用官方influxdb-client-java;查询语法从InfluxQL切换为Flux,数据存储单元从Database变为Bucket。
本次案例代码仓库:gitee.com/liyongde/ja...
1 环境准备
- 开发环境:JDK 8+、Maven 3.6+、IntelliJ IDEA
- 框架版本:SpringBoot 2.7.18(兼容JDK8,3.x需适配Jakarta注解)
- InfluxDB版本:2.7.1(官方Docker/本地安装均可)
- 核心依赖:influxdb-client-java(InfluxDB 2.x官方Java客户端)
1.1 安装 InfluxDB2.x
本次安装为了快捷,使用了 docker 安装。
shell
# 拉取镜像
docker pull influxdb:2.1.1
运行容器
java
docker run --name influxdb -p 8086:8086 --restart always -e DOCKER_INFLUXDB_INIT_USERNAME=admin -e DOCKER_INFLUXDB_INIT_PASSWORD=sc@123456 --privileged=true -v /home/influxdb/data:/var/lib/influxdb2 -td influxdb:2.1.1
## -p 8086:8086 映射influxdb默认端口
## --restart always 开机自启
## -e DOCKER_INFLUXDB_INIT_USERNAME 设置登录用户名
## -e DOCKER_INFLUXDB_INIT_PASSWORD 设置登录密码(8位及以上)
## --privileged=true 对主机系统资源更深层次的访问权限
## -v /home/influxdb/data:/var/lib/influxdb2 映射数据存储路径
访问Web控制台:http://localhost:8086 一开始进入会显示 get start 的按钮然后初始化,具体安装见附录[1]。
1.2 InfluxDB 2.x 初始化准备

- 启动InfluxDB 2.x服务,访问Web控制台:
http://localhost:8086 - 完成初始化:设置用户名、密码、组织名(Org)、数据桶(Bucket) ,生成All-Access Token
- 记录关键信息:Org名称、Bucket名称、All-Access Token(后续配置必填)
2 SpringBoot项目搭建(2.x专属配置)
2.1 引入Maven依赖
剔除1.x客户端,引入InfluxDB 2.x官方客户端,保留SpringBoot基础依赖:
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-influxdb2-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-influxdb2-demo</name>
<description>SpringBoot整合InfluxDB 2.x实战案例</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- SpringBoot Web核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- InfluxDB 2.x 官方Java客户端 -->
<dependency>
<groupId>com.influxdb</groupId>
<artifactId>influxdb-client-java</artifactId>
<version>6.11.0</version>
</dependency>
<!-- 日期工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.30</version>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 编写InfluxDB 2.x配置文件
在application.yml中配置2.x专属连接参数,替换为自己的Token、Org、Bucket:
yaml
spring:
application:
name: springboot-influxdb2-demo
# InfluxDB 2.x 核心配置
influx:
# 服务地址
url: http://127.0.0.1:8086
# 初始化生成的All-Access令牌
token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 组织名称
org: my-org
# 数据桶名称(替代1.x的database)
bucket: monitor-bucket
2.3 封装InfluxDB 2.x配置类
初始化InfluxDBClient客户端,注入WriteApi(写入)、QueryApi(查询),交由Spring容器管理:
java
@Configuration
public class InfluxDB2Config {
@Value("${influx.url}")
private String url;
@Value("${influx.token}")
private String token;
@Value("${influx.org}")
private String org;
@Value("${influx.bucket}")
private String bucket;
/**
* 初始化InfluxDB 2.x客户端
* 修复:通过OkHttpClient配置超时,替代不存在的set超时方法
*/
@Bean
public InfluxDBClient influxDBClient() {
// 创建客户端,传入自定义OkHttp配置
InfluxDBClient client = InfluxDBClientFactory.create(
url,
token.toCharArray(),
org,
bucket
);
// 测试连接
client.ping();
System.out.println("InfluxDB 2.x 连接成功");
return client;
}
/**
* 写入API(开启批量写入,提升性能)
*/
@Bean
public WriteApi writeApi(InfluxDBClient client) {
// 批量写入配置:缓冲1000条、500ms刷新、最大5000条
WriteOptions writeOptions = WriteOptions.builder()
.batchSize(1000)
.flushInterval(500)
.bufferLimit(5000)
.build();
return client.makeWriteApi(writeOptions);
}
/**
* 查询API
*/
@Bean
public QueryApi queryApi(InfluxDBClient client) {
return client.getQueryApi();
}
/**
* 项目关闭时销毁客户端
*/
@PreDestroy
public void destroy() {
influxDBClient().close();
}
}
3 核心业务代码实现
3.1 时序数据实体类
本次通过一个简单存储 PLC 信号值的案例,通过注解映射Measurement、Tag、Field,适配2.x客户端写入规则:
java
/**
* plc信号实体
* @Author: liyongde
* @Date: 2026/3/24 10:53
* @Modified By:
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Measurement(name = "plc_signal")
public class PlcSignal {
/**
* 设备名称(Tag索引)
*/
@Column(tag = true, name = "device")
private String device;
/**
* 信号点位/标签(Tag索引)
*/
@Column(tag = true, name = "tag")
private String tag;
/**
* 信号值(Field,存变化数据)
*/
@Column(name = "value")
private String value;
/**
* 变化时间戳(InfluxDB自动维护)
*/
@Column(timestamp = true)
private Instant changeTime;
}
在 2.x 版本中,Measurement 不需要手动创建,运行的时候会自动生成。
3.2 InfluxDB 2.x工具类
封装单条/批量写入、Flux查询通用方法,简化业务层调用:
java
/**
* 工具类
* @Author: liyongde
* @Date: 2026/3/24 10:43
* @Modified By:
*/
@Component
public class InfluxDB2Util {
@Resource
private WriteApi writeApi;
@Resource
private QueryApi queryApi;
/**
* 单条写入PLC信号
*/
public void insertOne(String bucket, String org, PlcSignal data) {
writeApi.writeMeasurement(bucket, org, WritePrecision.NS, data);
}
/**
* PLC专用Flux查询结果转换
*/
public List<PlcSignal> queryPlcFlux(String flux) {
List<FluxTable> tables = queryApi.query(flux);
return tables.stream()
.flatMap(table -> table.getRecords().stream())
.map(this::convertToPlcEntity)
.collect(Collectors.toList());
}
/**
* 执行统计查询,返回总数
* @param flux Flux查询语句
* @return 统计结果
*/
public long queryCount(String flux) {
List<FluxTable> tables = queryApi.query(flux);
if (tables.isEmpty()) {
return 0L;
}
// 获取count结果
return tables.stream()
.flatMap(table -> table.getRecords().stream())
.mapToLong(record -> {
Object value = record.getValue();
if (value instanceof Number) {
return ((Number) value).longValue();
}
return 0L;
})
.sum();
}
/**
* Flux结果转PLC实体
*/
private PlcSignal convertToPlcEntity(FluxRecord record) {
return PlcSignal.builder()
.device(record.getValueByKey("device").toString())
.tag(record.getValueByKey("tag").toString())
.value(record.getValueByKey("value").toString())
.changeTime(record.getTime())
.build();
}
}
这里比较麻烦的是需要针对不同的实体进行转换,这个应该是可以自行封装套件来实现自动匹配转换。
3.3 Service层实现
实现数据新增、批量插入、IP查询、全量查询核心业务:
java
/**
*
* @Author: liyongde
* @Date: 2026/3/24 10:54
* @Modified By:
*/
@Service
public class PlcSignalService {
@Value("${influx.bucket}")
private String bucket;
@Value("${influx.org}")
private String org;
@Resource
private InfluxDB2Util influxDB2Util;
/**
* 本地缓存:存储device+tag对应的最新value,LRU缓存避免内存溢出
* key: device_tag value: 最新信号值
*/
private final LRUCache<String, String> signalCache = CacheUtil.newLRUCache(1000);
/**
* 存储PLC变化信号(核心方法:仅存变化值)
* @param device 设备名
* @param tag 信号点位
* @param newValue 新信号值
* @return 存储结果 true-已存储 false-无变化未存储
*/
public boolean savePlcChangeSignal(String device, String tag, String newValue) {
// 1. 构建缓存key
String cacheKey = device + "_" + tag;
// 2. 获取历史最新值
String oldValue = signalCache.get(cacheKey);
// 3. 判断是否变化:无历史值/值不一致则写入
if (oldValue == null || !oldValue.equals(newValue)) {
PlcSignal plcSignal = PlcSignal.builder()
.device(device)
.tag(tag)
.value(newValue)
.changeTime(Instant.now())
.build();
// 写入InfluxDB
influxDB2Util.insertOne(bucket, org, plcSignal);
// 更新缓存
signalCache.put(cacheKey, newValue);
return true;
}
// 值无变化,不写入
return false;
}
/**
* 查询指定设备+标签的历史变化记录
* @param device 设备名
* @param tag 信号点位
* @return 历史变化列表(按时间倒序)
*/
public List<PlcSignal> getPlcSignalHistory(String device, String tag) {
// Flux查询语句:查询24小时内指定设备、标签的所有变化数据
String flux = String.format("from(bucket: \"%s\")\n" +
" |> range(start: -24h)\n" +
" |> filter(fn: (r) => r._measurement == \"plc_signal\" and r.device == \"%s\" and r.tag == \"%s\")\n" +
" |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n" +
" |> sort(columns: [\"_time\"], desc: true)",
bucket, device, tag);
return influxDB2Util.queryPlcFlux(flux);
}
/**
* 分页查询指定设备+标签的历史变化记录
* @param device 设备名
* @param tag 信号点位
* @param page 页码(从1开始)
* @param size 每页大小
* @return 历史变化列表(按时间倒序)
*/
public List<PlcSignal> getPlcSignalHistoryByPage(String device, String tag, int page, int size) {
// 计算偏移量
int offset = (page - 1) * size;
// Flux查询语句:分页查询24小时内指定设备、标签的变化数据
String flux = String.format("from(bucket: \"%s\")\n" +
" |> range(start: -24h)\n" +
" |> filter(fn: (r) => r._measurement == \"plc_signal\" and r.device == \"%s\" and r.tag == \"%s\")\n" +
" |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n" +
" |> sort(columns: [\"_time\"], desc: true)\n" +
" |> limit(n: %d, offset: %d)",
bucket, device, tag, size, offset);
return influxDB2Util.queryPlcFlux(flux);
}
/**
* 分页查询指定设备+标签的历史变化记录(支持自定义时间范围)
* @param device 设备名
* @param tag 信号点位
* @param startTime 开始时间(如:-7d, -1h, 2024-03-20T00:00:00Z)
* @param page 页码(从1开始)
* @param size 每页大小
* @return 历史变化列表(按时间倒序)
*/
public List<PlcSignal> getPlcSignalHistoryByPageWithTimeRange(String device, String tag, String startTime, int page, int size) {
// 计算偏移量
int offset = (page - 1) * size;
// Flux查询语句:分页查询指定时间范围内设备、标签的变化数据
String flux = String.format("from(bucket: \"%s\")\n" +
" |> range(start: %s)\n" +
" |> filter(fn: (r) => r._measurement == \"plc_signal\" and r.device == \"%s\" and r.tag == \"%s\")\n" +
" |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n" +
" |> sort(columns: [\"_time\"], desc: true)\n" +
" |> limit(n: %d, offset: %d)",
bucket, startTime, device, tag, size, offset);
return influxDB2Util.queryPlcFlux(flux);
}
/**
* 分页查询指定设备+标签的历史变化记录(返回分页结果对象)
* @param device 设备名
* @param tag 信号点位
* @param page 页码(从1开始)
* @param size 每页大小
* @return 分页结果对象
*/
public PageResult<PlcSignal> getPlcSignalHistoryPage(String device, String tag, int page, int size) {
return getPlcSignalHistoryPageWithTimeRange(device, tag, "-24h", page, size);
}
/**
* 分页查询指定设备+标签的历史变化记录(支持自定义时间范围,返回分页结果对象)
* @param device 设备名
* @param tag 信号点位
* @param startTime 开始时间(如:-7d, -1h, 2024-03-20T00:00:00Z)
* @param page 页码(从1开始)
* @param size 每页大小
* @return 分页结果对象
*/
public PageResult<PlcSignal> getPlcSignalHistoryPageWithTimeRange(String device, String tag, String startTime, int page, int size) {
// 1. 查询总数
long total = getPlcSignalCount(device, tag, startTime);
if (total == 0) {
return PageResult.empty(page, size);
}
// 2. 查询当前页数据
List<PlcSignal> records = getPlcSignalHistoryByPageWithTimeRange(device, tag, startTime, page, size);
// 3. 构建分页结果
return PageResult.of(records, total, page, size);
}
/**
* 统计指定设备+标签在指定时间范围内的记录总数
* @param device 设备名
* @param tag 信号点位
* @param startTime 开始时间
* @return 记录总数
*/
private long getPlcSignalCount(String device, String tag, String startTime) {
// Flux查询语句:统计记录总数
String countFlux = String.format("from(bucket: \"%s\")\n" +
" |> range(start: %s)\n" +
" |> filter(fn: (r) => r._measurement == \"plc_signal\" and r.device == \"%s\" and r.tag == \"%s\")\n" +
" |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n" +
" |> count(column: \"_time\")",
bucket, startTime, device, tag);
// 使用专门的统计方法
return influxDB2Util.queryCount(countFlux);
}
}
3.4 Controller层接口
提供RESTful接口,方便测试数据读写功能:
java
@RestController
@RequestMapping("/influx2/plc")
public class PlcSignalController {
@Resource
private PlcSignalService plcSignalService;
/**
* 接收PLC信号并存储(仅存变化)
* 入参: {"device":"press","tag":"mode","value":"1"}
*/
@PostMapping("/save")
public Map<String, Object> savePlcSignal(@RequestBody Map<String, String> plcData) {
Map<String, Object> result = new HashMap<>();
String device = plcData.get("device");
String tag = plcData.get("tag");
String value = plcData.get("value");
// 调用存储方法
boolean saved = plcSignalService.savePlcChangeSignal(device, tag, value);
result.put("code", 200);
result.put("data", saved);
result.put("msg", saved ? "信号变化,已存储" : "信号无变化,未存储");
return result;
}
/**
* 查询PLC信号历史变化
*/
@GetMapping("/history")
public List<PlcSignal> getPlcHistory(@RequestParam String device, @RequestParam String tag) {
return plcSignalService.getPlcSignalHistory(device, tag);
}
/**
* 分页查询PLC信号历史变化(24小时内)
* @param device 设备名
* @param tag 信号点位
* @param page 页码(默认1)
* @param size 每页大小(默认10)
* @return 分页结果
*/
@GetMapping("/history/page")
public PageResult<PlcSignal> getPlcHistoryPage(
@RequestParam String device,
@RequestParam String tag,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
return plcSignalService.getPlcSignalHistoryPage(device, tag, page, size);
}
/**
* 分页查询PLC信号历史变化(自定义时间范围)
* @param device 设备名
* @param tag 信号点位
* @param startTime 开始时间(如:-7d, -1h, 2024-03-20T00:00:00Z)
* @param page 页码(默认1)
* @param size 每页大小(默认10)
* @return 分页结果
*/
@GetMapping("/history/page/range")
public PageResult<PlcSignal> getPlcHistoryPageWithRange(
@RequestParam String device,
@RequestParam String tag,
@RequestParam String startTime,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
return plcSignalService.getPlcSignalHistoryPageWithTimeRange(device, tag, startTime, page, size);
}
}
5 项目启动与测试
5.1 启动类
java
/**
* 启动类
* @Author: liyongde
* @Date: 2026/3/24 10:46
* @Modified By:
*/
@SpringBootApplication
public class InfluxDB2Application {
public static void main(String[] args) {
SpringApplication.run(InfluxDB2Application.class, args);
}
}
启动之后,同过 postman 或者 apifox 进行测试。
6 总结
本文完整实现了SpringBoot与InfluxDB 2.x的整合,覆盖客户端初始化、注解映射、批量写入、Flux查询全流程,代码可直接部署运行。相比1.x,2.x功能更强大、生态更完善,适配物联网、监控、日志等海量时序数据场景,后续可扩展定时任务、数据聚合、可视化报表等高级功能。
7 附录
1\]: 参考安装: [Docker:docker部署influxdb时序数据库 - 怒吼的萝卜 - 博客园](https://link.juejin.cn?target=https%3A%2F%2Fwww.cnblogs.com%2Fnhdlb%2Fp%2F17960707 "https://www.cnblogs.com/nhdlb/p/17960707") *** ** * ** *** *转发请携带作者信息* **@怒放吧德德 @一个有梦有戏的人** 持续创作很不容易,作者将以尽可能的详细把所学知识分享各位开发者,一起进步一起学习。**转载请携带链接,转载到微信公众号请勿选择原创,谢谢!** 👍创作不易,如有错误请指正,感谢观看!记得点赞哦!👍 谢谢支持!