Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
byte timeSeriesMetadataType;
String measurementID; // can remove
varInt chunkMetaDataListDataSize;

...

Code Block
byte marker;
varInt data_size;
String measurementID; // redundant, can remove
byte datatype;
byte compressionType;
byte encoding type

...

(1)节省存储空间,每序列10个点的情况下,移除统计信息大约能节省28%的空间;每序列1个点的情况下,移除统计信息大约能节省45%的空间

(2)提高写入性能:写入性能约提高5(2)提高写入性能:写入性能约提高2-10%3%

缺点

(1)查询无法使用统计信息进行过滤和加速聚合

(2)删除chunk header里面的measurementID后,Tsfile无法顺序恢复chunk,需要重做所有WAL


实验

实验场景

模拟海量时间序列一次刷盘1%的数据量,即100万时间序列,其中有1万个设备,每个设备下有100个工况,每条时间序列写10个点,生成tsfile

每条时间序列的类型为long,编码算法为RLE,压缩算法为SNAPPY或GZIP每条时间序列的类型为long,值随机生成,编码算法为RLE,压缩算法为SNAPPY或GZIP

去除统计信息前后文件大小比较

每序列10个点(SNAPPY)

文件部分 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
数据区(Timeseries metadata之前的部分)126.24 MB126.24 MB126.24 MB126.24 MB
元数据区(Timeseries metadata之后的部分)79.17 MB38.17 MB22.17 MB12.25 MB
总大小205.41 MB164.41 MB148.41 MB138.49 MB
总大小比例100 %80.04 %72.25 %67.42 %

每序列1个点(SNAPPY)

文件部分 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
数据区(Timeseries metadata之前的部分)48.99 MB48.99 MB48.99 MB48.99 MB
元数据区(Timeseries metadata之后的部分)79.16 MB38.16 MB22.16 MB12.24 MB
总大小128.15 MB87.15 MB71.15 MB61.23 MB
总大小比例100 %68.01 %55.52 %47.80 %

每序列10个点(GZIP)

文件部分 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
数据区(Timeseries metadata之前的部分)140.11 MB140.11 MB140.11 MB140.11 MB
元数据区(Timeseries metadata之后的部分)79.17 MB38.17 MB22.17 MB12.25 MB
总大小219.28 MB178.28 MB162.28 MB152.36 MB
总大小比例100 %81.30 %74.01 %69.48 %

每序列1个点(GZIP)

文件部分 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
数据区(Timeseries metadata之前的部分)62.03 MB62.03 MB62.03 MB62.03 MB
元数据区(Timeseries metadata之后的部分)79.16 MB38.16 MB22.16 MB12.25 MB
总大小141.20 MB100.20 MB84.20 MB74.28 MB
总大小比例100 %70.96 %59.63 %52.61 %

...

去除统计信息前后写入时间比较 (SSD)

CPU:i5-5257U CPU @ 2.70GHz 

每序列10个点 (SNAPPY)

写入速度 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间4439.858 s4439.216 575 s4239.582 674 s4038.714 948 s
写入时间比100 % 9899.57 29 %9499.93 54 %9097.76 72 %

每序列1个点 (SNAPPY)

写入速度 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间4237.771 s4237.401 s4137.987 023 s4036.030 993 s
写入时间比100 %99.13 02 %98.17 02 %9397.59 94 %

每序列10个点 (GZIP)

写入速度 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间8154.053 s7954.042 s7453.134 782 s7053.287 465 s
写入时间比100 % 9799.52 98 %9199.46 52 %8698.72 91 %

每序列1个点 (GZIP)

写入速度 / 文件所含内容原始文件仅保留起止时间统计信息去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间7949.911 s7249.701 s6549.384 s6349.224 s
写入时间比100 %99.58 %98.94 %98.47 %


去除统计信息前后写入时间比较 (HDD)

CPU:i7-10700 CPU @ 2.90GHz

每序列10个点 (SNAPPY)

写入速度 / 文件所含内容原始文件去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间8.887 s8.837 s8.729 s8.627 s
写入时间比100 % 99.44 %98.22 %97.07 %

每序列1个点 (SNAPPY)

写入速度 / 文件所含内容原始文件去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间8.182 s8.023 s8.092 s7.998 s
写入时间比100 %98.06 %98.90 %97.75 %

每序列10个点 (GZIP)

写入速度 / 文件所含内容原始文件去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间

18.591 s

18.293 s17.992 s18.003 s
写入时间比100 % 98.40 %96.78 %96.83 %

每序列1个点 (GZIP)

写入速度 / 文件所含内容原始文件去掉值统计信息去除全部统计信息去除全部统计信息及measurementID
写入时间16.392 s16.113 s16.104 s15.982 s
写入时间比100 %98.30 %8198.82 24 %7997.12 50 %


实验结论

(1)存储空间:每序列10个点的情况下,移除统计信息大约能节省33%的空间;每序列1个点的情况下,移除统计信息大约能节省50%的空间

(2)写入速度:去除统计信息可以节省10(2)写入速度:去除统计信息可以节省2-20%的写入时间 3%的写入时间,写入时间与CPU关系很大,按照磁盘写入速度,SSD写磁盘占用的时间不到整个写入时间的4%,HDD写磁盘占用的时间不到整个写入时间的8%

实验源码

写入程序

Code Block
import java.io.File;
import java.util.Random;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
import org.apache.iotdb.tsfile.fileSystem.FSFactoryProducer;
import org.apache.iotdb.tsfile.read.common.Path;
import org.apache.iotdb.tsfile.write.TsFileWriter;
import org.apache.iotdb.tsfile.write.record.TSRecord;
import org.apache.iotdb.tsfile.write.record.datapoint.DataPoint;
import org.apache.iotdb.tsfile.write.record.datapoint.LongDataPoint;
import org.apache.iotdb.tsfile.write.schema.UnaryMeasurementSchema;

/**
 * An example of writing data with TSRecord to TsFile It uses the interface: public void
 * addMeasurement(MeasurementSchema measurementSchema) throws WriteProcessException
 */
public class TsFileWriteTest {
  public static int deviceNum;
  public static int sensorNum;
  public static int fileNum;
  public static int pointNum = 10;

  public static void main(String[] args) {
    try {
      deviceNum = 1; // Integer.parseInt(cl.getOptionValue("d"));
      sensorNum = 1; // Integer.parseInt(cl.getOptionValue("m"));
      fileNum = 1; // Integer.parseInt(cl.getOptionValue("f"));
    } catch (Exception e) {
      e.printStackTrace();
    }

    for (int fileIndex = 0; fileIndex < fileNum; fileIndex++) {
      try {
        String path =
            "/Users/SilverNarcissus/iotdb/tsfile_test"
                + "/withoutStat/"
                + deviceNum
                + "."
                + sensorNum
                + "/test"
                + fileIndex
                + ".tsfile";
        File f = FSFactoryProducer.getFSFactory().getFile(path);
        if (f.exists()) {
          f.delete();
        }

        try {
          TsFileWriter tsFileWriter = new TsFileWriter(f);
          for (int i = 1; i <= deviceNum; i++) {
            for (int j = 1; j <= sensorNum; j++) {
              Path path1 = new Path(Constant.DEVICE_PREFIX + i);
              tsFileWriter.registerTimeseries(
                  path1,
                  new UnaryMeasurementSchema(
                      Constant.SENSOR_ + j, TSDataType.INT64, TSEncoding.RLE));
            }
          }
          // construct TSRecord
          int count = 0;
          for (int j = 1; j <= deviceNum; j++) {
            for (int i = 1; i <= pointNum; i++) {
              TSRecord tsRecord = new TSRecord(i, Constant.DEVICE_PREFIX + j);
              for (int t = 1; t <= sensorNum; t++) {
                DataPoint dPoint1 =
                    new LongDataPoint(Constant.SENSOR_ + t, new Random().nextLong());
                tsRecord.addTuple(dPoint1);
                count++;
              }
              // write TSRecord
              tsFileWriter.write(tsRecord);
              if (count % 100000 == 0) {
                System.out.println(count);
                tsFileWriter.flushAllChunkGroups();
              }
            }
          }
          tsFileWriter.close();
        } catch (Throwable e) {
          e.printStackTrace();
          System.out.println(e.getMessage());
        }

      } catch (Throwable e) {
        e.printStackTrace();
        System.out.println(e.getMessage());
      }
    }
  }
}

...