Skip to content
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

Range Query Optimization (For sequential Vindex types) #17342

Merged
merged 16 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/vtexplain/sample_schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS `t1` (
`c1` bigint unsigned NOT NULL,
`c2` bigint unsigned NOT NULL,
PRIMARY KEY (`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
20 changes: 20 additions & 0 deletions examples/vtexplain/sample_vschema.json
harshit-gangal marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"ks1": {
"sharded": true,
"vindexes": {
"binary_vdx": {
"type": "binary"
}
},
"tables": {
"t1": {
"columnVindexes": [
{
"column": "c1",
"name": "binary_vdx"
}
]
}
}
}
}
5 changes: 5 additions & 0 deletions go/vt/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ func Empty(id []byte) bool {
// KeyRange helper methods
//

// Make a Key Range
func NewKeyRange(start []byte, end []byte) *topodatapb.KeyRange {
return &topodatapb.KeyRange{Start: start, End: end}
}

// KeyRangeAdd adds two adjacent KeyRange values (in any order) into a single value. If the values are not adjacent,
// it returns false.
func KeyRangeAdd(a, b *topodatapb.KeyRange) (*topodatapb.KeyRange, bool) {
Expand Down
43 changes: 43 additions & 0 deletions go/vt/vtgate/engine/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const (
// IN is for routing a statement to a multi shard.
// Requires: A Vindex, and a multi Values.
IN
// Between is for routing a statement to a multi shard
// Requires: A Vindex, and a multi Values.
Between
harshit-gangal marked this conversation as resolved.
Show resolved Hide resolved
// MultiEqual is used for routing queries with IN with tuple clause
// Requires: A Vindex, and a multi Tuple Values.
MultiEqual
Expand Down Expand Up @@ -78,6 +81,7 @@ var opName = map[Opcode]string{
EqualUnique: "EqualUnique",
Equal: "Equal",
IN: "IN",
Between: "Between",
MultiEqual: "MultiEqual",
Scatter: "Scatter",
DBA: "DBA",
Expand Down Expand Up @@ -157,6 +161,14 @@ func (rp *RoutingParameters) findRoute(ctx context.Context, vcursor VCursor, bin
default:
return rp.in(ctx, vcursor, bindVars)
}
case Between:
switch rp.Vindex.(type) {
case vindexes.SingleColumn:
return rp.between(ctx, vcursor, bindVars)
default:
// Only SingleColumn vindex supported.
return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "opcode: %v not supported", rp.Opcode)
}
harshit-gangal marked this conversation as resolved.
Show resolved Hide resolved
case MultiEqual:
switch rp.Vindex.(type) {
case vindexes.MultiColumn:
Expand Down Expand Up @@ -396,6 +408,19 @@ func (rp *RoutingParameters) inMultiCol(ctx context.Context, vcursor VCursor, bi
return rss, shardVarsMultiCol(bindVars, mapVals, isSingleVal), nil
}

func (rp *RoutingParameters) between(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor)
value, err := env.Evaluate(rp.Values[0])
if err != nil {
return nil, nil, err
}
rss, values, err := resolveShardsBetween(ctx, vcursor, rp.Vindex.(vindexes.Between), rp.Keyspace, value.TupleValues())
if err != nil {
return nil, nil, err
}
return rss, shardVars(bindVars, values), nil
}

func (rp *RoutingParameters) multiEqual(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor)
value, err := env.Evaluate(rp.Values[0])
Expand Down Expand Up @@ -520,6 +545,24 @@ func buildMultiColumnVindexValues(shardsValues [][][]sqltypes.Value) [][][]*quer
return shardsIds
}

func resolveShardsBetween(ctx context.Context, vcursor VCursor, vindex vindexes.Between, keyspace *vindexes.Keyspace, vindexKeys []sqltypes.Value) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error) {
// Convert vindexKeys to []*querypb.Value
ids := make([]*querypb.Value, len(vindexKeys))
for i, vik := range vindexKeys {
ids[i] = sqltypes.ValueToProto(vik)
}

// RangeMap using the Vindex
destinations, err := vindex.RangeMap(ctx, vcursor, vindexKeys[0], vindexKeys[1])
if err != nil {
return nil, nil, err

}

// And use the Resolver to map to ResolvedShards.
return vcursor.ResolveDestinations(ctx, keyspace.Name, ids, destinations)
}

func shardVars(bv map[string]*querypb.BindVariable, mapVals [][]*querypb.Value) []map[string]*querypb.BindVariable {
shardVars := make([]map[string]*querypb.BindVariable, len(mapVals))
for i, vals := range mapVals {
Expand Down
21 changes: 21 additions & 0 deletions go/vt/vtgate/planbuilder/operators/sharded_routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ func (tr *ShardedRouting) resetRoutingLogic(ctx *plancontext.PlanningContext) Ro
func (tr *ShardedRouting) searchForNewVindexes(ctx *plancontext.PlanningContext, predicate sqlparser.Expr) (Routing, bool) {
newVindexFound := false
switch node := predicate.(type) {
case *sqlparser.BetweenExpr:
return tr.planBetweenOp(ctx, node)

case *sqlparser.ComparisonExpr:
return tr.planComparison(ctx, node)

Expand All @@ -234,6 +237,24 @@ func (tr *ShardedRouting) searchForNewVindexes(ctx *plancontext.PlanningContext,
return nil, newVindexFound
}

func (tr *ShardedRouting) planBetweenOp(ctx *plancontext.PlanningContext, node *sqlparser.BetweenExpr) (routing Routing, foundNew bool) {
// x BETWEEN a AND b => x >= a AND x <= b
systay marked this conversation as resolved.
Show resolved Hide resolved
column, ok := node.Left.(*sqlparser.ColName)
if !ok {
return nil, false
}
vals := []sqlparser.Expr{}
vals = append(vals, node.From, node.To)
var vdValue sqlparser.ValTuple = vals
harshit-gangal marked this conversation as resolved.
Show resolved Hide resolved
opcode := func(*vindexes.ColumnVindex) engine.Opcode { return engine.Between }
harshit-gangal marked this conversation as resolved.
Show resolved Hide resolved

val := makeEvalEngineExpr(ctx, vdValue)
if val == nil {
return nil, false
}
return nil, tr.haveMatchingVindex(ctx, node, vdValue, column, val, opcode, justTheVindex)
}

func (tr *ShardedRouting) planComparison(ctx *plancontext.PlanningContext, cmp *sqlparser.ComparisonExpr) (routing Routing, foundNew bool) {
switch cmp.Operator {
case sqlparser.EqualOp:
Expand Down
15 changes: 15 additions & 0 deletions go/vt/vtgate/vindexes/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var (
_ Reversible = (*Binary)(nil)
_ Hashing = (*Binary)(nil)
_ ParamValidating = (*Binary)(nil)
_ Between = (*Binary)(nil)
)

// Binary is a vindex that converts binary bits to a keyspace id.
Expand Down Expand Up @@ -108,6 +109,20 @@ func (*Binary) ReverseMap(_ VCursor, ksids [][]byte) ([]sqltypes.Value, error) {
return reverseIds, nil
}

// RangeMap can map ids to key.Destination objects.
func (vind *Binary) RangeMap(ctx context.Context, vcursor VCursor, startId sqltypes.Value, endId sqltypes.Value) ([]key.Destination, error) {
startKsId, err := vind.Hash(startId)
if err != nil {
return nil, err
}
endKsId, err := vind.Hash(endId)
if err != nil {
return nil, err
}
out := []key.Destination{&key.DestinationKeyRange{KeyRange: key.NewKeyRange(startKsId, endKsId)}}
return out, nil
}

// UnknownParams implements the ParamValidating interface.
func (vind *Binary) UnknownParams() []string {
return vind.unknownParams
Expand Down
15 changes: 15 additions & 0 deletions go/vt/vtgate/vindexes/binary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,18 @@ func TestBinaryReverseMap(t *testing.T) {
t.Errorf("ReverseMap(): %v, want %s", err, wantErr)
}
}

func TestBinaryRangeMap(t *testing.T) {
systay marked this conversation as resolved.
Show resolved Hide resolved

startInterval := "0x01"
endInterval := "0x10"

got, err := binOnlyVindex.(Between).RangeMap(context.Background(), nil, sqltypes.NewHexNum([]byte(startInterval)),
sqltypes.NewHexNum([]byte(endInterval)))
require.NoError(t, err)
want := "DestinationKeyRange(01-10)"
if !reflect.DeepEqual(got[0].String(), want) {
t.Errorf("RangeMap(): %+v, want %+v", got, want)
}

}
15 changes: 15 additions & 0 deletions go/vt/vtgate/vindexes/numeric.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var (
_ Reversible = (*Numeric)(nil)
_ Hashing = (*Numeric)(nil)
_ ParamValidating = (*Numeric)(nil)
_ Between = (*Numeric)(nil)
)

// Numeric defines a bit-pattern mapping of a uint64 to the KeyspaceId.
Expand Down Expand Up @@ -108,6 +109,20 @@ func (*Numeric) ReverseMap(_ VCursor, ksids [][]byte) ([]sqltypes.Value, error)
return reverseIds, nil
}

// RangeMap implements Between.
func (vind *Numeric) RangeMap(ctx context.Context, vcursor VCursor, startId sqltypes.Value, endId sqltypes.Value) ([]key.Destination, error) {
startKsId, err := vind.Hash(startId)
if err != nil {
return nil, err
}
endKsId, err := vind.Hash(endId)
if err != nil {
return nil, err
}
out := []key.Destination{&key.DestinationKeyRange{KeyRange: key.NewKeyRange(startKsId, endKsId)}}
return out, nil
}

// UnknownParams implements the ParamValidating interface.
func (vind *Numeric) UnknownParams() []string {
return vind.unknownParams
Expand Down
7 changes: 7 additions & 0 deletions go/vt/vtgate/vindexes/vindex.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ type (
ReverseMap(vcursor VCursor, ks [][]byte) ([]sqltypes.Value, error)
}

// A Between vindex is an optional interface one that maps to a keyspace range
// instead of a single keyspace id. It's being used to reduce the fan out for
// 'BETWEEN' expressions.
Between interface {
RangeMap(ctx context.Context, vcursor VCursor, startId sqltypes.Value, endId sqltypes.Value) ([]key.Destination, error)
}

systay marked this conversation as resolved.
Show resolved Hide resolved
// A Prefixable vindex is one that maps the prefix of a id to a keyspace range
// instead of a single keyspace id. It's being used to reduced the fan out for
// 'LIKE' expressions.
Expand Down
Loading