GUC系统参数gp_use_legacy_hashops可以控制建立列分布表时使用传统的哈希算法还是新的哈希算法。新的哈希算法如​​Greenplum数据库Hash分布——计算哈希值和映射segment​​文章中列出的。传统的哈希算法就是PostgreSQL数据库所提供的算法。

gp_distribution_policy

首先我们知道创建表时指定分布键就是创建Hash分布表,举个例子说明一下gp_use_legacy_hashops参数的使用。首先我们了解一下pg_am表,其存储了relation access methods(可以看成一个大的目录,heap/btree/hash都是章节),目前仅有表和索引有访问方法,直接select查询该表。我们所要关注的hashops归所于amname为hash的access methods下。因此我们仅仅需要关注其oid为405。

select * from pg_am;
oid amname amhandler amtype
2 heap heap_tableam_handler t --> table/materialied views
403 btree bthandler i --> index
405 hash hashhandler i
783 gist gisthandler i
2742 gin ginhandler i
3434 ao_row ao_row_tableam_handler t --> greenplum新增
3435 ao_column ao_column_tableam_handler t --> greenplum新增
3580 brin brinhandler i
4000 spgist spghandler i
7013

再看pg_opfamily系统表,该系统表记录了上面access methods包含的access methods operator familiy(章节中的小节,每个小节一种operator familiy)。我们知道pg_opfamily的opfmethod列是reference自pg_am oid列的,所以我们这里只列出了opfmethod为405的部分记录的operator familiy。

select oid, opfmethod, opfname from pg_opfamily;
oid opfmethod opfname
...
427 405 bpchar_ops
431 405 char_ops
435 405 date_ops
627 405 array_ops
1971 405 float_ops
...
7100 405 cdbhash_interger_ops
7101 405 cdbhash_float4_ops
7102 405 cdbhash_float8_ops
...
7130 405

再次我们看一下pg_opclass表其定义了index access method operator class,每个操作符类定义特定数据类型的索引列和特定索引访问方法的语义。我们挑一个记录看一下,可以看出cdbhash_float8_ops操作符类属于hash am大章节中、处于cdbhash_float8_ops operator familiy小节中。

select * from pg_opclass;
oid opcmethod opcname opcnamespace opcowner opcfamily opcintype opcdefault opckeytype
10160 405 cdbhash_float8_ops 11 10 7102 701 f 0
...

这时我们再来看看创建hash分布表其分布键的信息存储和分布键列类型所所使用的hash函数信息存储的系统表。创建分布表为t3,然后指定float类型的id作为分布键,通过gp_distribution_policy函数查询到其分布键为第一列,所使用的hash operator class为cdbhash_float8_ops。如下为具体执行结果。

create table t3(id float, id1 int, id2 int) distributed by (id);
select oid from pg_class where relname = 't3';
oid
24582
select * from gp_distribution_policy where localoid = 24582;
localoid policytype numsegments distkey distclass segments
24582 p 4 1 10160
select * from pg_opclass where oid = 10160;
oid opcmethod opcname opcnamespace opcowner opcfamily opcintype opcdefault opckeytype
10160 405 cdbhash_float8_ops 11 10 7102 701 f 0

Greenplum数据库数据分片策略Hash分布——GUC gp_use_legacy_hashops_哈希算法

gp_use_legacy_hashops参数涉及的分支

gp_use_legacy_hashops参数涉及cdb_get_opclass_for_column_defcdbllize_adjust_top_path和get_opclass_name_for_distribution_key三个函数。首先我们看看cdb_get_opclass_for_column_def函数,该函数定义再src/backend/cdb/cdbhash.c文件中,用于在解析CREATE TABLE和ALTER TABLE的DISTRIBUTED BY语句时向系统表查询分布键列类型对应的operator class。如果指定opclassName则直接通过opclassname调用ResolveOpClass函数查找对应的operator class,否则需要通过列类型来查询,这时就要用到gp_use_legacy_hashops参数类:如果指定该参数,先使用get_legacy_cdbhash_opclass_for_base_type确定operator class,如果没有查询到,使用cdb_default_distribution_opclass_for_type函数查询postgres原生operator class;如果没有指定该参数,直接使用cdb_default_distribution_opclass_for_type函数查询postgres原生operator class。

Oid cdb_get_opclass_for_column_def(List *opclassName, Oid attrType){
Oid opclass = InvalidOid;
if (opclassName) { opclass = ResolveOpClass(opclassName, attrType, "hash", HASH_AM_OID);
}else{
if (gp_use_legacy_hashops) opclass = get_legacy_cdbhash_opclass_for_base_type(attrType);
if (!opclass) opclass = cdb_default_distribution_opclass_for_type(attrType);
if (!OidIsValid(opclass)) ereport(ERROR,(errcode(ERRCODE_UNDEFINED_OBJECT),errmsg("data type %s has no default operator class for access method \"%s\"",format_type_be(attrType), "hash"),errhint("You must specify an operator class or define a default operator class for the data type."))); /* Same error message as in ResolveOpClass, except for the hint, for consistency. */
}
return opclass;
}

get_legacy_cdbhash_opclass_for_base_type函数其实就是调用getBaseType处理一下postgres domains自定义数据类型,然后就根据类型oid来决定operator class name(就是一个switch case,比如INT2OID就是用cdbhash_int2_ops),最后调用get_opclass_oid函数通过查询pg_opclass获取operator class name对应那行记录的oid字段。
cdb_get_opclass_for_column_def函数在ALTER TABLE SET DISTRIBUTED BY语句为表设置distribution policy的函数ATExecSetDistributedBy、CREATE TABLE AS语句基于DISTRIBUTED BY设置Query->intoPolicy的函数setQryDistributionPolicy、给定用到DistributedBy结构体构建GpPolicy的函数getPolicyForDistributedBy这三种情况下会用到。