Apache IoTDB 系列教程-6:性能优化(0.8-0.10)

今天的内容包括建模优化、读写性能优化,会涉及一些简单的原理介绍。主要面向 0.8 - 0.10 版本。git

正文 3754 字,预计阅读时间 10 分钟。github

建模指南

关于存储组数据库

如今每一个存储组是一个相对独立的引擎,并且读写锁是存储组级别的。所以把存储组从1改到10,读写基本能增速8倍。单个 IoTDB 实例推荐设置 CPU 核数个存储组。存储组越多,并行度就越高。咱们以后打算把锁粒度下放到设备层。apache

设备缓存

设备这个概念没有在 SQL 语句里显示的定义出来,而是在服务器端处理时候默认将倒数第二层设置为设备,致使你们容易忽略这个概念。先说一下设备影响什么。性能优化

(1)区分顺序数据 和 乱序数据是以设备为粒度的。举个例子,假如一个设备在内存里写了时间戳 1-10 的数据(不论写哪些测点,时间戳都会算到这个设备头上),落盘了,再写时间戳<=10 的数据,这些数据就会被当作乱序数据缓存并落盘。服务器

(2)设备粒度的时间范围索引。对于每一个 TsFile 文件,会构造设备粒度的索引在内存里,假如全部设备都活跃,N 个 TsFile,D 个设备,就有 N*D 条索引。百万级设备的索引内存会吃不消。这个东西咱们会在一两个版本内改掉。网络

再说一下怎么设计建模来控制设备数。对于实际应用设备和传感器层次比较简单的状况比较好说,设备下直接是传感器层,通常不会建错。对于设备下有多层结构的就要注意了。工具

好比我一个设备下有10个传感器(s1,s2,...,s10),每一个传感器采集10个时间序列的数据(f1,f2,...f10)。这时候很容易建成 root.xxx.device.s1.f1 这种。当你建成这种时候,你觉得的 device 就再也不是你觉得的 device 了,实际的device 变成了 root.xxx.device.s1 。 实际 device 数量就是你觉得的 10 倍了。性能

怎么办嘞,若是设备下的子设备很少,这样建模也没啥问题,只要你内心清楚系统中实际有多少个设备就行,这样沟通不会出现误差,便于之后排查问题。

若是子设备很是多,能够把设备后的那几层压成一层,好比 root.xxx.device.s1_f1 。因为咱们是以 . 做为分隔符的,这样 s1_f1 就变成 1 层了。 实际的设备仍是 root.xxx.device。

Measurement 定义

Measurement 也就是最后一层的测点。假如一个测点是 INT32 或者 INT64 类型的,并且大部分时间这个数据的值都同样,没什么变化,这时候用 RLE 编码就很好。能够大大节省磁盘空间,固然刷盘速度也会变快。压缩方式开着 SNAPPY 就挺好。

Tag & Attribute

0.10.0 引入的这两个概念,容易分不清这两个有啥区别。 虽然都是 key-value 类型的属性。可是 Tag 是能够反向查询时间序列的元数据的,假若有个 tag  的 key 是 owner,就能够用 show timeseries where owner=Thanos 查灭霸拥有的时间序列。Tag 常驻内存,有Tag到时间序列的索引。

Attribute 就是普通属性了,好比有个属性是 description="this is my series"。这些属性只能是给定时间序列的路径顺带展现一下,辅助人查看的。

所以,要根据实际需求进行区分,那些须要作反向查询的属性,就建成 tag,其余的就搞成 attribute 就好了。 

读写优化

读和写关系密切,数据的写入和参数配置会影响查询性能。

写入接口

以 0.10 为例,先同类比较,insertRecords 接口确定比 insertRecord 接口要快,这个相似 JDBC 的 executeBatch 和 execute 的区别,节省了网络通讯次数。同理,insertTablets 比 insertTablet 要快,createMultiTimeseries 也比 createTimeseries 要快。

进一步,insertRecords 方法咱们提供了两种,一种是传 Object 的 value,一种是传 String 的 value。若是客户端能获取 value 的类型,建议用 Object 的,会比 String 的快 25% 左右。

跨类比较的话,若是不考虑客户端作格式转化的耗时,insertTablet 比 insertRecords 要快不少,可能 8 倍以上,节省了不少对象封装的耗时,batch size 1000左右就能够。

insertTablet 这个接口默认是没排序的,若是你能保证一个 Tablet 数据的时间戳是非递减的,那就能够多加一个 sorted 为 true 的参数。就节省了客户端的排序。

在统计耗时的时候,还须要注意客户端作格式转化的耗时,能够把接口参数构造的时间和执行的时间分开统计。

查询接口

查询接口比较简单,Session 默认的 hasNext 和 next 会返回 RowRecord 结构,这个结构不必定你们都须要,能够用 SessionDataSet 的 iterator 获得一个迭代器,而后经过相似 JDBC 的接口去获得原始数据,避免不少没用的对象生成。

有一个须要注意的是,查询有一个 fetchSize,一次从服务器取的点数,这个在最好大一些,1万左右,否则网络通讯次数会不少。

顺序写入

对于时序数据库,时序是一个很重要的概念,最好不要乱来。IoTDB 支持数据的乱序写入,可是乱序数据会影响查询性能,主要是对于聚合查询,原理是乱序数据会让预计算的统计信息失效,把聚合查询降维打击到读原始数据。

正常状况下,有个几倍的乱序都没问题,可是若是往一个时间段写入了过多(几万倍)的乱序数据,查询时候有可能爆内存。举个例子,内存缓冲区写了时间戳1-10的数据落盘了,而后又写了 9999 遍 1-10 的缓冲区,这样磁盘上就有 1 万个时间戳是 1-10 的数据块。查询时候须要将 1万 个数据块都读出来进行合并,内存占用就比较大了。

面对这种场景,咱们会后台作数据整理来处理乱序(在0.9引入的merge,可是0.9版本有bug,0.10修掉了,可是先默认关掉了,会在0.11从新开放merge),可是若是能在客户端避免乱序,就尽可能写入的时候避免掉。一个设备就按递增的顺序写入。

若是前边接了 kafka,最好注意一下,把设备 id 做为分区粒度,这样一个设备的数据都会发送到一个分区里,消费的时候同一个分区也能保证顺序。

内存缓冲区

先介绍一下每一个序列在内存里能缓存多少个点的怎么算的,用 memtable 大小除以序列数,再除以每一个点的大小,好比long类型就是 16字节(8字节时间戳,8字节值),float是12字节。

memtable 的大小能够从日志里看到,搜 reaches,大概日志就是 the memtable size xxx reaches the threshold。若是配置文件里的 enable_parameter_adapter 没有改成 false,这个 memtable 大小就不固定,随着注册的序列数量调整的。

内存缓冲区在必定范围内尽量大有利于读写。平均每一个序列能缓冲100万点如下是比较好的。可是不建议太大,查询时候会临时排序,若是内存中数据点过多,好比千万级,查询时候内存排序会占个十几秒。

为了不这个问题,0.10.0 里加了个参数,avg_series_point_number_threshold ,默认是 10000,也就是内存缓冲区中每一个序列最多缓存这么多点就会刷盘,这个默认参数没给好,能够改为50万或者100万。

memtable_size_threshold 这个参数越大,写入速度快,通常在几百M到一两G左右。不要设置的太小,好比几M,会严重影响写入速度。在设置这个参数时候须要注意不要超内存限制,调这个参数以前须要保证 enable_parameter_adapter 改成 false。

多数据目录

数据库的瓶颈在磁盘IO,简单的提高磁盘IO能力的就是配置多盘。IoTDB 的数据目录能够在 data_dirs 参数配置,用逗号分隔多个目录。能够每块盘一个目录。在写数据的时候会到这几个盘里找最空闲的写。

客户端优化

刚才说了存储组级别的锁,对于同一个存储组的N个写线程,这N个写线程都会抢一把锁,一个存储组对应不超过50个客户端比较好,写线程过多会致使过多的锁竞争。

线程池 SessionPool 的容量,通常搞个服务器 CPU 核数就能够了,不要过多。

客户端的内存,数据的生产和消费速率也能够监控起来,避免提交的任务积压过多,若是客户端内存满了,会出现一个现象:客户端发送请求到服务器,服务器执行和返回很快,可是客户端接收结果会很慢。

容易爆内存的点

select * from root 这个语句在序列过多时候最好不要作,这个操做会把整个库当作一张表,一下查出来全部列的一批数据,容易爆内存,咱们会在0.11版本加个检查,及时拒绝。

show timeseries 在 0.10 及之前的版本会把系统全部序列在内存里拷贝一遍传给客户端,若是序列过多,最好指定前缀作个过滤。或者 show child paths 一层一层往下查。

时间序列过多(亿级),元数据可能爆内存,能够按照一条时间序列 200字节估计一下,大概1千万序列会占2G元数据(就是那个元数据树)。

总结

数据库前期须要比较多的手动调优,如今的自动调优工具还有待完善,咱们的目标是越简单越好,0.11 版本会完善内存和参数配置。今天内容比较多,以后想到什么再出续集!

欢迎关注,转发,给 github 点 star!

https://github.com/apache/incubator-iotdb/tree/master

相关文章
相关标签/搜索