Apache IoTDB 系列教程-4:客户端接口
说了半天语法和部署运维,实际使用还是要落到代码里的,今天介绍一下客户端的接口。
正文 3516 字,预计阅读时间 5 分钟。
现在的客户端和服务器通信采用了跨语言的 RPC 框架 Thirft,理论上 Thrift 能生成的语言都能支持。但是直接用 Thrift 生成的代码对数据库使用者不太友好,所以我们在生成代码的基础上,包装出来了我们的各种客户端接口,这种接口对用户就比较友好了。接下来介绍一下各种客户端接口。
JDBC 接口
JDBC 是关系数据库的标准接口,也是大家最熟悉的接口。所以一开始我们就提供了这种接口。
和标准 JDBC 的使用方式一样,需要加载数据库驱动类,建立连接,建立 Statement,通过 Statement 执行语句,对于非查询来说,可以批量执行减少网络传输次数。下面是一个简单的例子,
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("org.apache.iotdb.jdbc.IoTDBDriver");
try (Connection connection = DriverManager
.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
Statement statement = connection.createStatement()) {
// 创建存储组
statement.execute("SET STORAGE GROUP TO root.sg1");
// 创建时间序列
statement.execute("CREATE TIMESERIES root.sg1.d1.s1 WITH DATATYPE=INT64, ENCODING=RLE, COMPRESSOR=SNAPPY");
statement.execute("CREATE TIMESERIES root.sg1.d1.s2 WITH DATATYPE=INT64, ENCODING=RLE, COMPRESSOR=SNAPPY");
statement.execute("CREATE TIMESERIES root.sg1.d1.s3 WITH DATATYPE=INT64, ENCODING=RLE, COMPRESSOR=SNAPPY");
// 在客户端积累一批更新语句
for (int i = 0; i <= 100; i++) {
statement.addBatch("insert into root.sg1.d1(timestamp, s1, s2, s3) values("+ i + "," + 1 + "," + 1 + "," + 1 + ")");
}
// 执行
statement.executeBatch();
statement.clearBatch();
// 查询
ResultSet resultSet = statement.executeQuery("select * from root where time <= 10");
// 打印结果集
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
for (int i = 1; i < columnCount; i++) {
System.out.print(resultSet.getString(i));
System.out.print(" ");
}
System.out.println();
}
}
}
完整的示例代码位置:
https://github.com/apache/incubator-iotdb/blob/master/example/jdbc/src/main/java/org/apache/iotdb/JDBCExample.java
Java 原生接口 Session
对于数据写入,SQL 解析就占了 70% 耗时。于是我们提供了一个原生的 NoSQL 接口(Session),相比于 JDBC 更高效。
insertRecord(String deviceId, long time, List<String> measurements,
List<TSDataType> types, List<Object> values)
这个接口就对应一个 insert 语句,一次可以写入一个设备一个时间戳多个测点的值,其中值的类型需要和注册的类型保持一致,如果没注册过则自动注册此类型。
insertRecord(String deviceId, long time, List<String> measurements,
List<String> values)
在一些场景下,客户端拿不到具体的数据类型,这时候可以用这种 String 参数的接口。如果提前注册了序列,服务器会根据注册的类型来解析这些 String 的值,如果没注册,会根据值的格式推断类型进行注册。
insertTablet(Tablet tablet, boolean sorted)
一个Tablet 是一个设备多个时间戳多个测点的值。这里要注意,每个测点在每个时间戳都需要有值,不能有空的。sorted 表示是否时间戳是递增的,如果能保证递增,可以设置为 true,否则我们还会再排个序。
如果只计算执行时间,这个接口是最高效的,因为里边使用了原始类型的数组,避免了装箱。但是,这个接口对数据的格式要求很高,如果数据采集不是对齐采的,强行转化成这种接口,转化的耗时需要统计一下。
此外还有 insertTablets 和 insertRecords 两种,其实就是以上几种接口的批量的形式。
Session 的查询结果集是 SessionDataSet,这个结构提供的 hasNext 和 next 方法把每一行数据都转化成了 RowRecord 这个结构,如果客户端还需要做其他转化,这个结构就多余了。这时候可以通过 SessionDataSet.iterator()得到一个迭代器,这个迭代器的访问数据的方式和 JDBC 的 ResultSet 是一样的,直接从字节数组里拿数据,比 RowRecord 更高效。
完整的示例代码位置:
https://github.com/apache/incubator-iotdb/blob/master/example/session/src/main/java/org/apache/iotdb/SessionExample.java
连接池 SessionPool
自从原生接口诞生以来,很多用户就从 JDBC 迁移到原始接口了,我们也扩充了原生接口的能力,增加了 Session 的连接池(东哥倾情奉献)。连接池的接口和 Session 基本一样,但是连接池可以供多线程使用,而且可以屏蔽连接异常等问题。
使用连接池唯一一点需要注意的是,查询得到的结果集使用完需要返还给连接池(调用连接池的 closeResultSet 方法),不然连接会被占用,无法得到新的连接就报超时了。
SessionDataSetWrapper wrapper = null;
try {
wrapper = pool.executeQueryStatement("select * from root.sg1.d1");
while (wrapper.hasNext()) {
System.out.println(wrapper.next());
}
} catch (IoTDBConnectionException | StatementExecutionException e) {
e.printStackTrace();
} finally {
// remember to close data set finally!
pool.closeResultSet(wrapper);
}
完整的示例代码位置:
https://github.com/apache/incubator-iotdb/blob/master/example/session/src/main/java/org/apache/iotdb/SessionPoolExample.java
Python 接口
除了 JAVA 的接口,我们还包装了一下 Python 的接口,是在 0.9 版本之后才增加的。位置在
https://github.com/apache/incubator-iotdb/blob/master/client-py
- 计算机程序的思维逻辑 (13) - 类
- 新浪微博爬虫最新分享
- Unity Application Block 3月12 发布的版本
- HTTPS静态服务搭建过程详解
- 基于Webkit的浏览器关键渲染路径介绍
- CodePlex 全面支持Subversion/TortoiseSVN
- 计算机程序的思维逻辑 (11) - 初识函数
- Android WebView 调试方法
- Python开发微信公众号后台(系列一)
- Consul最常用的命令和接口api
- webpack的Hot Module Replacement运行机制
- 如何使用Node.js编写命令工具——以vue-cli为例
- SpringBoot编写自定义的starter
- webpack3新特性简介
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 01Python的基本的数据结构之List
- 02Python数据结构之再谈List的常用操作
- 03Python List不得不知的操作之改、查
- 04Python基础之字符串Str
- 05Python元组tuple的个性
- Tkinter Canvas
- Python实现最小二乘法
- 一个简单的例子学明白用Python插值
- python 类class基础简明笔记
- 数据离散化及其KMeans算法实现的理解
- [tensorflow损失函数系列]sparse_softmax_cross_entropy_with_logits
- 怎样将Anaconda设置为国内的镜像
- Python实现KMeans算法
- Python面向对象编程
- HTML和CSS常见问题整理