【Postgres】Semijoin估算优化
【摘要】 Improve planner's cost estimation in the presence of semijoins.
对于类似下面的语句
SELECT * FROM x WHERE x1 IN (SELECT y1 FROM y)
在使用x的索引进行参数化索引扫描的时候,循环的次数应该是不重复y1值的个数,不应该是y表的个数。提交记录如下
Improve planner's cost estimation in the presence of semijoins.
该语句在逻辑重写阶段将转化为SEMI JOIN类型查询关联语句,在函数create_index_paths中针对关联列评估索引扫描的代价(参数化索引路径),其中重要的一个是获取外表大小(Semijoin和innerjoin类似),因此可以在代价估计的时候,考虑其进行进行分组,提高优化器的估算的准确性。
该部分操作在函数adjust_rowcount_for_semijoins中完成,从函数定义上可以看出,其是给semijoin操作调整行数估计的,实现如下:
/* * Check to see if outer_relid is on the inside of any semijoin that cur_relid * is on the outside of. If so, replace rowcount with the estimated number of * unique rows from the semijoin RHS (assuming that's smaller, which it might * not be). The estimate is crude but it's the best we can do at this stage * of the proceedings. */ static double adjust_rowcount_for_semijoins(PlannerInfo *root, Index cur_relid, // 左表relid Index outer_relid, // 右表relid double rowcount) // 右表满足条件 { ListCell *lc; foreach(lc, root->join_info_list) { // 获取连接信息 SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); if (sjinfo->jointype == JOIN_SEMI && // SEMIjoin类型 bms_is_member(cur_relid, sjinfo->syn_lefthand) && // 匹配左表 bms_is_member(outer_relid, sjinfo->syn_righthand)) // 匹配右表 { /* Estimate number of unique-ified rows */ double nraw; double nunique; // 估计右表集合的行数,将所有基表估计行数相乘,通常情况下SEMI join是单表情况 nraw = approximate_joinrel_size(root, sjinfo->syn_righthand); // 估算分组后的唯一行数 nunique = estimate_num_groups(root, sjinfo->semi_rhs_exprs, nraw, NULL); // 调整估算的循环次数 if (rowcount > nunique) rowcount = nunique; } } return rowcount; }
make_outerjoin中调用compute_semijoin_info
struct SpecialJoinInfo { NodeTag type; Relids min_lefthand; /* 限制连接顺序,记录LHS最小集合 */ Relids min_righthand; /* 限制连接顺序,记录RHS最小集合 */ Relids syn_lefthand; /* Query->jointree中的LHS集合 */ Relids syn_righthand; /* Query->jointree中的RHS集合 */ JoinType jointype; /* 连接类型:Left join、Full join等 */ bool lhs_strict; /* 限制连接顺序 */ bool delay_upper_joins; /* 限制连接顺序 */ /* 下面给semijoin连接类型设置: */ bool semi_can_btree; /* 操作符是否支持索引 */ bool semi_can_hash; /* 操作符是否支持hash */ List *semi_operators; /* 连接条件中的操作符 */ List *semi_rhs_exprs; /* 连接条件中右操作数 */ };
执行如下(执行计划中已经消除了semiJoin,转化为InnerJoin)
postgres=# explain select * from test1 where a in (select * from test2); QUERY PLAN ------------------------------------------------------------------------------ Nested Loop (cost=2.66..77.60 rows=10 width=4) -> HashAggregate (cost=2.38..2.48 rows=10 width=4) Group Key: test2.a -> Seq Scan on test2 (cost=0.00..2.10 rows=110 width=4) -> Index Only Scan using a_idx on test1 (cost=0.29..7.50 rows=1 width=4) Index Cond: (a = test2.a) (6 rows)
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)