predicate_refuted_by 和 make_new_qual_list的运行逻辑以及PARAM 无法选出一个节点的原因
make_new_qual_list需要使用表达式和分区信息构造新的表达式,但是PARAM不是常量,所以无法构造, 所以predicate_refuted_by判定变大时和约束的时候无法正确的选择节点,动态语句只要使用到分区键都不行
node_count = 2
Table "public.bmsql_new_order"
Column | Type | Nullable | Storage |
---------+---------+----------+---------+
no_w_id | integer | not null | plain |
no_d_id | integer | not null | plain |
no_o_id | integer | not null | plain |
Indexes:
"bmsql_new_order_pkey" PRIMARY KEY, btree (no_w_id, no_d_id, no_o_id)
Foreign-key constraints:
"no_order_fkey" FOREIGN KEY (no_w_id, no_d_id, no_o_id) REFERENCES bmsql_oorder(o_w_id, o_d_id, o_id)
DISTRIBUTE BY HASH(no_w_id) TO NODE(dn1, dn2)
Access method: heap
数据分布
benchmarksql=# execute direct on (dn1) 'select distinct no_w_id from public.bmsql_new_order';
no_w_id
---------
1
5
2
(3 rows)
benchmarksql=# execute direct on (dn2) 'select distinct no_w_id from public.bmsql_new_order';
no_w_id
---------
4
3
(2 rows)
语句select no_o_id from bmsql_new_order where no_w_id = 4 and no_d_id = 1 order by no_o_id asc;
========================================
(no_w_id = 4 and no_d_id = 1)
COALESCE(hash_combin_mod(2, hash(no_w_id)), 0) = 1 and (no_d_id = 1)
hash(no_w_id) % node_count = hash(4) % node_count
===============
(no_w_id, no_d_id, no_o_id) is not null
XC_NODE_ID is not null
ABLE.XC_NODE_ID=id
COALESCE(hash_combin_mod(2, hash(no_w_id)), 0) = 0
第一轮,检测条件
- 限制条件为
1. 主键不为NULL,
2. 节点XC_NODE_ID不为null
3. XC_NODE_ID=loc_info.nodeid
4. hash(no_w_id) % node_count = 0
- 输入表达式为
1. (no_w_id = 4 and no_d_id = 1)
2. (hash(no_w_id) % node_count = 1) and no_d_id = 1 (语句条件根据分布方式计算的hash)
由于hash出来判断的节点不一致,所以无法通过判断
=============================================
(no_w_id, no_d_id, no_o_id) is not null
XC_NODE_ID is not null
XC_NODE_ID=id
COALESCE(hash_combin_mod(2, hash(no_w_id)), 0) = 1
(no_w_id = 4 and no_d_id = 1)
COALESCE(hash_combin_mod(2, hash(no_w_id)), 0) = 1 and (no_d_id = 1)
第而轮,检测条件
- 限制条件为
1. 主键不为NULL,
2. 节点XC_NODE_ID不为null
3. XC_NODE_ID=loc_info.nodeid
4. hash(no_w_id) % node_count = 1
- 输入表达式为
1. (no_w_id = 4 and no_d_id = 1)
2. (hash(no_w_id) % node_count = 1) and no_d_id = 1 (语句条件根据分布方式计算的hash)
判定表达式是否可以通过约束的检查
判定通过
// 递归地检查子句clause_list中的子句是否驳斥给定的predicate_list(也就是说,证明它为 false)。
/*
predicate_list
* no_w_id is not null
* no_d_id is not null
* no_o_id is not null
* XC_NODE_ID is not null
* XC_NODE_ID = nodeid
* hash(no_w_id) % node_count = 0
clause_list
* no_w_id = 4
* no_d_id = 1
* hash(no_w_id) % node_count = 1
* no_d_id = 1
*/
/*
predicate_list
* no_w_id is not null
* no_d_id is not null
* no_o_id is not null
* XC_NODE_ID is not null
* XC_NODE_ID = nodeid
* hash(no_w_id) % node_count = 0
clause_list
* no_w_id = $1
* no_d_id = $2
*/
predicate_refuted_by(*predicate_list, *clause_list) {
predicate_refuted_by_recurse() {
pclass = predicate_classify(predicate, &pred_info);
// 拆解表达式,最终不能拆解的表达书为atom
switch (predicate_classify(clause, &clause_info)) {
case CLASS_AND:
switch (pclass) {
case CLASS_AND:
iterate_begin(pitem, predicate, pred_info) {
if (predicate_refuted_by_recurse(clause, pitem, weak)) {
result = true;
break;
}
}
iterate_begin(citem, clause, clause_info) {
if (predicate_refuted_by_recurse(citem, predicate, weak)) {
result = true;
break;
}
}
case CLASS_OR:
case CLASS_ATOM:
not_arg = extract_not_arg(predicate);
if (not_arg && predicate_implied_by_recurse(clause, not_arg, false))
return true;
iterate_begin(citem, clause, clause_info) {
if (predicate_refuted_by_recurse(citem, predicate, weak)) {
result = true;
break;
}
}
}
break;
.....
case CLASS_ATOM: {
.....
case CLASS_ATOM:
// 使用此方法判断最终的atom表达式
return predicate_refuted_by_simple_clause((Expr *) predicate, clause, weak) {
//
if (predicate && IsA(predicate, NullTest) && ((NullTest *) predicate)->nulltesttype == IS_NULL) {
// 判断null,argisrow直接false,一个是is not null, 拎一个是is null,则直接true
}
// 对简单表达式的判断,简单表达式这里指的是 op(left, right)
return operator_predicate_proof(predicate, clause, true, weak) {
// no_w_id is not null 从这里跳出
if (!is_opclause(predicate))
return false;
pred_opexpr = (OpExpr *) predicate;
if (list_length(pred_opexpr->args) != 2)
return false;
....(do same as clause)
if (pred_opexpr->inputcollid != clause_opexpr->inputcollid)
return false;
if (equal(pred_leftop, clause_leftop)) {
if (equal(pred_rightop, clause_rightop)) {
return operator_same_subexprs_proof(pred_op, clause_op, refute_it);
} else {
if (pred_rightop == NULL || !IsA(pred_rightop, Const))
return false;
pred_const = (Const *) pred_rightop;
....
// 下面使用const_v构造一个 (l_const != r_cont)并计算,直接返回计算结果,下面的步骤类似
// 使用
// hash(no_w_id) % node_count = 0
// 和
// hash(no_w_id) % node_count = 1
// 为例。最终执行到这里,const对比不一样,返回false
}
} else if (equal(pred_rightop, clause_rightop)) {
} else if (equal(pred_leftop, clause_rightop)) {
} else if (equal(pred_rightop, clause_leftop)) {
} else {
return false;
}
}
}
}
}
}
}
/*
约束使用分区方式和节点序号构造表达式,查询的where表达式需要判断节点,也需要构造类似的表达式
*/
make_new_qual_list() {
/*
* let column=val to (like) hash(column)%remote_count = hash(value)%remote_count to hash(column)%remote_count = new_value
* column [not] in (v1,v2) to hash(column)%remote [not] in (new_v1,new_v2)
*/
// 表达式使用分区信息构造 新的clause,规则如上
// 例如 (no_w_id = 4 and no_d_id = 1),使用表的分区信息会对no_w_id = 4构造一个新的表达式,
// hash(column)%remote_count = hash(value)%remote_count
// hash(no_w_id)%remote_count = hash(4)%remote_count
// 所以最终的clause构造出来是 (hash(no_w_id) % node_count = 1)
// 动态参数时,此处无法构造
mutator_equal_expr() {
get_var_equal_const(op->args, op->opno, context->relid, context->varattno, &var) == null;
goto next_mutator_equal_expr_;
next_mutator_equal_expr_:
return expression_tree_mutator(node, mutator_equal_expr, context);
}
}
Read other posts