Apache Iceberg 中引入索引提升查询性能( 二 )

name = 'LiLy'age > 30进行判断 , 得到 data file 1 和 data file 2 都满足条件 。然而 , 仔细分析 data file 1 和 data file 2 的数据 , 并不存在符合条件的数据 , 因此 min-max 过滤效果不太理想 。所以通过引入合适的索引功能 , 可以提高 data skipping 的概率 , 提高查询性能 。
【Apache Iceberg 中引入索引提升查询性能】1.  首先探究索引类型
索引类型有多种 , 如 BloomFilter、Ribbon Filter、Dictionary Index、BitMap 等 。为了满足多维分析场景 , 我们选择了[Range-Encoded BitMap]https://www.featurebase.com/blog/range-encoded-bitmaps ( Base-2, Bit-sliced Index) , 可适用于高基数场景 , 满足=、<、>、IN、BETWEEN 等操作的多维分析 。
例如 , 对上面的 name 和 age 两列分别计算索引信息 。由于 name 属于字符串类型 , 需要先进行字典编码再进行计算索引信息 。采用 Range-Encoded 技术 , 根据数据的二进制相关信息以及对应的 pos 信息生成索引数据 。利用索引数据分析得到 , 同时满足name = 'LiLy' 和age > 30的数据不在同一行 , 恰好可利用 Range-Encoded 的交并运算将数据进行过滤掉 , 因此 data file 1 不用参与计算 。
也就是说 , BitMap 的交并运算可以更好地在复杂过滤条件的情况下过滤掉更多的数据文件 。
 

Apache Iceberg 中引入索引提升查询性能

文章插图
 
2.  接下来探究索引的粒度 。
Iceberg 提供的 min-max , 也是一种文件级别的索引 。文件级别的索引就是根据 filter 条件过滤掉不符合条件的 data file 。文件级别的索引可适用于多种文件类型 , 但这种粒度比较粗 , 只要 data file 中有一条数据符合条件 , 该 data file 中的数据就会全部读取出来参与计算 , 从而影响 SQL 的查询性能 。
对于 Parquet、ORC 的文件格式 , 提供有 file chunk 的概念(row group or stripe) , 我们完全可以按照 row group / stripe 粒度 , 对数据进行过滤 。(为了方便描述 , 我们将 row group 和 stripe 统称 split 。)
如:SQL语句:SELECT * FROM table WHERE col_1> v1 AND col_2 = v2 , 其中对 col_1 字段和 col_2 字段已构建 Index 信息 。现在利用索引对 SQL 语句作用 。
 
Apache Iceberg 中引入索引提升查询性能

文章插图
 
SQL 语句解析后 , 将符合条件的 data file 列表进行切分后 , 得到很多 split 的列表 。利用索引 , 分析 split 中数据是否满足条件 , 如果不满足则跳过 。如上图 data file 列表切分后 , 得到数万级别数量的 split 列表 。将索引数据作用在 split1 , 发现 split1 中没有同时col_1> v1 AND col_2 = v2满足条件的数据 , 该 split1 中的数据就不会参与计算 。最后处理后 , 只得到了少量的 split 列表 , 数据过滤度达到 10% 以上 , 查询性能有明显提升 。
因此 , 采用 row group / stripe 级别的细粒度索引 , 可以过滤大部分数据 。
细粒度索引实现逻辑Iceberg 元数据中 manifest file 中除了提供 min-max 等统计信息 , 还提供有 split 相关信息:"split_offsets":{"array":[4,...]} , 极大方便我们实现 row group / stripe 级别的细粒度索引 。
  1. 提供索引的构建 API
Iceberg 中提供构建索引的 API , 引擎端调用该 API 即可实现索引构建功能 。对于 Spark 3.3 及以上版本 , 已经提供有索引的 SQL 语句 , 在 Iceberg 的 Spark 模块实现 Spark 提供的索引接口即可 。
  1. 构建索引
我们采用异步构建索引 , 不影响主线任务 。也提供了增量构建索引功能 , 只对 Append 数据进行构建索引 。调用 TableScan 读取数据 , 按照 data file 的 split offset 切分数据 , 进行构建索引 , 并保存索引数据和对应的元数据信息 。为了避免出现小文件存在 , 我们会进行索引数据合并 。
  1. 索引文件存储


    推荐阅读