前言
pg_hint_plan技术内幕–03 Join method中我们介绍了join method的原理,本文我们继续分析Join order即leading hint的原理。
join order在hint list中的介绍:
如下sql,默认走nestloop且pu为inner,可以使用hint Leading((pu du))指定du为inner
原理
在make_rel_from_joinlist时进入hook
OuterInnerJoinCreate获取Innerrel并将传递给hint->inner_nrels, hint->inner_joinrelids
add_paths_to_joiner_wraper会执行两次,第一次rel2为inner,第二次rel1为inner;每次执行时比较join_hint->inner_joinrelids和innerrel->relids的bitmapset是否相同。
若相同,说明和Leading hint相匹配,则set join_hint->enforce_mas也就是将选择的join method置为on,对other join Method配置disable_cost
若不同,则将所有join method的startup_cost 配置为disable_cost
最终set_cheapest(rel)计算最小代价,确定最终joinpath。
debug关键步骤:
transform_join_hints:
根据aliasname获得表的relid,pu为2,du为1
OuterInnerJoinCreate:
outerrel为pu,innerrel为du
populate_joinrel_with_paths:
第一次调用add_paths_to_joinrel(由于宏定义实际将调用add_paths_to_joinrel_wrapper)
将rel1以outer入参,rel2以inner入参。
rel1的bitmapset为(b 1)即du,rel2的bitmapset为(b 2)即pu
add_paths_to_joinrel_wrapper:
hint指定的inner的bitmapset为(b 1)即du,入参inner(rel2)的bitmapset为(b 2)即pu,两者不匹配
因此给所有join Method startup_cost赋值disable_cost
populate_joinrel_with_paths:
第二次调用add_paths_to_joinrel(由于宏定义实际将调用add_paths_to_joinrel_wrapper)
将rel2以outer入参,rel1以inner入参。
rel1的bitmapset为(b 1)即du,rel2的bitmapset为(b 2)即pu
add_paths_to_joinrel_wrapper:
hint指定的inner的bitmapset为(b 1)即du,入参inner(rel1)的bitmapset为(b 1)即du,两者匹配
因此给除Nestloop 之外的join Method startup_cost赋值disable_cost
pg_hint_plan_standard_join_search:
经过set_cheapest(rel)最小代价计算,确定最终的joinpath
小结
join order也就是leading hint的逻辑相对复杂一点,从原理上看也是在hook函数中,将不同于leading hint的join path的startup_cost配置为disable_cost,这样最小代价就是hint所指定的join顺序,就实现了指定join顺序。