千家信息网

Apache TDB性能优化的知识点有哪些

发表于:2024-11-18 作者:千家信息网编辑
千家信息网最后更新 2024年11月18日,本篇内容介绍了"Apache TDB性能优化的知识点有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所
千家信息网最后更新 2024年11月18日Apache TDB性能优化的知识点有哪些

本篇内容介绍了"Apache TDB性能优化的知识点有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

关于存储组

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

设备

设备这个概念没有在 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 的接口去得到原始数据,避免很多没用的对象生成。

顺序写入

对于时序数据库,时序是一个很重要的概念,最好不要乱来。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元数据(就是那个元数据树)。

"Apache TDB性能优化的知识点有哪些"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0