Spring Boot 集成 Apache IoTDB 詳細(xì)過(guò)程
Apache IoTDB 是一款專為物聯(lián)網(wǎng)場(chǎng)景設(shè)計(jì)的時(shí)序數(shù)據(jù)庫(kù),具備高寫(xiě)入性能、低存儲(chǔ)成本和靈活的查詢能力。本文將詳細(xì)介紹 Spring Boot 項(xiàng)目集成 IoTDB 的完整流程,包括環(huán)境準(zhǔn)備、依賴配置、數(shù)據(jù)讀寫(xiě)操作、連接池優(yōu)化及常見(jiàn)問(wèn)題解決,幫助開(kāi)發(fā)者快速搭建穩(wěn)定的 IoT 數(shù)據(jù)存儲(chǔ)解決方案。
一、集成前準(zhǔn)備
1.1 環(huán)境要求
- JDK 版本:1.8 及以上(IoTDB 1.0 + 推薦 JDK 11)
- Spring Boot 版本:2.3.x ~ 3.2.x(本文以 2.7.10 為例)
- Apache IoTDB 版本:1.2.0(最新穩(wěn)定版,兼容 1.0 + 所有版本)
- Maven/Gradle:項(xiàng)目構(gòu)建工具(本文以 Maven 為例)
1.2 IoTDB 服務(wù)部署
- 下載安裝包:從IoTDB 官網(wǎng)下載對(duì)應(yīng)版本的二進(jìn)制包
- 啟動(dòng)服務(wù):
- Linux/Mac:執(zhí)行
sbin/``start-server.sh - Windows:執(zhí)行
sbin\start-server.bat
- 驗(yàn)證服務(wù):通過(guò)
telnet ``localhost`` 6667或iotdb-cli工具連接,確認(rèn)服務(wù)正常運(yùn)行
二、核心依賴配置
在 Spring Boot 項(xiàng)目的pom.xml中添加 IoTDB 相關(guān)依賴,主要包括 JDBC 驅(qū)動(dòng)和 Spring 數(shù)據(jù)集成支持(可選)。
2.1 基礎(chǔ)依賴(JDBC 方式)
<!-- IoTDB JDBC驅(qū)動(dòng) -->
<dependency>
<groupId>org.apache.iotdb</groupId>
<artifactId>iotdb-jdbc</artifactId>
<version>1.2.0</version>
</dependency>
<!-- Spring Boot JDBC starter(可選,用于簡(jiǎn)化JDBC操作) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 連接池(推薦HikariCP,Spring Boot默認(rèn)集成) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>2.2 高級(jí)依賴(Spring Data JPA/MyBatis)
若需使用 ORM 框架,需額外添加對(duì)應(yīng)依賴(以 MyBatis 為例):
<!-- MyBatis整合Spring Boot -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!-- 分頁(yè)插件(可選) -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>三、配置文件設(shè)置
在application.yml或application.properties中配置 IoTDB 連接信息,核心包括 JDBC URL、賬號(hào)密碼及連接池參數(shù)。
3.1 基礎(chǔ)配置(application.yml)
spring:
datasource:
# IoTDB JDBC URL格式:jdbc:iotdb://<host>:<port>/\[?<params>]
url: jdbc:iotdb://localhost:6667/?enableAutoCreateSchema=true\&timeZone=UTC+8
username: root # 默認(rèn)用戶名
password: root # 默認(rèn)密碼
driver-class-name: org.apache.iotdb.jdbc.IoTDBDriver
# HikariCP連接池配置
hikari:
maximum-pool-size: 10 # 最大連接數(shù)(根據(jù)業(yè)務(wù)調(diào)整)
minimum-idle: 2 # 最小空閑連接數(shù)
idle-timeout: 300000 # 空閑連接超時(shí)時(shí)間(5分鐘)
connection-timeout: 30000 # 連接超時(shí)時(shí)間(30秒)
max-lifetime: 1800000 # 連接最大生命周期(30分鐘)3.2 關(guān)鍵參數(shù)說(shuō)明
| 參數(shù)名 | 說(shuō)明 | 示例值 |
|---|---|---|
enableAutoCreateSchema | 是否自動(dòng)創(chuàng)建時(shí)間序列(新手推薦開(kāi)啟) | true |
timeZone | 時(shí)區(qū)配置(避免時(shí)間戳偏差) | UTC+8/Asia/Shanghai |
fetchSize | 查詢結(jié)果分頁(yè)大?。ù髷?shù)據(jù)量查詢必備) | 1000 |
username/password | IoTDB 認(rèn)證信息(可通過(guò)iotdb-env.sh修改) | root/root |
四、數(shù)據(jù)操作實(shí)現(xiàn)
Spring Boot 集成 IoTDB 支持多種數(shù)據(jù)操作方式,包括原生 JDBC、JdbcTemplate、MyBatis 等,以下分別介紹核心用法。
4.1 原生 JDBC 操作(基礎(chǔ))
通過(guò)DriverManager直接創(chuàng)建連接,適用于簡(jiǎn)單場(chǎng)景:
import org.apache.iotdb.jdbc.IoTDBDriver;
import java.sql.*;
@Service
public class IoTDBNativeService {
// IoTDB連接信息
private static final String URL = "jdbc:iotdb://localhost:6667/";
private static final String USER = "root";
private static final String PASSWORD = "root";
// 寫(xiě)入時(shí)序數(shù)據(jù)(單條)
public void insertSingleData() throws SQLException {
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO root.sensor.demo(timestamp, temperature, humidity) VALUES(?, ?, ?)")) {
// 設(shè)置參數(shù):時(shí)間戳(毫秒級(jí))、溫度、濕度
pstmt.setLong(1, System.currentTimeMillis());
pstmt.setFloat(2, 25.6f);
pstmt.setInt(3, 60);
pstmt.executeUpdate();
System.out.println("數(shù)據(jù)寫(xiě)入成功");
}
}
// 查詢時(shí)序數(shù)據(jù)(時(shí)間范圍)
public void queryDataByTimeRange() throws SQLException {
String sql = "SELECT temperature, humidity " +
"FROM root.sensor.demo " +
"WHERE time >= ? AND time <= ?";
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 設(shè)置查詢時(shí)間范圍(1小時(shí)內(nèi)數(shù)據(jù))
long endTime = System.currentTimeMillis();
long startTime = endTime - 3600 * 1000;
pstmt.setLong(1, startTime);
pstmt.setLong(2, endTime);
// 處理查詢結(jié)果
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
System.out.printf("時(shí)間戳:%d, 溫度:%f, 濕度:%d%n",
rs.getLong("time"), // IoTDB默認(rèn)時(shí)間列名為"time"
rs.getFloat("temperature"),
rs.getInt("humidity"));
}
}
}
}
}4.2 JdbcTemplate 操作(推薦)
借助 Spring Boot 的JdbcTemplate簡(jiǎn)化 JDBC 代碼,減少 try-catch 冗余:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class IoTDBJdbcTemplateService {
@Autowired
private JdbcTemplate jdbcTemplate;
// 批量寫(xiě)入數(shù)據(jù)(高效)
public void batchInsertData(List<Object\[]> dataList) {
String sql = "INSERT INTO root.sensor.demo(timestamp, temperature, humidity) VALUES(?, ?, ?)";
// 批量執(zhí)行(推薦一次批量1000-5000條,平衡性能與內(nèi)存)
jdbcTemplate.batchUpdate(sql, dataList);
System.out.printf("批量寫(xiě)入成功,共%d條數(shù)據(jù)", dataList.size());
}
// 分頁(yè)查詢數(shù)據(jù)
public List<Map<String, Object>> queryDataWithPage(long startTime, long endTime, int pageNum, int pageSize) {
// IoTDB分頁(yè)通過(guò)LIMIT和OFFSET實(shí)現(xiàn)
String sql = String.format(
"SELECT time, temperature, humidity " +
"FROM root.sensor.demo " +
"WHERE time >= ? AND time <= ? " +
"ORDER BY time DESC " +
"LIMIT %d OFFSET %d",
pageSize, (pageNum - 1) * pageSize
);
return jdbcTemplate.queryForList(sql, startTime, endTime);
}
// 創(chuàng)建時(shí)間序列(手動(dòng)模式,enableAutoCreateSchema=false時(shí)需調(diào)用)
public void createTimeSeries() {
String sql = "CREATE TIMESERIES root.sensor.demo.temperature " +
"WITH DATATYPE=FLOAT, ENCODING=RLE, COMPRESSOR=SNAPPY; " +
"CREATE TIMESERIES root.sensor.demo.humidity " +
"WITH DATATYPE=INT32, ENCODING=PLAIN, COMPRESSOR=SNAPPY";
jdbcTemplate.execute(sql);
System.out.println("時(shí)間序列創(chuàng)建成功");
}
}4.3 MyBatis 操作(ORM 方式)
對(duì)于復(fù)雜 SQL 場(chǎng)景,推薦使用 MyBatis,通過(guò) Mapper 接口實(shí)現(xiàn)數(shù)據(jù)操作。
4.3.1 定義 Mapper 接口
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface IoTDBMapper {
// 插入單條數(shù)據(jù)
void insertData(@Param("timestamp") long timestamp,
@Param("temperature") float temperature,
@Param("humidity") int humidity);
// 時(shí)間范圍查詢(帶參數(shù))
List<Map<String, Object>> queryByTimeRange(@Param("startTime") long startTime,
@Param("endTime") long endTime);
// 分頁(yè)查詢(使用PageHelper插件)
List<Map<String, Object>> queryWithPage(@Param("startTime") long startTime,
@Param("endTime") long endTime);
}4.3.2 編寫(xiě) Mapper XML(resources/mapper/IoTDBMapper.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.iotdb.mapper.IoTDBMapper">
<!-- 插入數(shù)據(jù) -->
<insert id="insertData">
INSERT INTO root.sensor.demo(timestamp, temperature, humidity)
VALUES(#{timestamp}, #{temperature}, #{humidity})
</insert>
<!-- 時(shí)間范圍查詢 -->
<select id="queryByTimeRange" resultType="java.util.Map">
SELECT time, temperature, humidity
FROM root.sensor.demo
WHERE time >= #{startTime} AND time <= #{endTime}
ORDER BY time ASC
</select>
<!-- 分頁(yè)查詢(PageHelper自動(dòng)攔截添加LIMIT) -->
<select id="queryWithPage" resultType="java.util.Map">
SELECT time, temperature, humidity
FROM root.sensor.demo
WHERE time >= #{startTime} AND time <= #{endTime}
ORDER BY time DESC
</select>
</mapper>4.3.3 服務(wù)層調(diào)用
import com.example.iotdb.mapper.IoTDBMapper;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class IoTDBMyBatisService {
@Autowired
private IoTDBMapper ioTDBMapper;
// 分頁(yè)查詢(使用PageHelper)
public List<Map<String, Object>> queryWithPageHelper(long startTime, long endTime, int pageNum, int pageSize) {
// 開(kāi)啟分頁(yè)(PageHelper會(huì)自動(dòng)對(duì)下一次查詢生效)
PageHelper.startPage(pageNum, pageSize);
return ioTDBMapper.queryWithPage(startTime, endTime);
}
}五、高級(jí)特性配置
5.1 連接池監(jiān)控
通過(guò) Spring Boot Actuator 監(jiān)控連接池狀態(tài),添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>配置監(jiān)控端點(diǎn)(application.yml):
management:
endpoints:
web:
exposure:
include: hikaricp,health,info # 暴露HikariCP監(jiān)控端點(diǎn)
endpoint:
hikaricp:
enabled: true # 啟用HikariCP監(jiān)控訪問(wèn)http://localhost:8080/actuator/hikaricp即可查看連接池詳情(如活躍連接數(shù)、空閑連接數(shù)等)。
5.2 數(shù)據(jù)分區(qū)與存儲(chǔ)優(yōu)化
IoTDB 支持按時(shí)間分區(qū)(如按天 / 按月),在配置文件中優(yōu)化存儲(chǔ)參數(shù):
# IoTDB服務(wù)端配置(iotdb-engine.properties) storage_group_partition_strategy=MONTH # 存儲(chǔ)組按月份分區(qū) time_partition_interval=86400000 # 時(shí)間分區(qū)間隔(1天,單位:毫秒) compressor=SNAPPY # 全局壓縮算法(SNAPPY/LZ4)
5.3 異常處理與重試機(jī)制
使用 Spring 的@Retryable實(shí)現(xiàn)連接重試,添加依賴:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>在啟動(dòng)類添加@EnableRetry注解,服務(wù)層方法添加重試邏輯:
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
@Service
public class IoTDBRetryService {
// 連接失敗時(shí)重試3次,間隔1秒
@Retryable(value = SQLException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void insertWithRetry(long timestamp, float temperature, int humidity) throws SQLException {
// 數(shù)據(jù)插入邏輯(同4.1/4.2)
}
}六、常見(jiàn)問(wèn)題與排查
6.1 連接超時(shí)問(wèn)題
- 原因 1:IoTDB 服務(wù)未啟動(dòng)或端口被占用
- 排查:執(zhí)行
netstat -tuln | grep 6667查看端口是否被占用,重啟 IoTDB 服務(wù)。
- 排查:執(zhí)行
- 原因 2:防火墻攔截端口
- 解決:開(kāi)放 6667 端口(Linux:
firewall-cmd --add-port=6667/tcp --permanent)。
- 解決:開(kāi)放 6667 端口(Linux:
6.2 時(shí)間序列創(chuàng)建失敗
- 錯(cuò)誤信息:
org.apache.iotdb.rpc.TSStatusCode: PATH_ALREADY_EXISTS- 原因:重復(fù)創(chuàng)建已存在的時(shí)間序列
- 解決:刪除重復(fù)的
CREATE TIMESERIES語(yǔ)句,或開(kāi)啟enableAutoCreateSchema=true自動(dòng)創(chuàng)建。
6.3 批量寫(xiě)入性能低
- 優(yōu)化方向:
- 增大批量寫(xiě)入條數(shù)(建議 1000-5000 條 / 批次)
- 使用
Session客戶端而非 JDBC(IoTDB 提供更高效的 Session API) - 服務(wù)端配置
max_write_batch_size=10000(iotdb-engine.properties)
七、擴(kuò)展:使用 Session API(高性能)
對(duì)于高并發(fā)寫(xiě)入場(chǎng)景,推薦使用 IoTDB 原生 Session API,性能優(yōu)于 JDBC:
import org.apache.iotdb.session.Session;
import org.apache.iotdb.tsfile.write.record.Tablet;
import org.apache.iotdb.tsfile.write.schema.MeasurementSchema;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class IoTDBSessionService {
private Session session;
// 初始化Session連接
public void initSession() throws Exception {
session = new Session("localhost", 6667, "root", "root");
session.open();
// 設(shè)置批量寫(xiě)入?yún)?shù)
session.setBatchSize(5000);
session.setFetchSize(5000);
}
// 批量寫(xiě)入數(shù)據(jù)(Session API 核心優(yōu)勢(shì):低開(kāi)銷、高吞吐量)
public void batchInsertWithSession (List timestamps, List temperatures, List humidities) throws Exception {
// 1. 定義時(shí)間序列路徑(格式:root. 存儲(chǔ)組。設(shè)備。測(cè)量值)
String deviceId = "root.sensor.demo";
// 2. 定義測(cè)量值 schema(數(shù)據(jù)類型、編碼方式)
List measurements = new ArrayList<>();
List schemas = new ArrayList<>();
// 添加溫度測(cè)量值(FLOAT 類型,RLE 編碼適合連續(xù)數(shù)值)
measurements.add ("temperature");
schemas.add (new MeasurementSchema ("temperature", TSDataType.FLOAT, TSEncoding.RLE));
// 添加濕度測(cè)量值(INT32 類型,PLAIN 編碼適合離散數(shù)值)
measurements.add ("humidity");
schemas.add (new MeasurementSchema ("humidity", TSDataType.INT32, TSEncoding.PLAIN));
// 3. 創(chuàng)建 Tablet(IoTDB 批量數(shù)據(jù)載體,比 JDBC Batch 更高效)
Tablet tablet = new Tablet (deviceId, schemas, timestamps.size ());
// 4. 填充數(shù)據(jù)(按列填充,減少 IO 開(kāi)銷)
int row = 0;
for (Long timestamp : timestamps) {
// 設(shè)置時(shí)間戳
tablet.addTimestamp (row, timestamp);
// 設(shè)置溫度值(第 0 列)
tablet.addValue (measurements.get (0), row, temperatures.get (row));
// 設(shè)置濕度值(第 1 列)
tablet.addValue (measurements.get (1), row, humidities.get (row));
row++;
}
// 5. 執(zhí)行批量寫(xiě)入(Session 會(huì)自動(dòng)處理連接復(fù)用和數(shù)據(jù)分片)
session.insertTablet (tablet);
System.out.printf ("Session API 批量寫(xiě)入成功,共 % d 條數(shù)據(jù)", timestamps.size ());
}
// 時(shí)間范圍查詢(Session API 支持更靈活的結(jié)果格式)
public List<Map<String, Object>> queryWithSession (long startTime, long endTime) throws Exception {
List<Map<String, Object>> resultList = new ArrayList<>();
// 1. 定義查詢參數(shù)
String deviceId = "root.sensor.demo";
List measurements = Arrays.asList ("temperature", "humidity");
// 2. 執(zhí)行查詢(返回 ResultSet,支持流式處理)
try (SessionDataSet resultSet = session.executeQuery (deviceId, measurements, startTime, endTime)) {
// 3. 解析查詢結(jié)果
while (resultSet.hasNext ()) {
Map<String, Object> dataMap = new HashMap<>();
// 獲取時(shí)間戳
dataMap.put ("timestamp", resultSet.nextTimestamp ());
// 獲取測(cè)量值(按順序匹配 measurements 列表)
dataMap.put ("temperature", resultSet.nextFloat ());
dataMap.put ("humidity", resultSet.nextInt ());
resultList.add (dataMap);
}
}
return resultList;
}
// 關(guān)閉 Session 連接(建議在服務(wù)銷毀時(shí)調(diào)用)
@PreDestroy
public void closeSession () throws Exception {
if (session != null && session.isOpen ()) {
session.close ();
System.out.println ("Session 連接已關(guān)閉");
}
}
}### 7.1 Session API 與 JDBC 對(duì)比
| 特性 | Session API | JDBC |
|---------------------|--------------------------------------|-------------------------------|
| 性能 | 高(直接基于RPC,無(wú)JDBC協(xié)議開(kāi)銷) | 中(需適配JDBC規(guī)范,額外開(kāi)銷)|
| 批量寫(xiě)入效率 | 極高(Tablet載體,按列存儲(chǔ)) | 中等(Statement Batch,按行處理)|
| 功能完整性 | 專注時(shí)序數(shù)據(jù)操作(寫(xiě)入、查詢、刪除) | 支持標(biāo)準(zhǔn)SQL操作(DDL、DML) |
| 易用性 | 需熟悉IoTDB特有API | 通用SQL語(yǔ)法,學(xué)習(xí)成本低 |
| 適用場(chǎng)景 | 高并發(fā)寫(xiě)入(如設(shè)備實(shí)時(shí)上報(bào)數(shù)據(jù)) | 復(fù)雜查詢、多數(shù)據(jù)源兼容場(chǎng)景 |
## 八、項(xiàng)目實(shí)戰(zhàn):完整業(yè)務(wù)流程示例
以“溫濕度傳感器數(shù)據(jù)采集”為例,整合上述技術(shù)實(shí)現(xiàn)端到端業(yè)務(wù)邏輯。
### 8.1 實(shí)體類定義(封裝傳感器數(shù)據(jù))
\`\`\`java
public class SensorData {
// 設(shè)備ID
private String deviceId;
// 采集時(shí)間戳(毫秒級(jí))
private long timestamp;
// 溫度(℃)
private float temperature;
// 濕度(%RH)
private int humidity;
// 省略getter/setter/構(gòu)造方法
}8.2 業(yè)務(wù)服務(wù)層(整合 Session API 與異常處理)
@Service
public class SensorDataService {
@Autowired
private IoTDBSessionService sessionService;
// 初始化Session(項(xiàng)目啟動(dòng)時(shí)執(zhí)行)
@PostConstruct
public void init() {
try {
sessionService.initSession();
} catch (Exception e) {
throw new RuntimeException("Session初始化失敗", e);
}
}
// 接收傳感器數(shù)據(jù)并批量寫(xiě)入(支持重試)
@Retryable(value = {Exception.class}, maxAttempts = 3, backoff = @Backoff(delay = 2000))
public void batchSaveSensorData(List<SensorData> dataList) throws Exception {
// 1. 數(shù)據(jù)預(yù)處理:按設(shè)備分組(Session API按設(shè)備寫(xiě)入更高效)
Map<String, List<SensorData>> dataGroupByDevice = dataList.stream()
.collect(Collectors.groupingBy(SensorData::getDeviceId));
// 2. 按設(shè)備批量寫(xiě)入
for (Map.Entry<String, List<SensorData>> entry : dataGroupByDevice.entrySet()) {
String deviceId = entry.getKey();
List<SensorData> deviceDataList = entry.getValue();
// 3. 轉(zhuǎn)換為Session API所需格式
List<Long> timestamps = deviceDataList.stream().map(SensorData::getTimestamp).collect(Collectors.toList());
List<Float> temperatures = deviceDataList.stream().map(SensorData::getTemperature).collect(Collectors.toList());
List<Integer> humidities = deviceDataList.stream().map(SensorData::getHumidity).collect(Collectors.toList());
// 4. 調(diào)用Session API寫(xiě)入
sessionService.batchInsertWithSession(timestamps, temperatures, humidities);
}
}
// 查詢指定設(shè)備的歷史數(shù)據(jù)(帶分頁(yè))
public PageInfo<SensorData> querySensorData(String deviceId, long startTime, long endTime, int pageNum, int pageSize) throws Exception {
// 1. 調(diào)用Session API查詢?cè)紨?shù)據(jù)
List<Map<String, Object>> rawDataList = sessionService.queryWithSession(startTime, endTime);
// 2. 數(shù)據(jù)轉(zhuǎn)換與分頁(yè)(手動(dòng)分頁(yè),避免內(nèi)存溢出)
List<SensorData> sensorDataList = rawDataList.stream()
.map(map -> new SensorData(
deviceId,
(Long) map.get("timestamp"),
(Float) map.get("temperature"),
(Integer) map.get("humidity")
)).collect(Collectors.toList());
// 3. 分頁(yè)處理(使用PageHelper工具類)
PageHelper.startPage(pageNum, pageSize);
Page<SensorData> page = new Page<>(sensorDataList);
return new PageInfo<>(page);
}
// 重試失敗回調(diào)
@Recover
public void recover(Exception e) {
System.err.println("數(shù)據(jù)寫(xiě)入重試失敗:" + e.getMessage());
// 此處可添加告警邏輯(如發(fā)送郵件、短信通知運(yùn)維人員)
}
}8.3 控制層(提供 RESTful API)
@RestController
@RequestMapping("/api/sensor")
public class SensorDataController {
@Autowired
private SensorDataService sensorDataService;
// 批量接收傳感器數(shù)據(jù)(POST請(qǐng)求)
@PostMapping("/data/batch")
public ResponseEntity<String> batchSave(@RequestBody List<SensorData> dataList) {
try {
sensorDataService.batchSaveSensorData(dataList);
return ResponseEntity.ok("數(shù)據(jù)接收成功,共" + dataList.size() + "條");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("數(shù)據(jù)處理失?。? + e.getMessage());
}
}
// 查詢歷史數(shù)據(jù)(GET請(qǐng)求)
@GetMapping("/data/history")
public ResponseEntity<PageInfo<SensorData>> queryHistory(
@RequestParam String deviceId,
@RequestParam long startTime,
@RequestParam long endTime,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "20") int pageSize) {
try {
PageInfo<SensorData> pageInfo = sensorDataService.querySensorData(
deviceId, startTime, endTime, pageNum, pageSize);
return ResponseEntity.ok(pageInfo);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(null);
}
}
}九、性能優(yōu)化最佳實(shí)踐
9.1 服務(wù)端優(yōu)化(iotdb-engine.properties)
- 內(nèi)存配置:根據(jù)服務(wù)器內(nèi)存調(diào)整,建議設(shè)置為物理內(nèi)存的 50%-70%
# 堆內(nèi)存大小(根據(jù)實(shí)際情況調(diào)整) jvm_options=-Xms8g -Xmx8g # 緩存大?。ㄌ嵘樵冃阅埽? query_cache_size=1024MB chunk_cache_size=2048MB
- 寫(xiě)入優(yōu)化:減少刷盤(pán)頻率,提升寫(xiě)入吞吐量
# 刷盤(pán)策略(周期性刷盤(pán),單位:毫秒) flush_interval=30000 # 內(nèi)存塊大小(越大寫(xiě)入效率越高,建議16MB-64MB) page_size=32MB
- 分區(qū)優(yōu)化:按業(yè)務(wù)場(chǎng)景選擇分區(qū)粒度
# 時(shí)間分區(qū)策略(MONTH/DAY/HOUR,高頻數(shù)據(jù)建議按小時(shí)分區(qū)) storage_group_partition_strategy=DAY time_partition_interval=86400000
9.2 客戶端優(yōu)化(Spring Boot 項(xiàng)目)
- 連接池優(yōu)化:針對(duì) JDBC 方式
spring:
datasource:
hikari:
maximum-pool-size: 20 # 高頻寫(xiě)入場(chǎng)景可適當(dāng)增大
connection-timeout: 5000 # 縮短連接超時(shí)時(shí)間,快速失敗
idle-timeout: 600000 # 空閑連接超時(shí)10分鐘,減少資源占用- 批量寫(xiě)入策略:
- 單批次數(shù)據(jù)量控制在 1000-5000 條(平衡吞吐量與內(nèi)存占用)
- 使用定時(shí)任務(wù)批量匯總數(shù)據(jù)(如每 5 秒執(zhí)行一次批量寫(xiě)入)
- 查詢優(yōu)化:
- 避免全表掃描,必須指定時(shí)間范圍(
WHERE time >= ? AND time <= ?) - 只查詢需要的測(cè)量值,避免
SELECT * - 大數(shù)據(jù)量查詢使用分頁(yè)(
LIMIT + OFFSET)或流式處理
十、總結(jié)與擴(kuò)展
本文詳細(xì)介紹了 Spring Boot 集成 Apache IoTDB 的完整流程,從基礎(chǔ)的 JDBC 連接到高性能的 Session API,再到實(shí)戰(zhàn)業(yè)務(wù)場(chǎng)景,覆蓋了物聯(lián)網(wǎng)時(shí)序數(shù)據(jù)存儲(chǔ)的核心需求。
10.1 擴(kuò)展方向
- 多節(jié)點(diǎn)部署:IoTDB 支持集群模式,可通過(guò)
session.setNodeUrls()配置多個(gè)服務(wù)節(jié)點(diǎn),實(shí)現(xiàn)負(fù)載均衡與高可用 - 數(shù)據(jù)訂閱:使用 IoTDB 的
ConsumerAPI 實(shí)現(xiàn)數(shù)據(jù)實(shí)時(shí)訂閱(如監(jiān)控異常數(shù)據(jù)并實(shí)時(shí)告警) - 可視化集成:結(jié)合 Grafana(通過(guò) IoTDB 數(shù)據(jù)源插件)實(shí)現(xiàn)時(shí)序數(shù)據(jù)可視化展示
- 數(shù)據(jù)備份與恢復(fù):使用 IoTDB 的
backup工具實(shí)現(xiàn)定時(shí)備份,避免數(shù)據(jù)丟失
10.2 版本兼容性說(shuō)明
- IoTDB 1.0+ 版本與 Spring Boot 2.3.x-3.2.x 完全兼容
- 若使用 Spring Boot 3.x,需確保 JDK 版本為 17+,并升級(jí) IoTDB JDBC 驅(qū)動(dòng)至 1.2.0+
- Session API 在 IoTDB 0.13+ 版本開(kāi)始支持,推薦使用 1.0+ 穩(wěn)定版
通過(guò)本文的指南,開(kāi)發(fā)者可快速搭建穩(wěn)定、高效的物聯(lián)網(wǎng)時(shí)序數(shù)據(jù)存儲(chǔ)系統(tǒng),并根據(jù)業(yè)務(wù)需求選擇合適的技術(shù)方案(JDBC/MyBatis/Session API),同時(shí)通過(guò)優(yōu)化配置充分發(fā)揮 IoTDB 的性能優(yōu)勢(shì)。
到此這篇關(guān)于Spring Boot 集成 Apache IoTDB 詳細(xì)過(guò)程的文章就介紹到這了,更多相關(guān)Spring Boot 集成 Apache IoTDB內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot使用Apache?Tika檢測(cè)敏感信息
- SpringBoot使用Apache?POI實(shí)現(xiàn)導(dǎo)入導(dǎo)出Excel文件
- 使用Apache?POI和SpringBoot實(shí)現(xiàn)Excel文件上傳和解析功能
- SpringBoot集成Apache POI實(shí)現(xiàn)Excel的導(dǎo)入導(dǎo)出
- 詳解Springboot快速搭建跨域API接口的步驟(idea社區(qū)版2023.1.4+apache-maven-3.9.3-bin)
- Springboot swagger配置過(guò)程詳解(idea社區(qū)版2023.1.4+apache-maven-3.9.3-bin)
- SpringBoot+Apache tika實(shí)現(xiàn)文檔內(nèi)容解析的示例詳解
- SpringBoot整合Apache Ignite的實(shí)現(xiàn)
相關(guān)文章
Java中Pattern.compile函數(shù)的使用詳解
這篇文章主要介紹了Java中Pattern.compile函數(shù)的使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java輕松實(shí)現(xiàn)權(quán)限認(rèn)證管理的示例代碼
我們?cè)趯?shí)際開(kāi)發(fā)中經(jīng)常會(huì)進(jìn)行權(quán)限認(rèn)證管理,給不同的人加上對(duì)應(yīng)的角色和權(quán)限,本文將實(shí)現(xiàn)一個(gè)簡(jiǎn)易的權(quán)限驗(yàn)證管理系統(tǒng),感興趣的小伙伴可以了解下2023-12-12
mybatis Interceptor對(duì)UpdateTime自動(dòng)處理的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于使用mybatis Interceptor對(duì)UpdateTime自動(dòng)處理的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧2018-12-12
Java 發(fā)送http請(qǐng)求上傳文件功能實(shí)例
本文通過(guò)實(shí)例代碼給大家介紹了Java 發(fā)送http請(qǐng)求上傳文件功能,需要的朋友參考下吧2017-06-06
Java中關(guān)于Null的9個(gè)解釋(Java Null詳解)
這篇文章主要介紹了Java中關(guān)于Null的9個(gè)解釋(Java Null詳解),本文詳細(xì)講解了Java中Null的9個(gè)相關(guān)知識(shí),需要的朋友可以參考下2015-01-01

