65.3. 可扩展性 #
BRIN 接口具有较高层次的抽象,访问方法实现者只需实现被访问数据类型的语义。BRIN 层本身负责并发、日志记录以及搜索索引结构。
要让一种 BRIN 访问方法工作起来,只需实现少数几个用户定义的方法,它们定义索引中存储的摘要值的行为,以及这些摘要值与扫描键之间的交互。简言之,BRIN 把可扩展性与通用性、代码重用以及清晰的接口结合在一起。
在 BRIN 中,操作符类必须提供以下四个方法:
BrinOpcInfo *opcInfo(Oid type_oid)返回索引列摘要数据的内部信息。返回值必须指向一个由 palloc 分配的
BrinOpcInfo,其定义如下:typedef struct BrinOpcInfo { /* 此操作符类在一个索引列中存储的列数 */ uint16 oi_nstored; /* 供操作符类私有使用的不透明指针 */ void *oi_opaque; /* 所存储列的类型缓存条目 */ TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER]; } BrinOpcInfo;BrinOpcInfo.oi_opaque可供操作符类例程在索引扫描期间于各支持函数之间传递信息。bool consistent(BrinDesc *bdesc, BrinValues *column, ScanKey key)返回该 ScanKey 是否与某个范围给定的索引值一致。要使用的属性编号作为扫描键的一部分传入。
bool addValue(BrinDesc *bdesc, BrinValues *column, Datum newval, bool isnull)给定一个索引元组和一个被索引值,修改该元组中指定的属性,使其能够额外表示这个新值。如果对该元组做了任何修改,就返回
true。bool unionTuples(BrinDesc *bdesc, BrinValues *a, BrinValues *b)合并两个索引元组。给定两个索引元组,修改第一个元组中指定的属性,使其能够表示这两个元组。第二个元组不会被修改。
核心发行版包含两种操作符类支持:minmax 和 inclusion。针对核心数据类型,发行版会酌情附带使用它们的操作符类定义。用户也可以为其他数据类型定义等价的操作符类,而无需编写任何源代码;只要声明适当的系统目录项即可。注意,对操作符策略语义的某些假设嵌入在支持函数的源码中。
只要为上文所述的四个主要支持函数编写实现,也可以实现语义完全不同的操作符类。注意,不保证跨主版本的向后兼容性:例如,在后续版本中可能需要附加的支持函数。
要为实现全序集的数据类型编写操作符类,可以按表 65.2 所示,将 minmax 支持函数与相应操作符一起使用。所有操作符类成员(函数和操作符)都是必需的。
表 65.2. Minmax 操作符类的函数和支持编号
| 操作符类成员 | 对象 |
|---|---|
| 支持函数 1 | 内部函数 brin_minmax_opcinfo() |
| 支持函数 2 | 内部函数 brin_minmax_add_value() |
| 支持函数 3 | 内部函数 brin_minmax_consistent() |
| 支持函数 4 | 内部函数 brin_minmax_union() |
| 操作符策略 1 | 小于操作符 |
| 操作符策略 2 | 小于等于操作符 |
| 操作符策略 3 | 等于操作符 |
| 操作符策略 4 | 大于等于操作符 |
| 操作符策略 5 | 大于操作符 |
要为一种值能够被包含在另一种类型中的复杂数据类型编写操作符类,可以按表 65.3 所示,将 inclusion 支持函数与相应操作符一起使用。它只需要额外一个函数,而且该函数可以用任何语言编写。还可以定义更多函数以提供附加功能。所有操作符都是可选的。某些操作符依赖其他操作符,表中已列出这些依赖关系。
表 65.3. Inclusion 操作符类的函数和支持编号
| 操作符类成员 | 对象 | 依赖关系 |
|---|---|---|
| 支持函数 1 | 内部函数 brin_inclusion_opcinfo() | |
| 支持函数 2 | 内部函数 brin_inclusion_add_value() | |
| 支持函数 3 | 内部函数 brin_inclusion_consistent() | |
| 支持函数 4 | 内部函数 brin_inclusion_union() | |
| 支持函数 11 | 合并两个元素的函数 | |
| 支持函数 12 | 可选函数,检查两个元素是否可以合并 | |
| 支持函数 13 | 可选函数,检查一个元素是否被包含在另一个中 | |
| 支持函数 14 | 可选函数,用于检查元素是否为空 | |
| 操作符策略 1 | 位于左侧的操作符 | 操作符策略 4 |
| 操作符策略 2 | 不延伸到右侧的操作符 | 操作符策略 5 |
| 操作符策略 3 | 重叠操作符 | |
| 操作符策略 4 | 不延伸到左侧的操作符 | 操作符策略 1 |
| 操作符策略 5 | 位于右侧的操作符 | 操作符策略 2 |
| 操作符策略 6、18 | 相同或等于操作符 | 操作符策略 7 |
| 操作符策略 7、13、16、24、25 | 包含或等于操作符 | |
| 操作符策略 8、14、26、27 | 被包含或等于操作符 | 操作符策略 3 |
| 操作符策略 9 | 不延伸到上方的操作符 | 操作符策略 11 |
| 操作符策略 10 | 位于下方的操作符 | 操作符策略 12 |
| 操作符策略 11 | 位于上方的操作符 | 操作符策略 9 |
| 操作符策略 12 | 不延伸到下方的操作符 | 操作符策略 10 |
| 操作符策略 20 | 小于操作符 | 操作符策略 5 |
| 操作符策略 21 | 小于等于操作符 | 操作符策略 5 |
| 操作符策略 22 | 大于操作符 | 操作符策略 1 |
| 操作符策略 23 | 大于等于操作符 | 操作符策略 1 |
支持函数编号 1 到 10 保留给 BRIN 内部函数,因此 SQL 层函数从编号 11 开始。支持函数 11 是构建索引所需的主要函数。它应接受两个与操作符类数据类型相同的参数,并返回它们的并集。如果 inclusion 操作符类在定义时使用了 STORAGE 参数,则它可以存储具有不同数据类型的并集值。并集函数的返回值应与 STORAGE 数据类型匹配。
支持函数编号 12 和 14 用于支持内置数据类型中的特殊情形。编号 12 的函数用于支持无法合并的不同族的网络地址。编号 14 的函数用于支持空范围。编号 13 的函数是可选的,但建议提供,以便在把新值传给并集函数之前对其进行检查。由于 BRIN 框架在并集未改变时可以跳过某些操作,因此使用这个函数可以提高索引性能。
minmax 和 inclusion 操作符类都支持跨数据类型操作符,但这样依赖关系会更加复杂。minmax 操作符类要求定义一整套两侧参数都具有相同数据类型的操作符。它还允许通过定义额外的操作符集合来支持附加数据类型。如表 65.3 所示,inclusion 操作符类的操作符策略依赖于另一种操作符策略,或者依赖于它们自身对应的同名操作符策略。这要求把依赖操作符定义为:左侧参数是 STORAGE 数据类型,右侧参数是其他受支持的数据类型。minmax 的示例见 float4_minmax_ops,inclusion 的示例见
box_inclusion_ops。