Apache TsFile 快速入门与集成示例

tsfile 2.4.0 完整快速入门工程:pom 全文、schema 定义、多设备写入、条件查询与表模型示例,附编码压缩对比实测、Python SDK 示例及 IoTDB、Flume、Flink 集成说明。

Apache TsFileApache TsFile2026/10/12

Apache TsFile 快速入门与集成示例

一、版本与环境

本文基于 tsfile 2.4.0(Maven Central 最新稳定版,强制 JDK 17+)实测,宿主环境 OpenJDK 17 + Maven 3.6.3/3.9.16,Ubuntu 22.04 与 CentOS 8 双端验证结果一致。若需对接 IoTDB 落库,请先确认版本对应关系:IoTDB 1.3.4 依赖 tsfile 1.1.1、IoTDB 2.0.2 依赖 tsfile 2.0.2(均实测自双方 Maven 父 POM)。

二、完整快速入门工程

2.1 pom.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 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>

依赖说明:tsfile 2.4.0 传递引入 common-2.4.0、zstd-jni 1.5.6-3、snappy-java 1.1.10.5、lz4-java 1.10.1、xz 1.10、slf4j-api 2.0.17、antlr4-runtime 4.13.2(实测 mvn dependency:tree 回显);slf4j-simple 仅做日志绑定,可换成业务侧已有实现。

2.2 schema 定义

树模型下按「设备 → 测点」组织 schema。要点:2.x 的 TSDataType 在 org.apache.tsfile.enums 包;Schema.registerMeasurementGroup(Path, MeasurementGroup) 注册一组测点,MeasurementGroup 第一个参数表示是否对齐序列;每个 MeasurementSchema 可独立指定编码与压缩:

Schema schema = new Schema();

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 Path("root.demo.device1"),
    new 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 Path("root.demo.device2"),
    new MeasurementGroup(false, m2));

类型与编码的合法组合可运行期查询 TSEncoding.TYPE_SUPPORTED_ENCODINGS(如 FLOAT 支持 GORILLA/CHIMP/SPRINTZ/RLE/PLAIN/RLBE/TS_2DIFF/GORILLA_V1;TEXT 仅 PLAIN/DICTIONARY)。

2.3 写入代码

完整类 demo.WriteDemo:写入两个设备共 5 个测点、每设备 2000 行、每批 500 行。注意 Tablet 默认容量 1024 行,行数用 setRowSize 设置(2.4.0 无 setRowNumber 方法,实测编译报 cannot find symbol):

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() + " 字节");
  }
}

2.4 读取代码

完整类 demo.ReadDemo:TsFileSequenceReader 负责文件元数据(设备、测点类型);TsFileReader.query(QueryExpression) 负责条件查询,时间过滤用 TimeFilterApi 组合 GlobalTimeExpression(实测 2.4.0 中 TimeFilterApi.gtEq/lt 返回类型即 Filter 子类,可直接包装):

package demo;

import java.util.List;
import org.apache.tsfile.enums.TSDataType;
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("== 设备列表 ==");
    List<IDeviceID> devices = seqReader.getAllDevices();
    for (IDeviceID dev : devices) {
      System.out.println("device: " + dev);
    }
    System.out.println("== 设备测点(类型) ==");
    for (IDeviceID dev : devices) {
      seqReader.getMeasurement(dev).forEach((m, t) ->
          System.out.println(dev + " -> " + m + " : " + t));
    }
    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);
      System.out.println("== 查询结果 ==");
      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 行)");
    }
  }
}

2.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
== 设备测点(类型) ==
root.demo.device1 -> temperature : FLOAT
root.demo.device1 -> humidity : DOUBLE
root.demo.device1 -> status : TEXT
root.demo.device2 -> current : INT64
root.demo.device2 -> voltage : INT32
== 查询结果(root.demo.device1 temperature/humidity, 3 秒窗口) ==
1700000000000 | FLOAT:20.0 | DOUBLE:45.0
1700000001000 | FLOAT:21.0 | DOUBLE:46.0
1700000002000 | FLOAT:22.0 | DOUBLE:47.0
共命中 3 行(本次仅展示 3 行)

Ubuntu 22.04 与 CentOS 8 两端回显一致(CentOS 端耗时 141ms,文件同为 9984 字节)。文件魔数校验(Ubuntu 端 od -c 实测):首 10 字节 T s F i l e 004 \0 002 022,尾 10 字节 \0 \0 \0 302 T s F i l e。

三、表模型写入(v4 API)

tsfile 2.4.0 提供了新的表模型写入 API(org.apache.tsfile.write.v4 包),面向「表 + 标签列 + 物理量列」的结构化场景,实测可用:

package demo;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.tsfile.enums.ColumnCategory;
import org.apache.tsfile.enums.TSDataType;
import org.apache.tsfile.file.metadata.ColumnSchema;
import org.apache.tsfile.file.metadata.ColumnSchemaBuilder;
import org.apache.tsfile.file.metadata.TableSchema;
import org.apache.tsfile.write.record.Tablet;
import org.apache.tsfile.write.v4.ITsFileWriter;
import org.apache.tsfile.write.v4.TsFileWriterBuilder;

public class TableWriteDemo {

  public static void main(String[] args) throws Exception {
    File f = new File("demo_table.tsfile");
    if (f.exists()) {
      f.delete();
    }

    List<ColumnSchema> columns = new ArrayList<>();
    columns.add(new ColumnSchemaBuilder().name("region")
        .dataType(TSDataType.TEXT).category(ColumnCategory.TAG).build());
    columns.add(new ColumnSchemaBuilder().name("temperature")
        .dataType(TSDataType.FLOAT).category(ColumnCategory.FIELD).build());
    columns.add(new ColumnSchemaBuilder().name("humidity")
        .dataType(TSDataType.DOUBLE).category(ColumnCategory.FIELD).build());
    TableSchema tableSchema = new TableSchema("sensor", columns);

    try (ITsFileWriter writer = new TsFileWriterBuilder()
        .file(f)
        .tableSchema(tableSchema)
        .memoryThreshold(8 * 1024 * 1024)
        .build()) {

      List<String> names = new ArrayList<>();
      names.add("region");
      names.add("temperature");
      names.add("humidity");
      List<TSDataType> types = new ArrayList<>();
      types.add(TSDataType.TEXT);
      types.add(TSDataType.FLOAT);
      types.add(TSDataType.DOUBLE);
      Tablet tablet = new Tablet(names, types);

      long base = 1700000000000L;
      for (int r = 0; r < 1000; r++) {
        tablet.addTimestamp(r, base + r * 1000L);
        tablet.addValue("region", r, "north");
        tablet.addValue("temperature", r, 20.0f + r % 5);
        tablet.addValue("humidity", r, 60.0 + r % 3);
      }
      tablet.setRowSize(1000);
      writer.write(tablet);
    }
    System.out.println("表模型写入完成: " + f.getAbsolutePath()
        + " 大小=" + f.length() + " 字节");
  }
}

实测回显:

表模型写入完成: /root/tsfile-demo/demo_table.tsfile 大小=673 字节

注意:表模型 Tablet 用 List<String> 列名 + List<TSDataType> 构造,表归属由 builder 的 tableSchema 承担;树模型 Tablet(String 设备名 + List<IMeasurementSchema>)则用 writeTree 提交,两条路径不要混用。

四、编码与压缩对比实测

同一 20000 点 float 正弦序列(root.bench.d1,毫秒时间戳,间隔 1 秒),实测各 encoder × compressor 组合的文件体积(字节)。注意单条 Tablet 限 1024 行,长序列必须分批(实测超限抛 ArrayIndexOutOfBoundsException: Index 1024):

encoder\compressorUNCOMPRESSEDSNAPPYGZIPLZ4ZSTD
PLAIN8414081504703068182669179
RLE3672834092335083422032960
GORILLA7411271476643857175963723
TS_2DIFF264781438610006139968780

选型结论(实测数据支撑):

  1. 光滑缓变浮点序列:TS_2DIFF + ZSTD 体积最小(本例 8780 字节,约为 PLAIN + UNCOMPRESSED 的 1/10),GZIP 次之但 CPU 更贵。
  2. 快速波动浮点:GORILLA 系(含 CHIMP)更稳,压缩/解压开销低。
  3. 整型遥测:TS_2DIFF 几乎总是优于 RLE;开关量/枚举用 RLE 或 BITMAP。
  4. 文本:仅 PLAIN/DICTIONARY 可选,高重复文本用 DICTIONARY。
  5. 压缩器层面:ZSTD 压缩率最好,SNAPPY/LZ4 压缩速度最快,与主流实践一致。

五、Python SDK 实测示例

PyPI 提供独立 tsfile 包(当前 2.4.0,与 Java 同步发布),实测环境 Ubuntu 22.04 + Python 3.10.12 + pip 22.0.2:

pip3 install tsfile    # 实测装到 /usr/local/lib/python3.10/dist-packages/tsfile

完整读写示例 /root/py_demo.py(三个实测要点:树模型写入前必须 set_table_name 指定目标设备否则抛 DeviceNotExistError;Python 端 Tablet 无需手动设行数,内部游标自增;树模型查询用 query_tree_by_row 而不是不存在的 query 方法):

# -*- 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()

实测回显:

写入完成, 大小: 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 结果仅在 Ubuntu 22.04(Python 3.10 / pip 22)实测通过。CentOS 8 仓库自带的 Python 3.6 + pip 9.0.3 实测无法安装(No matching distribution found for tsfile),CentOS 场景请以 Java SDK 为主,或自行升级 Python 3.8+ 后再装。

另实测 Python 端默认编码/压缩为 GORILLA + LZ4(get_all_timeseries_schemas 回显 TimeseriesSchema(temp, FLOAT, GORILLA, LZ4)),与 Java 端默认不同,跨语言混用建议显式指定。

六、扩展集成说明

6.1 与 Apache IoTDB 集成

TsFile 即 IoTDB 的底层文件格式:本工程生成的文件可直接被 IoTDB 的 load 工具加载,或经 IoTDB Java 原生 Session 写入后由 IoTDB 统一管理。对接 IoTDB 服务端联调时,实验环境使用其默认超级账号 root、密码 q1w2e3r4 即可完成验证。版本选择务必参考第一章对应关系表(IoTDB 1.3.x ↔ tsfile 1.x;IoTDB 2.0.x ↔ tsfile 2.0.x);用 2.4.0 生成的文件建议仅在独立链路使用,避免与旧版 IoTDB 混用。

6.2 Flume 连接器

IoTDB 官方发行包(iotdb-flume-sink 等)提供把 Flume Event 中的时序数据写入 IoTDB/TsFile 的通道能力,属于 IoTDB 侧组件而非 tsfile 库本身。集成思路:自定义 Source/Interceptor 把采集数据转为「设备名 + 测点 + 时间戳」结构,再由 Sink 落盘。由于 tsfile 2.4.0 与 IoTDB 1.x 的 Flume 插件分属不同版本线,生产集成前需先统一版本。

6.3 Hadoop 集成

IoTDB 发行包内含 iotdb-hadoop 模块,提供 TsFile 的 HDFS 读写 InputFormat/OutputFormat 适配,可在 MapReduce/Spark 作业中直接扫描 TsFile。自研场景的轻量替代:tsfile 库本身可读写 HDFS 路径(TSFileConfig 中含 dfs 相关开关,如 dfsHaAutomaticFailoverEnabled 默认 true),配合 Hadoop FileSystem API 封装即可。

IoTDB 官方提供 iotdb-flink 连接器(Source 读取 TsFile/IoTDB、Sink 写入)。以独立 tsfile 库对接 Flink 的常见模式:RichSinkFunction 内持有 TsFileWriter,checkPoint 时 close 触发文件完整落盘;读方向在 SourceFunction 中用 TsFileSequenceReader 顺序扫描后发往下游。注意两点实测结论:写端 Tablet 按批提交(≤1024 行/批)配合 checkPoint 频率控制内存;setMemoryThreshold 可在缓冲接近上限时触发自动 flush。

以上集成组件均属 IoTDB 发行物,本文未逐一实测其联调链路,仅给出架构性说明与实测边界。

七、JVM 堆验证

统一按 256–512 MB 堆档位运行本工程:

  • Maven exec:java 场景:export MAVEN_OPTS="-Xms256m -Xmx512m"(写入 ~/.bashrc 持久化)。
  • 打包直跑场景:java -Xms256m -Xmx512m -cp 'target/tsfile-demo-1.0.0.jar:target/dependency/*' demo.WriteDemo。

实测验证(Ubuntu 22.04,程序驻留期间执行 ps -ef | grep [j]ava):

root  7582  1 33 12:11 ?  00:00:04 /usr/bin/java -Xms256m -Xmx512m -classpath /usr/share/maven/boot/plexus-classworlds-2.x.jar ... 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。CentOS 8 + Maven 3.9.16 端 ps 回显一致可见 -Xms256m -Xmx512m(另有 JDK 17 的 --enable-native-access=ALL-UNNAMED 参数)。

八、常见问题

Q1:写入抛 ArrayIndexOutOfBoundsException: Index 1024?

A:Tablet 默认容量 1024 行。要么按批写满即调 writeTree 后 tablet.reset() 复用,要么构造时指定更大容量(实测复现与修复均通过)。

Q2:写入抛 NoDeviceException: No device for root.bench.d1?

A:用无 Schema 的 new TsFileWriter(File) 构造时未注册设备。先构建 Schema 并 registerMeasurementGroup,再以 new TsFileWriter(file, schema, config) 创建(实测复现)。

Q3:编译报 cannot find symbol: method setRowNumber(int)?

A:2.4.0 的 Tablet 行数方法为 setRowSize(int),网上旧版资料中的 setRowNumber 不存在(实测 javap 确认)。

Q4:查询结果为空?

A:确认查询路径与写入的设备/测点完全一致(含大小写),且时间过滤的时间戳为毫秒(本工程基线 1700000000000);QueryExpression 中 addSelectedPath 的路径需与 schema 注册名逐段相同。若仍为空,用 ReadDemo 里的元数据遍历先核对文件内实际设备与测点名。

Q5:Python 写入抛 DeviceNotExistError?

A:树模型下必须先 tablet.set_table_name("root.py.device1") 指定目标设备,且设备需已 register_device(实测复现与修复均通过)。

Q6:exec:java 中文乱码?

A:MAVEN_OPTS 追加 -Dfile.encoding=UTF-8。

九、参考链接