Ubuntu 22.04 安装 Apache TsFile 指南
在 Ubuntu 22.04 服务器从零部署 Apache TsFile 2.4.0:APT 安装 OpenJDK 17 与 Maven,创建工程引入依赖,完成时序数据写入、查询与堆参数验证,并实测 Python SDK 读写闭环。
Ubuntu 22.04 安装 Apache TsFile 指南
一、环境信息
本文所有命令均在以下环境实测通过,输出均为真实回显摘录。
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS(内核 6.8.0-138-generic) |
| CPU / 内存 | 12 vCPU / 32 GiB(可用约 19 GiB) |
| 磁盘 | 根分区可用 ≥ 50 GB |
| JDK | openjdk-17-jdk-headless 17.0.20.1+1-1~22.04 |
| 构建工具 | Apache Maven 3.6.3(APT 源版本,满足 tsfile 2.4.0 工程要求) |
| Python | 3.10.12 / pip 22.0.2(用于 Python SDK 实测) |
| TsFile 版本 | org.apache.tsfile:tsfile 2.4.0;Python 包 tsfile 2.4.0 |
| 网络 | 可访问 repo1.maven.org 与 pypi.org |
二、前置条件
- root 权限或 sudo 账号;本文统一以 root 演示,实验口令 q1w2e3r4 仅用于本地验证场景。
- 服务器可访问 Maven Central(
https://repo1.maven.org/maven2/)与 PyPI(https://pypi.org/simple/)。 - 磁盘预留约 2 GB:JDK 约 300 MB、Maven 约 10 MB、本地仓库 tsfile 及依赖约 40 MB、Python 轮子约 30 MB。
三、版本选型
3.1 版本线与 JDK 要求
Maven Central 上 org.apache.tsfile:tsfile 当前可选 1.0.0 至 2.4.0 共 17 个版本。各版本线对 JDK 的要求(实测自 Maven Central 父 POM 的 maven.compiler.release):
| 版本线 | 最新版本 | 编译目标 | 说明 |
|---|---|---|---|
| 1.x | 1.1.4 | 8 | JDK 8+ |
| 2.0.x – 2.3.x | 2.3.1 | 8 | JDK 8+ |
| 2.4.x | 2.4.0 | 17 | 强制 JDK 17+ |
本文选 2.4.0 + OpenJDK 17。如需兼容 JDK 8 运行时,固定使用 2.3.1 及以下。
3.2 与 IoTDB 的版本对应关系
生成文件最终交给 IoTDB 加载时,tsfile 版本应与 IoTDB 对齐(对应关系实测自 IoTDB 父 POM):
| IoTDB 版本 | 依赖的 tsfile 版本 |
|---|---|
| 1.3.4 | 1.1.1 |
| 2.0.2 | 2.0.2 |
若只做独立文件读写或对接自研系统,直接选最新稳定版 2.4.0。
四、部署步骤
4.1 安装 JDK 17 与 Maven
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y openjdk-17-jdk-headless maven
实测说明:安装过程会连带装入 openjdk-11 相关包(作为 APT 依赖链被拉入),但默认 java 替代项指向 17,mvn -version 亦运行在 17 上,无需处理;如默认指向被改动,用 update-alternatives --config java 交互选择 17 即可。
验证:
java -version
mvn -version | head -3
实测回显:
openjdk version "17.0.20.1" 2026-08-18
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
编辑 /root/.bashrc 追加环境变量:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
export MAVEN_OPTS="-Xms256m -Xmx512m"
执行 source /root/.bashrc 生效。MAVEN_OPTS 控制 Maven 进程(含 exec:java 运行的业务代码)的堆大小,后续统一使用 256–512 MB。
4.2 创建工程并引入依赖
mkdir -p /root/tsfile-demo/src/main/java/demo
cd /root/tsfile-demo
创建 pom.xml,完整内容如下(tsfile.version 抽为属性便于后续切换版本):
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>tsfile-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<tsfile.version>2.4.0</tsfile.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.tsfile</groupId>
<artifactId>tsfile</artifactId>
<version>${tsfile.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</build>
</project>
拉取依赖并确认依赖树:
mvn -q dependency:resolve
mvn dependency:tree 2>&1 | sed -n '/com.example:tsfile-demo/,/BUILD/p'
实测回显:
com.example:tsfile-demo:jar:1.0.0
+- org.apache.tsfile:tsfile:jar:2.4.0:compile
| +- org.apache.tsfile:common:jar:2.4.0:compile
| +- com.github.luben:zstd-jni:jar:1.5.6-3:compile
| +- org.xerial.snappy:snappy-java:jar:1.1.10.5:compile
| +- at.yawk.lz4:lz4-java:jar:1.10.1:compile
| +- org.tukaani:xz:jar:1.10:compile
| +- org.slf4j:slf4j-api:jar:2.0.17:compile
| \- org.antlr:antlr4-runtime:jar:4.13.2:compile
\- org.slf4j:slf4j-simple:jar:2.0.17:compile
4.3 编写写入程序
创建 /root/tsfile-demo/src/main/java/demo/WriteDemo.java。要点:2.x 的 TSDataType 在 org.apache.tsfile.enums 包;设备与测点通过 Schema.registerMeasurementGroup 注册;Tablet 每批不超过 1024 行,行数用 setRowSize 设置:
package demo;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.tsfile.common.conf.TSFileConfig;
import org.apache.tsfile.common.conf.TSFileDescriptor;
import org.apache.tsfile.enums.TSDataType;
import org.apache.tsfile.file.metadata.enums.CompressionType;
import org.apache.tsfile.file.metadata.enums.TSEncoding;
import org.apache.tsfile.write.TsFileWriter;
import org.apache.tsfile.write.record.Tablet;
import org.apache.tsfile.write.schema.IMeasurementSchema;
import org.apache.tsfile.write.schema.MeasurementSchema;
import org.apache.tsfile.write.schema.Schema;
public class WriteDemo {
public static void main(String[] args) throws Exception {
TSFileConfig config = TSFileDescriptor.getInstance().getConfig();
config.setPageSizeInByte(64 * 1024);
config.setGroupSizeInByte(8 * 1024 * 1024);
File f = new File("demo.tsfile");
if (f.exists()) {
f.delete();
}
Schema schema = new Schema();
String device1 = "root.demo.device1";
String device2 = "root.demo.device2";
List<IMeasurementSchema> m1 = new ArrayList<>();
m1.add(new MeasurementSchema("temperature", TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY));
m1.add(new MeasurementSchema("humidity", TSDataType.DOUBLE, TSEncoding.GORILLA, CompressionType.ZSTD));
m1.add(new MeasurementSchema("status", TSDataType.TEXT, TSEncoding.PLAIN, CompressionType.UNCOMPRESSED));
schema.registerMeasurementGroup(new org.apache.tsfile.read.common.Path(device1),
new org.apache.tsfile.utils.MeasurementGroup(false, m1));
List<IMeasurementSchema> m2 = new ArrayList<>();
m2.add(new MeasurementSchema("voltage", TSDataType.INT32, TSEncoding.TS_2DIFF, CompressionType.LZ4));
m2.add(new MeasurementSchema("current", TSDataType.INT64, TSEncoding.TS_2DIFF, CompressionType.GZIP));
schema.registerMeasurementGroup(new org.apache.tsfile.read.common.Path(device2),
new org.apache.tsfile.utils.MeasurementGroup(false, m2));
long start = System.currentTimeMillis();
try (TsFileWriter writer = new TsFileWriter(f, schema, config)) {
long baseTime = 1700000000000L;
int batch = 500;
for (long t = 0; t < 2000; t += batch) {
Tablet tab1 = new Tablet(device1, m1);
for (int r = 0; r < batch; r++) {
long ts = baseTime + (t + r) * 1000L;
tab1.addTimestamp(r, ts);
tab1.addValue("temperature", r, 20.0f + (t + r) % 10);
tab1.addValue("humidity", r, 45.0 + (t + r) % 5);
tab1.addValue("status", r, ((t + r) % 2 == 0) ? "OK" : "WARN");
}
tab1.setRowSize(batch);
writer.writeTree(tab1);
Tablet tab2 = new Tablet(device2, m2);
for (int r = 0; r < batch; r++) {
long ts = baseTime + (t + r) * 1000L;
tab2.addTimestamp(r, ts);
tab2.addValue("voltage", r, (int) (220 + (t + r) % 3));
tab2.addValue("current", r, 1000L + (t + r) % 7);
}
tab2.setRowSize(batch);
writer.writeTree(tab2);
}
writer.flush();
}
System.out.println("写入完成: " + f.getAbsolutePath()
+ " 大小=" + f.length() + " 字节");
}
}
4.4 编写读取程序
创建 /root/tsfile-demo/src/main/java/demo/ReadDemo.java:
package demo;
import java.util.List;
import org.apache.tsfile.file.metadata.IDeviceID;
import org.apache.tsfile.read.TsFileReader;
import org.apache.tsfile.read.TsFileSequenceReader;
import org.apache.tsfile.read.common.Field;
import org.apache.tsfile.read.common.Path;
import org.apache.tsfile.read.common.RowRecord;
import org.apache.tsfile.read.expression.IExpression;
import org.apache.tsfile.read.expression.QueryExpression;
import org.apache.tsfile.read.expression.impl.BinaryExpression;
import org.apache.tsfile.read.expression.impl.GlobalTimeExpression;
import org.apache.tsfile.read.filter.factory.TimeFilterApi;
import org.apache.tsfile.read.query.dataset.QueryDataSet;
public class ReadDemo {
public static void main(String[] args) throws Exception {
String file = "demo.tsfile";
TsFileSequenceReader seqReader = new TsFileSequenceReader(file);
System.out.println("== 设备列表 ==");
for (IDeviceID dev : seqReader.getAllDevices()) {
System.out.println("device: " + dev);
}
seqReader.close();
TsFileSequenceReader reader = new TsFileSequenceReader(file);
try (TsFileReader tsFileReader = new TsFileReader(reader)) {
QueryExpression query = QueryExpression.create()
.addSelectedPath(new Path("root.demo.device1", "temperature", true))
.addSelectedPath(new Path("root.demo.device1", "humidity", true));
long start = 1700000000000L;
IExpression timeFilter = BinaryExpression.and(
new GlobalTimeExpression(TimeFilterApi.gtEq(start)),
new GlobalTimeExpression(TimeFilterApi.lt(start + 3000)));
query.setExpression(timeFilter);
QueryDataSet dataSet = tsFileReader.query(query);
int rows = 0;
while (dataSet.hasNext() && rows < 3) {
RowRecord record = dataSet.next();
StringBuilder sb = new StringBuilder();
sb.append(record.getTimestamp());
for (Field field : record.getFields()) {
sb.append(" | ").append(field.getDataType()).append(":")
.append(field.getObjectValue(field.getDataType()));
}
System.out.println(sb);
rows++;
}
System.out.println("共命中 " + rows + " 行(本次仅展示 3 行)");
}
}
}
4.5 运行与验证
cd /root/tsfile-demo
export MAVEN_OPTS="-Xms256m -Xmx512m -Dfile.encoding=UTF-8"
mvn -q compile exec:java -Dexec.mainClass=demo.WriteDemo
mvn -q compile exec:java -Dexec.mainClass=demo.ReadDemo
实测回显(写入):
写入完成: /root/tsfile-demo/demo.tsfile 大小=9984 字节, 耗时=125ms
实测回显(读取):
device: root.demo.device1
device: root.demo.device2
1700000000000 | FLOAT:20.0 | DOUBLE:45.0
1700000001000 | FLOAT:21.0 | DOUBLE:46.0
1700000002000 | FLOAT:22.0 | DOUBLE:47.0
共命中 3 行(本次仅展示 3 行)
文件魔数校验(首尾均为 TsFile 字符串):
head -c 6 demo.tsfile && echo && tail -c 6 demo.tsfile && echo
实测回显:
TsFile
TsFile
4.6 JVM 堆参数验证
写入程序运行期间另开一个会话:
ps -ef | grep [j]ava
实测回显(节选,-Xms256m -Xmx512m 生效):
root 7582 1 33 12:11 ? 00:00:04 /usr/bin/java -Xms256m -Xmx512m -classpath /usr/share/maven/boot/plexus-classworlds-2.x.jar -Dclassworlds.conf=/usr/share/maven/bin/m2.conf ... org.codehaus.plexus.classworlds.launcher.Launcher -q exec:java -Dexec.mainClass=demo.HeapHolder
jps -v 回显:
7582 Launcher -Xms256m -Xmx512m -Dclassworlds.conf=/usr/share/maven/bin/m2.conf ...
配合验证程序输出 Runtime.getRuntime().maxMemory() 实测为 maxMemory=512MB,与堆上限一致。
4.7 Python SDK 实测(可选)
Ubuntu 22.04 自带 Python 3.10,PyPI 上有独立的 tsfile 包(当前最新 2.4.0,与 Java 版本同步发布):
apt-get install -y python3-pip
pip3 install tsfile
pip3 show tsfile | head -2
实测回显:
Name: tsfile
Version: 2.4.0
Summary: TsFile Python
创建 /root/py_demo.py(Python 端 Tablet 无需手动设置行数;树模型写入前必须 set_table_name 指定目标设备,否则抛 DeviceNotExistError):
# -*- coding: utf-8 -*-
import os
from tsfile import TsFileWriter, TsFileReader, DeviceSchema
from tsfile import TimeseriesSchema, Tablet, TSDataType
path = "/tmp/py_demo.tsfile"
if os.path.exists(path):
os.remove(path)
w = TsFileWriter(path)
ts_list = [
TimeseriesSchema("temp", TSDataType.FLOAT),
TimeseriesSchema("hum", TSDataType.DOUBLE),
]
w.register_device(DeviceSchema("root.py.device1", ts_list))
tablet = Tablet(["temp", "hum"], [TSDataType.FLOAT, TSDataType.DOUBLE])
tablet.set_table_name("root.py.device1")
for i in range(100):
tablet.add_timestamp(i, 1700000000000 + i * 1000)
tablet.add_value_by_name("temp", i, 20.0 + i % 5)
tablet.add_value_by_name("hum", i, 50.0 + i % 3)
w.write_tablet(tablet)
w.close()
print("写入完成, 大小:", os.path.getsize(path), "字节")
r = TsFileReader(path)
print("设备:", list(r.get_all_devices()))
rs = r.query_tree_by_row(["root.py.device1"], ["temp", "hum"], 0, 5)
print("结果集列:", rs.get_result_column_info())
while rs.next():
print("temp =", rs.get_value_by_name("temp"), ", hum =", rs.get_value_by_name("hum"))
rs.close()
r.close()
运行 python3 /root/py_demo.py,实测回显:
写入完成, 大小: 1646 字节
设备: [DeviceID(path='root.py.device1', table_name='root.py', segments=('root.py', 'device1'))]
结果集列: {'time': <TSDataType.INT64: 2>, 'root.py.device1.temp': <TSDataType.FLOAT: 3>, 'root.py.device1.hum': <TSDataType.DOUBLE: 4>}
temp = 20.0 , hum = 50.0
temp = 21.0 , hum = 51.0
temp = 22.0 , hum = 52.0
temp = 23.0 , hum = 50.0
temp = 24.0 , hum = 51.0
补充说明:Python 端未显式指定编码/压缩时,实测默认使用 GORILLA 编码 + LZ4 压缩(get_all_timeseries_schemas 回显 TimeseriesSchema(temp, FLOAT, GORILLA, LZ4))。
五、配置说明
- JVM 堆:统一通过
MAVEN_OPTS(Maven/exec:java 场景)或java -Xms256m -Xmx512m(打包直跑场景)限定在 256–512 MB,修改位置与验证方式见 4.1 与 4.6 节。 - 写端文件参数:代码内经
TSFileDescriptor.getInstance().getConfig()的 setter 修改;或把iotdb-system.properties放到 classpath(如src/main/resources/)覆盖默认值,键名如page_size_in_byte、group_size_in_byte、compressor、float_precision等,详见《Apache TsFile 配置参数详解》。 - 日志绑定:tsfile 仅依赖 slf4j-api,需自行加入实现包(本文用 slf4j-simple 2.0.17),否则日志静默。
- 字符集:Maven 控制台输出中文乱码时,在
MAVEN_OPTS中追加-Dfile.encoding=UTF-8。
六、升级与多版本管理
- 切版本只需修改 pom 属性
<tsfile.version>,例如改为1.1.4后执行mvn -q dependency:resolve(实测可正常解析 1.1.4)。 - 降级到 2.3.1 及以下需把
maven.compiler.source/target改为 8。 - 2.4.0 相对旧版的显性 API 差异:
TSDataType包迁移至org.apache.tsfile.enums;Tablet行数方法为setRowSize;新增 v4 表模型 API(org.apache.tsfile.write.v4.TsFileWriterBuilder)。 - 本地仓库冲突时删除
~/.m2/repository/org/apache/tsfile对应版本目录后重新拉取。
七、注意事项
- JDK 与版本线必须匹配:2.4.0 强制 JDK 17+,低版本 JDK 编译会报 class 版本错误。
Tablet默认 1024 行上限,超限抛ArrayIndexOutOfBoundsException(实测复现),务必分批。- 无 Schema 构造的
TsFileWriter写入未注册设备会抛NoDeviceException(实测复现)。 - Python 端与 Java 端生成的文件互相可读(同为 TsFile 格式),但 Python 端默认编码/压缩与 Java 端默认(SNAPPY 系)不同,混用场景建议显式指定。
- 对接 IoTDB 联调时,实验环境可直接使用默认账号 root、密码 q1w2e3r4。
- PyPI 上的
apache-iotdb是 IoTDB 客户端包,独立 TsFile 读写请安装tsfile包,二者用途不同。
八、常见问题
Q1:mvn -version 显示的 Java 版本不是 17?
A:系统存在多版本 JDK 时,执行 update-alternatives --config java 选择 java-17 对应项,并确认 JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 已写入 /root/.bashrc。
Q2:运行类时报 NoClassDefFoundError?
A:多见于 java -cp 的通配符 * 未加引号,被 shell 展开后 classpath 被截断。正确写法:java -cp 'target/tsfile-demo-1.0.0.jar:target/dependency/*' demo.ReadDemo。
Q3:exec:java 跑中文输出乱码?
A:在 MAVEN_OPTS 追加 -Dfile.encoding=UTF-8,或改用打包后 java -Dfile.encoding=UTF-8 -cp ... 直跑。
Q4:Python 安装 tsfile 失败或没有 2.4.0?
A:pip 版本过旧(如 9.x)无法读取新轮子元数据,先升级 pip3 install --upgrade pip;确认系统 Python ≥ 3.8。Python SDK 为可选组件,核心链路以 Java SDK 为准。
Q5:写入抛 NoDeviceException: No device for root.bench.d1?
A:TsFileWriter 未注册该设备。先构造 Schema 并 registerMeasurementGroup,再以 new TsFileWriter(file, schema, config) 创建写入器。
九、卸载方式
- 删除工程与本地仓库缓存:
rm -rf /root/tsfile-demo /root/py_demo.py
rm -rf /root/.m2/repository/org/apache/tsfile
pip3 uninstall -y tsfile
- 卸载 APT 包:
apt-get remove -y --purge maven openjdk-17-jdk-headless
apt-get autoremove -y
- 清理环境变量:删除
/root/.bashrc中JAVA_HOME、MAVEN_OPTS相关行。
十、参考链接
- TsFile 官网:https://tsfile.apache.org/
- TsFile GitHub 仓库:https://github.com/apache/tsfile
- Maven Central:https://central.sonatype.com/artifact/org.apache.tsfile/tsfile
- Maven Central 版本元数据:https://repo1.maven.org/maven2/org/apache/tsfile/tsfile/maven-metadata.xml
- PyPI tsfile 包:https://pypi.org/project/tsfile/
- IoTDB 官方文档(TsFile 章节):https://iotdb.apache.org/zh/UserGuide/latest/API/Programming-Java-Native-SDK.html