mirror of https://github.com/apache/druid.git
Optimize JoinCondition matching (#9200)
* Optimize JoinCondition matching The LookupJoinMatcher needs to check if a condition is always true or false multiple times. This can be pre-computed to speed up the match checking This change reduces the time it takes to perform a for joining on a long key from ~ 36 ms/op to 23 ms/ op * Rename variables * fix typo
This commit is contained in:
parent
f511af1306
commit
a2939bbd1a
|
@ -28,6 +28,7 @@ import org.apache.druid.math.expr.Parser;
|
||||||
import org.apache.druid.query.expression.ExprUtils;
|
import org.apache.druid.query.expression.ExprUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -50,6 +51,9 @@ public class JoinConditionAnalysis
|
||||||
private final String originalExpression;
|
private final String originalExpression;
|
||||||
private final List<Equality> equiConditions;
|
private final List<Equality> equiConditions;
|
||||||
private final List<Expr> nonEquiConditions;
|
private final List<Expr> nonEquiConditions;
|
||||||
|
private final boolean isAlwaysFalse;
|
||||||
|
private final boolean isAlwaysTrue;
|
||||||
|
private final boolean canHashJoin;
|
||||||
|
|
||||||
private JoinConditionAnalysis(
|
private JoinConditionAnalysis(
|
||||||
final String originalExpression,
|
final String originalExpression,
|
||||||
|
@ -58,8 +62,15 @@ public class JoinConditionAnalysis
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.originalExpression = Preconditions.checkNotNull(originalExpression, "originalExpression");
|
this.originalExpression = Preconditions.checkNotNull(originalExpression, "originalExpression");
|
||||||
this.equiConditions = equiConditions;
|
this.equiConditions = Collections.unmodifiableList(equiConditions);
|
||||||
this.nonEquiConditions = nonEquiConditions;
|
this.nonEquiConditions = Collections.unmodifiableList(nonEquiConditions);
|
||||||
|
// if any nonEquiCondition is an expression and it evaluates to false
|
||||||
|
isAlwaysFalse = nonEquiConditions.stream()
|
||||||
|
.anyMatch(expr -> expr.isLiteral() && !expr.eval(ExprUtils.nilBindings()).asBoolean());
|
||||||
|
// if there are no equiConditions and all nonEquiConditions are literals and the evaluate to true
|
||||||
|
isAlwaysTrue = equiConditions.isEmpty() && nonEquiConditions.stream()
|
||||||
|
.allMatch(expr -> expr.isLiteral() && expr.eval(ExprUtils.nilBindings()).asBoolean());
|
||||||
|
canHashJoin = nonEquiConditions.stream().allMatch(Expr::isLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,8 +152,7 @@ public class JoinConditionAnalysis
|
||||||
*/
|
*/
|
||||||
public boolean isAlwaysFalse()
|
public boolean isAlwaysFalse()
|
||||||
{
|
{
|
||||||
return nonEquiConditions.stream()
|
return isAlwaysFalse;
|
||||||
.anyMatch(expr -> expr.isLiteral() && !expr.eval(ExprUtils.nilBindings()).asBoolean());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,9 +160,7 @@ public class JoinConditionAnalysis
|
||||||
*/
|
*/
|
||||||
public boolean isAlwaysTrue()
|
public boolean isAlwaysTrue()
|
||||||
{
|
{
|
||||||
return equiConditions.isEmpty() &&
|
return isAlwaysTrue;
|
||||||
nonEquiConditions.stream()
|
|
||||||
.allMatch(expr -> expr.isLiteral() && expr.eval(ExprUtils.nilBindings()).asBoolean());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +168,7 @@ public class JoinConditionAnalysis
|
||||||
*/
|
*/
|
||||||
public boolean canHashJoin()
|
public boolean canHashJoin()
|
||||||
{
|
{
|
||||||
return nonEquiConditions.stream().allMatch(Expr::isLiteral);
|
return canHashJoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -267,7 +267,11 @@ public class JoinConditionAnalysisTest
|
||||||
{
|
{
|
||||||
EqualsVerifier.forClass(JoinConditionAnalysis.class)
|
EqualsVerifier.forClass(JoinConditionAnalysis.class)
|
||||||
.usingGetClass()
|
.usingGetClass()
|
||||||
.withIgnoredFields("equiConditions", "nonEquiConditions")
|
.withIgnoredFields(
|
||||||
|
// These fields are tightly coupled with originalExpression
|
||||||
|
"equiConditions", "nonEquiConditions",
|
||||||
|
// These fields are calculated from nonEquiConditions
|
||||||
|
"isAlwaysTrue", "isAlwaysFalse", "canHashJoin")
|
||||||
.verify();
|
.verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue