GBase 8a数据库索引机制详解-列存引擎的原生加速机制
本文继续说明为什么列存不依赖传统 B-Tree 索引,南大通用GBase 8a数据库(gbase database) 实际使用了哪些替代机制,以及怎样在列存环境下做到真正有效的查询加速。
既然传统索引在列存里失效,GBase 8a数据库用什么机制来加速过滤查询?答案是列存引擎特有的三层过滤机制:分区裁剪、列块元数据过滤(Min-Max 剪枝)和字典过滤。这三层机制组合起来,能在不使用 B-Tree 索引的情况下大幅减少实际需要读取的数据量。
1、分区裁剪(Partition Pruning)
这是 GBase 8a 最重要的查询加速手段,在前面的文章中已多次提到。当查询带有针对分区键的范围过滤条件时,优化器直接跳过不在范围内的分区,把扫描数据量从全表缩减到若干个分区。分区裁剪发生在查询规划阶段,不需要任何索引结构,是纯粹基于元数据的静态剪枝。
分区裁剪的效果取决于分区键与查询 WHERE 条件的匹配程度。最理想的情况是 WHERE 条件精确地覆盖了分区键且不对分区键列使用函数,此时优化器能准确地确定需要扫描哪些分区,剪枝效果最佳。
2、 列块 Min-Max 过滤(Zone Map)
列存引擎在存储数据时,把每一列的数据切分成若干个固定大小的"列块(Column Block)",每个列块通常包含几万到几十万行的数据。GBase 8a 的 Express 引擎为每个列块维护一份元数据,记录该列块中值的最小值和最大值,这种机制称为 Zone Map 或 Min-Max 索引。
当查询带有等值或范围过滤条件时,引擎在扫描每个列块之前,先检查 WHERE 条件是否与该列块的 Min-Max 范围有交集:如果查询是WHERE amount > 50000,而某个列块的最大值max_amount = 10000,则该列块中不可能有满足条件的行,直接跳过,不需要解压和扫描该列块的实际数据。
Zone Map 的过滤效果与数据的物理排序有关。如果amount列的数据按升序物理排列,则低值区间的列块能被早早剪掉,过滤效果极好;如果数据完全随机分布,每个列块的 Min 到 Max 范围都很宽,Zone Map 几乎无法剪掉任何列块,过滤效果很差。这也是为什么"数据按查询过滤列有序写入"能提升查询性能——有序写入会让同一过滤范围的数据集中在少数几个列块里,Zone Map 能裁掉大量列块。
实际上,Zone Map 是列存引擎的"内置 Min-Max 索引",不需要用户显式创建,是列存存储格式本身的一部分。只要合理安排数据的写入顺序,Zone Map 的过滤效果相当于在行存数据库上建了一个覆盖索引。
3、字典过滤(Dictionary Filter)
对于使用了字典编码的低基数列(如status、dept_id、province),GBase 8a 在扫描实际数据之前可以先在字典层面过滤:把 WHERE 条件的目标值(如status = 2)映射到字典中的 ID(如字典 ID = 2),然后在列块的字典映射层面直接判断,不需要解压原始值就能确定某行是否满足条件。
字典过滤的开销极低,因为操作的是压缩后的字典 ID(通常是 1~2 字节的整数),而不是解压后的原始字符串。对于高频出现的低基数过滤条件(如状态筛选、部门筛选),字典过滤能在接近零额外开销的情况下完成过滤,是列存引擎天然具备的优化。
