Skip to content

Conversation

@yucai
Copy link
Contributor

@yucai yucai commented Apr 25, 2018

What changes were proposed in this pull request?

To improve the bucket join, when join keys are a super-set of bucket keys, we should avoid shuffle.

How was this patch tested?

Enable ignored test.

@yucai yucai changed the title [SPARK-24087][SQL] Avoid shuffle when join keys are a super-set of bucket keys [WIP][SPARK-24087][SQL] Avoid shuffle when join keys are a super-set of bucket keys Apr 25, 2018
@SparkQA
Copy link

SparkQA commented Apr 25, 2018

Test build #89847 has finished for PR 21156 at commit 27df568.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yucai yucai changed the title [WIP][SPARK-24087][SQL] Avoid shuffle when join keys are a super-set of bucket keys [SPARK-24087][SQL] Avoid shuffle when join keys are a super-set of bucket keys Apr 26, 2018
@SparkQA
Copy link

SparkQA commented Apr 26, 2018

Test build #89867 has finished for PR 21156 at commit b6bfdc2.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@SparkQA
Copy link

SparkQA commented Apr 26, 2018

Test build #89869 has finished for PR 21156 at commit a59c94f.

  • This patch fails due to an unknown error code, -9.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yucai
Copy link
Contributor Author

yucai commented Apr 26, 2018

retest this please

@SparkQA
Copy link

SparkQA commented Apr 26, 2018

Test build #89875 has finished for PR 21156 at commit a59c94f.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@SparkQA
Copy link

SparkQA commented Jun 4, 2018

Test build #91434 has finished for PR 21156 at commit 4e026e5.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@SparkQA
Copy link

SparkQA commented Jun 4, 2018

Test build #91435 has finished for PR 21156 at commit fa76a78.

  • This patch fails due to an unknown error code, -9.
  • This patch merges cleanly.
  • This patch adds no public classes.

@SparkQA
Copy link

SparkQA commented Jun 4, 2018

Test build #91436 has finished for PR 21156 at commit 946688a.

  • This patch fails due to an unknown error code, -9.
  • This patch merges cleanly.
  • This patch adds no public classes.

@SparkQA
Copy link

SparkQA commented Jul 6, 2018

Test build #92669 has finished for PR 21156 at commit 981a0fd.

  • This patch fails due to an unknown error code, -9.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yucai
Copy link
Contributor Author

yucai commented Jul 6, 2018

I run the failed commands locally, no issue, retest again.

info] SHA-1: 4e68a043f1e9aa75e4395aba7892196e90cca1f4
[info] Packaging /mnt/work/upstream/spark/external/kinesis-asl-assembly/target/scala-2.11/spark-streaming-kinesis-asl-assembly-2.4.0-SNAPSHOT.jar ...
[info] Done packaging.
[success] Total time: 29 s, completed Jul 6, 2018 12:32:58 AM
[yyu1@yucai-dev-1659859 spark]$ ./build/sbt -Phadoop-2.6 -Pkubernetes -Phive-thriftserver -Pflume -Pkinesis-asl -Pyarn -Pkafka-0-8 -Phive -Pmesos test:package streaming-kafka-0-8-assembly/assembly streaming-flume-assembly/assembly streaming-kinesis-asl-assembly/assembly

@yucai
Copy link
Contributor Author

yucai commented Jul 6, 2018

retest this please

@yucai
Copy link
Contributor Author

yucai commented Jul 6, 2018

@cloud-fan @gatorsmile @mgaido91 @viirya Could you help review this feature?

@SparkQA
Copy link

SparkQA commented Jul 6, 2018

Test build #92674 has finished for PR 21156 at commit 981a0fd.

  • This patch fails SparkR unit tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yucai
Copy link
Contributor Author

yucai commented Jul 6, 2018

retest this please

@SparkQA
Copy link

SparkQA commented Jul 6, 2018

Test build #92682 has finished for PR 21156 at commit 981a0fd.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

if leftPartitioning.satisfies(ClusteredDistribution(leftKeys)) =>
avoidShuffleIfPossible(leftKeys, leftExpressions)

case _ => rightPartitioning match {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC if either left or right are not HashPartitioning we are sure we won't meet the required distribution, so I guess this is useless, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. The main purpose of this feature is for the bucketed table, so the HashPartitioning is enough.
Actually, with the similar way, we can skip the shuffle for one side if it is RangePartitioning also, but I am not sure if it is really useful.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that case would not be covered anyway here as we are returning that we require a HashClusteredDistribution so a RangePartitioning would never match anyway, wouldn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, we can return OrderedDistribution :: OrderedDistribution :: Nil to avoid shuffle for the RangePartitioning side.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we can do that, but anyway this case is useless...


// Enable it after fix https://issues.apache.org/jira/browse/SPARK-12704
ignore("avoid shuffle when join keys are a super-set of bucket keys") {
test("avoid shuffle when join keys are a super-set of bucket keys") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add more tests with different BucketSpec on the two sides?

private def avoidShuffleIfPossible(
joinKeys: Seq[Expression],
expressions: Seq[Expression]): Seq[Distribution] = {
val indices = expressions.map(x => joinKeys.indexWhere(_.semanticEquals(x)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if here we don't find an expression? I think it would return -1 causing an error when using the index later. Can we also add a test case for this situation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

      case HashPartitioning(leftExpressions, _)
        if leftPartitioning.satisfies(ClusteredDistribution(leftKeys)) =>
        avoidShuffleIfPossible(leftKeys, leftExpressions)

if leftPartitioning.satisfies(ClusteredDistribution(leftKeys)) has ensured expressions is a subset of joinKeys, so it would not return -1, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you're right, thanks.

@yucai
Copy link
Contributor Author

yucai commented Jul 9, 2018

@mgaido91 with this way, seems like we don't need reorderJoinPredicates anymore, any thoughts?

@SparkQA
Copy link

SparkQA commented Jul 9, 2018

Test build #92754 has finished for PR 21156 at commit 371c3a9.

  • This patch fails from timeout after a configured wait of `300m`.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yucai
Copy link
Contributor Author

yucai commented Jul 10, 2018

@cloud-fan For bucket table, the user will do the bucket on the primary key, so in this case, they will not have the parallelism and data skew issue and we can see good benefit from avoiding shuffle.
Do you mean the performance regression in some more general cases?

@yucai
Copy link
Contributor Author

yucai commented Jul 10, 2018

A classic scenario could be like below:

SELECT
  ...
FROM
  lstg_item item,
  lstg_item_vrtn v
WHERE 
  item.auct_end_dt = CAST(SUBSTR('2018-04-19 00:00:00',1,10) AS DATE)
  AND item.item_id = v.item_id
  AND item.auct_end_dt = v.auct_end_dt;

lstg_item is a really big table and item_id is its primary key.
If we bucket on its item_id:

  • No data skew. Each partition will have the same data.
  • Before this PR, the above query needs extra shuffle on big table. After this PR, we can save that shuffle.

@SparkQA
Copy link

SparkQA commented Jul 10, 2018

Test build #92810 has finished for PR 21156 at commit 6148029.

  • This patch fails Spark unit tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@SparkQA
Copy link

SparkQA commented Jul 10, 2018

Test build #92809 has finished for PR 21156 at commit 48511b7.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds the following public classes (experimental):
  • trait HashJoin extends Join
  • trait Join
  • trait ShuffledJoin extends Join

@maryannxue
Copy link
Contributor

maryannxue commented Jul 10, 2018

IMHO, this ShuffledJoin is basically join + known distribution info. So instead of adding another join node (which doesn't map to any specific join algorithm), can we try to return the right distribution for bucket tables?

@SparkQA
Copy link

SparkQA commented Jul 10, 2018

Test build #92822 has finished for PR 21156 at commit 3eee88b.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yucai
Copy link
Contributor Author

yucai commented Jul 12, 2018

@maryannxue how about this way? Any better idea?

@SparkQA
Copy link

SparkQA commented Jul 12, 2018

Test build #92909 has finished for PR 21156 at commit de2bc4d.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@yucai yucai closed this Jul 26, 2018
@yucai yucai deleted the SPARK-24087 branch July 26, 2018 14:34
@yucai yucai restored the SPARK-24087 branch July 26, 2018 14:35
@yucai
Copy link
Contributor Author

yucai commented Jul 26, 2018

closed by mistake, reopen it.

@yucai yucai reopened this Jul 26, 2018
@SparkQA
Copy link

SparkQA commented Jul 26, 2018

Test build #93601 has finished for PR 21156 at commit f406062.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@wangshisan
Copy link
Contributor

wangshisan commented Oct 9, 2018

What is the status now? I think this is of great value, since this gives users more possibility to leverage bucket join, all joins which take the bucket key as the prefix of join keys will benefit from this.
And we have a further optimization here:

  1. Table A(a1, a2, a3) is bucketed by a1, a2
  2. Table B(b1, b2, b3) is bucketed by b1.
  3. A join B on (a1=b1, a2=b2, a3=b3)

In this case, only table B needs extra shuffle, and shuffle keys are (b1, b2), shuffle partition number is table A's bucket number.

@SparkQA
Copy link

SparkQA commented Oct 22, 2018

Test build #97825 has started for PR 21156 at commit f406062.

@SparkQA
Copy link

SparkQA commented Oct 22, 2018

Test build #97855 has started for PR 21156 at commit f406062.

@SparkQA
Copy link

SparkQA commented Oct 22, 2018

Test build #97872 has started for PR 21156 at commit f406062.

@maryannxue
Copy link
Contributor

maryannxue commented Oct 22, 2018 via email

@maryannxue
Copy link
Contributor

The idea is good. Is it possible to make it an optimization rule? Another suggestion is we need more test cases.

}

val leftPartitioning = left.outputPartitioning
val rightPartitioning = right.outputPartitioning
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my biggest concern. Currently Spark adds shuffle with a rule, so we can't always get the children partitioning precisely. We implemented a similar feature in EnsureRequirements.reorderJoinPredicates, which is hacky and we should improve the framework before adding more features like this.

Copy link
Contributor Author

@yucai yucai Oct 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cloud-fan in this PR, requiredChildDistribution is always re-calculated each time it is invoked, could it be more precise than EnsureRequirements.reorderJoinPredicates

This kind of bucketjoin is common, do we have a plan to improve the framework in 3.0?

@AmplabJenkins
Copy link

Can one of the admins verify this patch?

@github-actions
Copy link

We're closing this PR because it hasn't been updated in a while. This isn't a judgement on the merit of the PR in any way. It's just a way of keeping the PR queue manageable.
If you'd like to revive this PR, please reopen it and ask a committer to remove the Stale tag!

@github-actions github-actions bot added the Stale label Jan 11, 2020
@github-actions github-actions bot closed this Jan 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants