IoTDB数据库的使用

网址:
https://iotdb.apache.org/zh/

源码,里面有C#的示例代码:
https://github.com/apache/iotdb-client-csharp

快速上手

https://iotdb.apache.org/zh/UserGuide/V1.2.x/QuickStart/QuickStart.html

服务端

服务端的下载页面:
https://iotdb.apache.org/Download/

我下载了最新的1.2.2版本。

下载完整后执行批处理文件:

sbin\start-standalone.bat

运行时报错:

"setting local JMX..."
Maximum memory allocation pool = 8140M, initial memory allocation pool = 600M
If you want to change this configuration, please check conf\datanode-env.bat.
Check whether the ports are occupied....
JAVA_HOME environment variable must be set

看了一下提示,应该是缺少JDK,于是便下载安装了最新的JDK,并且配置了JAVA_HOME。
再次执行start-standalone.bat批处理,运行正常。

客户端调用

安装Nuget包:

Install-Package Apache.IoTDB

示例代码:

// 参数定义
using Apache.IoTDB.DataStructure;
using Apache.IoTDB;

string host = "localhost";
int port = 6667;
int pool_size = 2;

// 初始化session
var session_pool = new SessionPool(host, port, pool_size);

// 开启session
await session_pool.Open(false);

// 创建时间序列
await session_pool.CreateTimeSeries("root.test_group.test_device.ts1", TSDataType.TEXT, TSEncoding.PLAIN, Compressor.UNCOMPRESSED);
await session_pool.CreateTimeSeries("root.test_group.test_device.ts2", TSDataType.BOOLEAN, TSEncoding.PLAIN, Compressor.UNCOMPRESSED);
await session_pool.CreateTimeSeries("root.test_group.test_device.ts3", TSDataType.INT32, TSEncoding.PLAIN, Compressor.UNCOMPRESSED);

// 插入record
var measures = new List<string> { "ts1", "ts2", "ts3" };
var values = new List<object> { "test_text", true, (int)123 };
var timestamp = 1;
var rowRecord = new RowRecord(timestamp, values, measures);
await session_pool.InsertRecordAsync("root.test_group.test_device", rowRecord);

// 插入Tablet
var timestamp_lst = new List<long> { timestamp + 1 };
var value_lst = new List<List<object>> { new List<object> { "iotdb", true, (int)12 } };
var tablet = new Tablet("root.test_group.test_device", measures, value_lst, timestamp_lst);
await session_pool.InsertTabletAsync(tablet);

// 关闭Session
await session_pool.Close();

CreateTimeSeries介绍

  1. session_pool: 这是一个SessionPool对象,它是与Apache IoTDB服务器交互的主要接口。session_pool通常在应用程序启动时创建,并用于获取和管理与IoTDB的会话。

  2. CreateTimeSeries: 这是SessionPool类的一个方法,用于在IoTDB中创建一个新的时间序列。

  3. "root.test_group.test_device.ts1": 这是一个字符串参数,表示要创建的时间序列的全路径。在Apache IoTDB中,时间序列的路径采用层次结构的形式,由多级节点组成。在这个例子中,路径表示根节点下的"test_group"分组下的"test_device"设备下的"ts1"时间序列。

  4. TSDataType.TEXT: 这是一个枚举参数,定义了时间序列的数据类型。TSDataType枚举包含了多种数据类型,如INT32、INT64、FLOAT、DOUBLE、BOOLEAN、TEXT等。在这个例子中,时间序列的数据类型被设置为TEXT,表示存储文本数据。

  5. TSEncoding.PLAIN: 这是一个枚举参数,定义了时间序列的数据编码方式。TSEncoding枚举包含了多种编码方式,如PLAIN、PLAIN_DICTIONARY、RLE、DIFF、TS_2DIFF等。在这个例子中,时间序列的数据编码方式被设置为PLAIN,表示使用原始数据格式存储。

  6. Compressor.UNCOMPRESSED: 这是一个枚举参数,定义了时间序列的数据压缩方式。Compressor枚举包含了多种压缩算法,如UNCOMPRESSED、SNAPPY、GZIP、LZ4等。在这个例子中,时间序列的数据压缩方式被设置为UNCOMPRESSED,表示不进行数据压缩。

TSEncoding.PLAIN介绍

TSEncoding.PLAIN是Apache IoTDB中的一种数据编码方式,它表示使用原始数据格式存储时间序列的数据。PLAIN编码是最简单、最直接的编码方式,不进行任何特殊的数据转换或压缩。

相比之下,Apache IoTDB还提供了其他几种数据编码方式,每种方式都有其特定的应用场景和优缺点:

  1. PLAIN_DICTIONARY
    PLAIN_DICTIONARY编码是一种字典编码方式,适用于值域较小且重复次数较多的数据。在这种编码下,IoTDB会维护一个字典,将原始数据映射为字典中的索引。这样可以减少存储空间,特别是当数据中有大量重复值时。然而,查询时需要额外的解码步骤,可能会增加一些计算开销。

  2. RLE
    RLE(Run-Length Encoding)编码是一种简单的无损压缩方法,用于存储连续重复的值。如果数据中有很长的连续相同值的序列,RLE编码可以显著减少存储空间。但是,对于没有或很少重复值的数据,RLE编码可能不会带来太大的压缩效果。

  3. DIFF
    DIFF编码基于前后值的差分进行存储,适用于连续数据值变化相对平滑的情况。这种编码可以减少存储空间,特别是当数据值的变化相对较小的时候。然而,查询时需要进行差分逆运算来恢复原始数据,可能会增加计算复杂性。

  4. TS_2DIFF
    TS_2DIFF编码是对DIFF编码的进一步优化,它存储的是连续两个数据点的差分。这种编码在数据变化更加平滑的情况下能提供更好的压缩效果,但同样在查询时需要进行更复杂的逆运算来恢复原始数据。

查询数据

//string query = "SELECT * FROM root.test_group.test_device where time>1";
//string query = "SELECT * FROM root.test_group.test_device";
string query = "SELECT ts1,ts2 FROM root.test_group.test_device";
SessionDataSet resultSet = await session_pool.ExecuteQueryStatementAsync(query);

// 打印列名
Console.WriteLine(string.Join(", ", resultSet.ColumnNames));

while (resultSet.HasNext())
{
    RowRecord record = resultSet.Next();
    //Console.WriteLine(record.Values.StringJoin(", "));
    Console.WriteLine(record.ToString());
}

SELECT * FROM root.test_group.test_device

相当于查询test_group.test_device下的所有参数

SELECT * FROM root.test_group.test_device where time>1

限制一下时间,只返回time大于1的。

SELECT ts1,ts2 FROM root.test_group.test_device

只返回需要的两个序列。感觉这里的序列有点像关系型数据库的表字段。

性能测试

下面是测试代码:

#if true
await session_pool.CreateTimeSeries("root.test_group.real_data.A", TSDataType.DOUBLE, TSEncoding.PLAIN, Compressor.UNCOMPRESSED);
await session_pool.CreateTimeSeries("root.test_group.real_data.B", TSDataType.DOUBLE, TSEncoding.PLAIN, Compressor.UNCOMPRESSED);
await session_pool.CreateTimeSeries("root.test_group.real_data.C", TSDataType.DOUBLE, TSEncoding.PLAIN, Compressor.UNCOMPRESSED);
await session_pool.CreateTimeSeries("root.test_group.real_data.D", TSDataType.DOUBLE, TSEncoding.PLAIN, Compressor.UNCOMPRESSED);

var measures = new List<string> { "A", "B", "C", "D" };

var timestamp_lst = new List<long>();
var value_lst = new List<List<object>>();

Random random = new Random();
for (int i = 0; i < 1000 * 10000; i++)
{
    timestamp_lst.Add(i + 1000 * 10000);
    var dataList = new List<object>();
    dataList.Add(random.NextDouble());
    dataList.Add(random.NextDouble());
    dataList.Add(random.NextDouble());
    dataList.Add(random.NextDouble());

    value_lst.Add(dataList);
}

Stopwatch stopwatch = Stopwatch.StartNew();
var tablet = new Tablet("root.test_group.real_data", measures, value_lst, timestamp_lst);
await session_pool.InsertTabletAsync(tablet);
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
#endif

string query = "SELECT * FROM root.test_group.real_data";
SessionDataSet resultSet = await session_pool.ExecuteQueryStatementAsync(query);

stopwatch.Restart();
var myList = new List<TestInfo>();
while (resultSet.HasNext())
{
    RowRecord record = resultSet.Next();
    TestInfo info = new TestInfo();
    info.A = (double)record.Values[0];
    info.B = (double)record.Values[1];
    info.C = (double)record.Values[2];
    info.D = (double)record.Values[3];

    myList.Add(info);
}
stopwatch.Stop();
Console.WriteLine(myList.Count);
Console.WriteLine(stopwatch.Elapsed.ToString());

public class TestInfo
{
    public double A { get; set; }
    public double B { get; set; }
    public double C { get; set; }
    public double D { get; set; }
}

每条记录是4个字段。
每次插入1000万条数据,第2次插入时返回的结果:

00:00:13.1639352
20000000
00:00:21.8426342

相当于插入1千万条记录耗时是13秒,返回2千万条数据耗时是21秒。

另外,值得注意的是,timestamp有点类似数据的关键字,如果timestamp设置成一样的话,老的数据是会被新数据覆盖的。

posted @ 2023-12-21 13:51  wzwyc  阅读(115)  评论(0编辑  收藏  举报