第一点优化就是对元数据和数据访问的优化 。一些表的元数据信息是非常大的,比如一张 Hive 表可能有十万个分区,如果把十万个分区信息在一开始的时候就全都同步到的 FE 节点,对 FE 节点内存压力会非常大 。因为 Doris 中所有的元数据都是在内存存放的,如果把这些外部数据源的信息全都一次性同步过来的话,FE 的元数据压力会非常大 。所以我们在 FE 上做了多种类型的 cache 。
第一种是 schema cache,对于外表的所有列信息的 cache 。这些 cache 全都是按需加载的 。比如我们有一千张表,只需要访问到其中的一张表的时候,我们才会把这张表的 schema 缓存到 FE 的缓存集中 。这样可以保证内存中只有需要用到的 schema 。
第二个是 partition value cache 。当查询一个外表时需要对分区进行裁剪 。分区裁剪只需使用分区值 。所以我们单独实现了一个 partition value cache 专门去缓存分区值的信息,用于分区裁剪 。分区值的内存空间占用是非常少的 。通过分区值裁剪,可以得到最终需要访问的分区列表 。
当得到分区列表以后,就进入到第三步,即需要访问 partition cache 去拿到完整的分区信息 。拿到这些信息以后,我们就来到第四步,就是 file cache 。一个分区下面会有多个文件 。我们拿到了分区的位置信息以后,就可以通过 file cache,去获取这个分区下的所有的文件的信息(文件路径) 。拿到文件路径后,我们在 FE 中会做任务的拆分 。最终会把这些文件列表拆分以后,发给 BE 。
BE 节点负责文件的读取和访问 。这里我们也实现了两个大类的缓存的优化 。
第一个是数据预读(prefetch buffer),在访问 HDFS 和 S3 时,本质是一个 RPC 请求 。第一个优化点就是如何能够尽量减少 RPC 的开销 。一次 RPC 开销本身的 overhead 很重 。所以我们增加了一个预取缓存,把多个小的 Remote IO 请求合并成一个大的 IO 请求,把原先可能几个字节的请求,合并成 4KB 到 1MB 的数据请求一次性读取过来,在本地内存中形成一个缓存 。后续的 IO 可以直接在内存缓存中去获取数据,极大的减少 remote IO 的次数 。
第二个是文件块级别的缓存(file block cache) 。在访问 HDFS 或者 S3 上的数据文件时,可能只需访问其中的一小部分 。比如一个列存格式的 parquet 文件,如果只需要访问其中的三列数据,那么只会读取整个文件的一部分,Doris 会在第一次文件读取时,将读取的文件块缓存到本地磁盘 。缓存文件的文件名是文件的路径,加上读取偏移量的组合标识 。之后如果有相同的文件访问,会先查看本地是否已经有缓存的数据文件 。如果有,则直接去读本地文件,减少访问远端数据的开销 。通过 file block cache,可以极大地提升一些热数据的访问效率 。

文章插图
第二个优化点就是 native 的 file format reader 。以 parquet 为例,在老版本的 Doris,是通过 Apache arrow 库内置的 parquet reader 完成读取的 。这个 Reader 的实现会有一些额外的开销 。比如它会多一层内存格式的转换 。因为它在读取的时候,首先需要把远端的文件转换成内部的 arrow 的格式 。然后再把 arrow 的格式转换成 Doris 的内部内存格式 。第二是 Apache arrow 内置的 parquet reader 对一些新的 Parquet 特性是不支持的,比如不支持 page index、不支持 parquet 的 bloom filter 的读取、不支持这种更精细的字典编码的优化等等 。
基于以上考虑,我们在 Doris 内部实现了一个 native 的 C++ 的 parquet reader 。首先是能直接转换内部存储格式,对于读取到的数据,直接转为内部内存格式,减少一次内存格式的拷贝和转换开销;第二点,我们能够利用 bloom filter 进行更精确的数据过滤 。用户写数据的时候,对某一列使用的 bloom filter,可以利用 bloom filter 去对数据进行过滤 。其次我们也支持了基于字典编码的谓词过滤,可以把谓词下推到 parquet reader 中 。把谓词中的,比如 “a=‘北京’” 这样的一个条件改成一个对应字典编码的值 。比如 “a=‘100’”,后续用 ‘100’ 在文件内部进行数据过滤 。因为整数型的过滤效果是比字符串的过滤效果要好很多的 。过滤完了以后,在最终返回结果的时候,我们再把字典编码值转换成真正的数据的值 。这样来达到加速的效果 。
最后一点也是非常重要的一点,就是在 Parquet Reader 上支持了延迟物化 。延迟物化是访问远端存储的时候,减少 IO 的一个非常重要的特性 。尤其是在带谓词条件的宽表查询上 。简单来说,Doris 会优先读取带谓词条件的列 。读取完这些列以后,我们先对这些列进行过滤得到最终过滤后的行号集合,再去读取剩余的其他的列 。这样就能保证剩余其他列都是只会去读取过滤后的数据 。从而极大地减少从远端去读取数据的 IO 开销 。
推荐阅读
- 哪个借款平台不查征信(贷款不查征信极速放款)
- 不查征信负债下款最快的平台(贷款不查征信极速放款)
- 极速僵尸结局什么意思 沐浴盐僵尸
- 山姆极速达时间?怎样给山姆极速达骑手小费
- 详解Apache Sentry->Ranger平滑升级方案
- Apache日志分析器
- 十大经典极速飞车电影 十大飙车电影
- 北京联通3G极速上网卡如何收费 北京3g木兰公寓怎么样
- Linux 9 自动化部署 Kafka 集群
- 极速保镖|电影《极速保镖》第十二届北京国际网络电影展载誉而归 尽显高能
