-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
planner/core: support union all for mpp. #24287
Conversation
ec3593c
to
859273b
Compare
} | ||
ua := PhysicalUnionAll{}.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), p.blockOffset, chReqProps...) | ||
ua := PhysicalUnionAll{ | ||
mpp: prop.TaskTp == property.MppTaskType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be canUseMpp && prop.TaskTp == property.MppTaskType
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right, I will fix it
} | ||
*forest = append(*forest, p.(*PhysicalExchangeSender)) | ||
for i := 1; i < len(stack); i++ { | ||
if _, ok := stack[i].(*PhysicalUnionAll); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to adjust the columns below and above this removed UnionAll to keep same UniqueID? or replace this UnionAll with a Projection?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's unnecessary, because TiFlash won't use UniqueID
.
if canUseMpp && prop.TaskTp == property.MppTaskType { | ||
chReqProps = append(chReqProps, &property.PhysicalProperty{ | ||
ExpectedCnt: prop.ExpectedCnt, | ||
TaskTp: property.MppTaskType, | ||
}) | ||
} else { | ||
chReqProps = append(chReqProps, &property.PhysicalProperty{ExpectedCnt: prop.ExpectedCnt}) | ||
} | ||
} | ||
ua := PhysicalUnionAll{}.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), p.blockOffset, chReqProps...) | ||
ua := PhysicalUnionAll{ | ||
mpp: prop.TaskTp == property.MppTaskType, | ||
}.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), p.blockOffset, chReqProps...) | ||
ua.SetSchema(p.Schema()) | ||
if canUseMpp && prop.TaskTp == property.RootTaskType { | ||
chReqProps = make([]*property.PhysicalProperty, 0, len(p.children)) | ||
for range p.children { | ||
chReqProps = append(chReqProps, &property.PhysicalProperty{ | ||
ExpectedCnt: prop.ExpectedCnt, | ||
TaskTp: property.MppTaskType, | ||
}) | ||
} | ||
mppUA := PhysicalUnionAll{mpp: true}.Init(p.ctx, p.stats.ScaleByExpectCnt(prop.ExpectedCnt), p.blockOffset, chReqProps...) | ||
mppUA.SetSchema(p.Schema()) | ||
return []PhysicalPlan{ua, mppUA}, true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is better to clear which case will generate mpp task, and which cases will generate root task. This will avoid some errors like pointed above by kenan.
if(canUseMpp && (prop.TaskTp == property.MppTaskType|| prop.TaskTp == property.RootTaskType))
{
generate mpp task
}
if (!(canUseMpp && prop.TaskTp == property.MppTaskType))
{
generate root task
}
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a little troublesome and confusing by using a negative condition (meaning condition with "!").
Although it aligns code beautifully, I cannot figure out the meanings clearly by only reading the conditions.
planner/core/fragment.go
Outdated
s.Fragment = f | ||
err := f.init(s) | ||
return f, errors.Trace(err) | ||
func flattenPlanForUnionAll(stack []PhysicalPlan, forest *[]*PhysicalExchangeSender) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pls add some comments
func (p *PhysicalUnionAll) attach2Task(tasks ...task) task { | ||
if _, ok := tasks[0].(*mppTask); ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
even if their children are mpp tasks, the unionAll may also return root task
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in what case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in TiDBMPP
mode, union all is at tidb, then its children run in MPP.
srcCol := ch.Schema().Columns[i] | ||
srcType := srcCol.RetType | ||
if !srcType.Equal(dstType) || !(mysql.HasNotNullFlag(dstType.Flag) == mysql.HasNotNullFlag(srcType.Flag)) { | ||
exprs[i] = expression.BuildCastFunction4Union(un.ctx, srcCol, dstType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should do cast like in the join, such as decima wich different precision and (uint8 = int 32), besides, set the null property for these cast functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When comparing types, It will take "length" and "decimal" of types into consideration.
p = ch | ||
} | ||
case *PhysicalHashJoin: | ||
stack = append(stack, x.children[1-x.InnerChildIdx]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- x.children[1-x.InnerChildIdx] means probe side?
- what about other side where union may also occur?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- yes
- the other side can never have a union-all.
tk.MustQuery("select x1.a, x.a from x1 left join (select x2.b a, x1.b from x1 join x2 on x1.a = x2.b union all select * from x1 ) x on x1.a = x.a order by x1.a").Check(testkit.Rows("1 1", "1 1", "2 2", "2 2", "3 3", "3 3", "4 4", "4 4")) | ||
tk.MustQuery("select x1.a, x.a from x1 left join (select count(*) a, sum(b) b from x1 group by a union all select * from x2 ) x on x1.a = x.a order by x1.a").Check(testkit.Rows("1 1", "1 1", "1 1", "1 1", "2 2", "3 3", "4 4")) | ||
|
||
tk.MustExec("drop table if exists x3") | ||
tk.MustExec("create table x3(a int , b int);") | ||
tk.MustExec("alter table x3 set tiflash replica 1") | ||
tb = testGetTableByName(c, tk.Se, "test", "x3") | ||
err = domain.GetDomain(tk.Se).DDL().UpdateTableReplicaInfo(tk.Se, tb.Meta().ID, true) | ||
c.Assert(err, IsNil) | ||
|
||
tk.MustExec("insert into x3 values (2, 2), (2, 3), (2, 4)") | ||
// test nested union all | ||
tk.MustQuery("select count(*) from (select a, b from x1 union all select a, b from x3 union all (select x1.a, x3.b from (select * from x3 union all select * from x2) x3 left join x1 on x3.a = x1.b))").Check(testkit.Rows("14")) | ||
// test union all join union all | ||
tk.MustQuery("select count(*) from (select * from x1 union all select * from x2 union all select * from x3) x join (select * from x1 union all select * from x2 union all select * from x3) y on x.a = y.b").Check(testkit.Rows("29")) | ||
tk.MustExec("set @@session.tidb_broadcast_join_threshold_count=100000") | ||
failpoint.Enable("github.com/pingcap/tidb/executor/checkTotalMPPTasks", `return(6)`) | ||
tk.MustQuery("select count(*) from (select * from x1 union all select * from x2 union all select * from x3) x join (select * from x1 union all select * from x2 union all select * from x3) y on x.a = y.b").Check(testkit.Rows("29")) | ||
failpoint.Disable("github.com/pingcap/tidb/executor/checkTotalMPPTasks") | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- please also check plans
- genereate more complex queries, like agg, agg+join, partition key is a subset of join/group by keys.
- use randgen to test more complex cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have checked plan results in plan tests. I agree with adding more tests but It needs time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the final plan be printed? as it is important to debug according to explain [analyze]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The plan that has eliminated union-all can not be showed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can it support explain analyze now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not yet
return nil, true | ||
} | ||
canUseMpp := p.ctx.GetSessionVars().AllowMPPExecution && p.canPushToCop(kv.TiFlash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function does not deliver the partition properity, which introduce more network overhead during exchange.
It is better to deliver the partition properity, then the unionAll becomes a exchange reciver, and set different exchange sender such as pashthrough or partition. for example:
group by R.a
|
(group by S.a, S.b union scan C) as R
if use the current code, the plan becomes:
group by R.a
|
exchange receiver
|----exchange sender1[partition by S.a] - group by S.a, S.b[partition by S.a,S.B]
|----exchange sender2[partition by C.a] - Scan C
the optimal one is:
group by R.a
|
exchange receiver
|----exchange sender1[pashthrough] - group by S.a, S.b[partition by S.a]
|----exchange sender2[partition by C.a] - Scan C
here exchange sender1 will send to local node and reduce network IO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's too complicated and only is beneficial in a few situations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eliminating shuffle is an important optimization for distributed plans. If the users' sql is slow due to more unnecessary exchange, how to support this case in the future?
besides, it is not too complicated, its main idea is:
- delivery
data distribution properity
to its child, - push up unionAll until the data distribution properity does not satisfy needs, then
- enforce exchange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chould you detail the pass through exchanger? How could we granrantee the same task number between source tasks and target tasks?
} | ||
return plan | ||
} | ||
|
||
func injectProjBelowUnion(un *PhysicalUnionAll) *PhysicalUnionAll { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary? In the logical plan building stage, field cast projection has already been appended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is! The check in logical plan is looser, such as lack of "NotNull" flag check.
Co-authored-by: Zhuomin Liu <lzmhhh123@gmail.com>
@eurekaka: In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository. |
/lgtm |
@eurekaka: Please use GitHub review feature instead of For the reason we drop support to the commands, see also this page. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository. |
[REVIEW NOTIFICATION] This pull request has been approved by:
To complete the pull request process, please ask the reviewers in the list to review by filling The full list of commands accepted by this bot can be found here. Reviewer can indicate their review by submitting an approval review. |
/merge |
This pull request has been accepted and is ready to merge. Commit hash: 9a7b2a7
|
// after untwist, there will be two plans in `forest` slice: | ||
// - ExchangeSender -> Projection (c1) -> TableScan(t) | ||
// - ExchangeSender -> Projection (c2) -> TableScan(s) | ||
func untwistPlanAndRemoveUnionAll(stack []PhysicalPlan, forest *[]*PhysicalExchangeSender) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this change be seen by explain?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this can only be seen in the log.
/run-all-tests |
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
cherry pick to release-5.0 in PR #25051 |
What problem does this PR solve?
Problem Summary:
Support union all operator push down
What is changed and how it works?
Proposal: proposal of union all
What's Changed:
multiplying tasks during buidling mpp tasks.
Related changes
Check List
Tests
Release note