Skip to content

Commit

Permalink
planner: move index advisor into the kernel (#55820)
Browse files Browse the repository at this point in the history
ref #12303
  • Loading branch information
qw4990 authored Sep 4, 2024
1 parent 638c05f commit e00454a
Show file tree
Hide file tree
Showing 11 changed files with 1,699 additions and 0 deletions.
3 changes: 3 additions & 0 deletions pkg/planner/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ go_library(
"//pkg/planner/core",
"//pkg/planner/core/base",
"//pkg/planner/core/resolve",
"//pkg/planner/indexadvisor",
"//pkg/planner/property",
"//pkg/planner/util/debugtrace",
"//pkg/planner/util/optimizetrace",
"//pkg/privilege",
"//pkg/sessionctx",
"//pkg/sessionctx/variable",
Expand Down
48 changes: 48 additions & 0 deletions pkg/planner/indexadvisor/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "indexadvisor",
srcs = [
"model.go",
"optimizer.go",
"utils.go",
],
importpath = "github.com/pingcap/tidb/pkg/planner/indexadvisor",
visibility = ["//visibility:public"],
deps = [
"//pkg/domain",
"//pkg/infoschema",
"//pkg/meta/model",
"//pkg/parser",
"//pkg/parser/ast",
"//pkg/parser/model",
"//pkg/parser/mysql",
"//pkg/parser/opcode",
"//pkg/planner/util/fixcontrol",
"//pkg/sessionctx",
"//pkg/types",
"//pkg/types/parser_driver",
"//pkg/util/logutil",
"//pkg/util/parser",
"//pkg/util/set",
"@org_uber_go_zap//:zap",
],
)

go_test(
name = "indexadvisor_test",
timeout = "short",
srcs = [
"optimizer_test.go",
"utils_test.go",
],
flaky = True,
shard_count = 17,
deps = [
":indexadvisor",
"//pkg/parser/mysql",
"//pkg/testkit",
"//pkg/util/set",
"@com_github_stretchr_testify//require",
],
)
137 changes: 137 additions & 0 deletions pkg/planner/indexadvisor/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2024 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package indexadvisor

import (
"fmt"
"math"
"strings"
)

// Query represents a Query statement.
type Query struct { // DQL or DML
Alias string
SchemaName string
Text string
Frequency int
CostPerMon float64
}

// Key returns the key of the Query.
func (q Query) Key() string {
return q.Text
}

// Column represents a column.
type Column struct {
SchemaName string
TableName string
ColumnName string
}

// NewColumn creates a new column.
func NewColumn(schemaName, tableName, columnName string) Column {
return Column{SchemaName: strings.ToLower(schemaName),
TableName: strings.ToLower(tableName), ColumnName: strings.ToLower(columnName)}
}

// NewColumns creates new columns.
func NewColumns(schemaName, tableName string, columnNames ...string) []Column {
cols := make([]Column, 0, len(columnNames))
for _, col := range columnNames {
cols = append(cols, NewColumn(schemaName, tableName, col))
}
return cols
}

// Key returns the key of the column.
func (c Column) Key() string {
return fmt.Sprintf("%v.%v.%v", c.SchemaName, c.TableName, c.ColumnName)
}

// Index represents an index.
type Index struct {
SchemaName string
TableName string
IndexName string
Columns []Column
}

// NewIndex creates a new index.
func NewIndex(schemaName, tableName, indexName string, columns ...string) Index {
return Index{SchemaName: strings.ToLower(schemaName), TableName: strings.ToLower(tableName),
IndexName: strings.ToLower(indexName), Columns: NewColumns(schemaName, tableName, columns...)}
}

// NewIndexWithColumns creates a new index with columns.
func NewIndexWithColumns(indexName string, columns ...Column) Index {
names := make([]string, len(columns))
for i, col := range columns {
names[i] = col.ColumnName
}
return NewIndex(columns[0].SchemaName, columns[0].TableName, indexName, names...)
}

// Key returns the key of the index.
func (i Index) Key() string {
names := make([]string, 0, len(i.Columns))
for _, col := range i.Columns {
names = append(names, col.ColumnName)
}
return fmt.Sprintf("%v.%v(%v)", i.SchemaName, i.TableName, strings.Join(names, ","))
}

// PrefixContain returns whether j is a prefix of i.
func (i Index) PrefixContain(j Index) bool {
if i.SchemaName != j.SchemaName || i.TableName != j.TableName || len(i.Columns) < len(j.Columns) {
return false
}
for k := range j.Columns {
if i.Columns[k].ColumnName != j.Columns[k].ColumnName {
return false
}
}
return true
}

// IndexSetCost is the cost of a index configuration.
type IndexSetCost struct {
TotalWorkloadQueryCost float64
TotalNumberOfIndexColumns int
IndexKeysStr string // IndexKeysStr is the string representation of the index keys.
}

// Less returns whether the cost of c is less than the cost of other.
func (c IndexSetCost) Less(other IndexSetCost) bool {
if c.TotalWorkloadQueryCost == 0 { // not initialized
return false
}
if other.TotalWorkloadQueryCost == 0 { // not initialized
return true
}
cc, cOther := c.TotalWorkloadQueryCost, other.TotalWorkloadQueryCost
if math.Abs(cc-cOther) > 10 && math.Abs(cc-cOther)/math.Max(cc, cOther) > 0.001 {
// their cost is very different, then the less cost, the better.
return cc < cOther
}

if c.TotalNumberOfIndexColumns != other.TotalNumberOfIndexColumns {
// if they have the same cost, then the less columns, the better.
return c.TotalNumberOfIndexColumns < other.TotalNumberOfIndexColumns
}

// to make the result stable.
return c.IndexKeysStr < other.IndexKeysStr
}
Loading

0 comments on commit e00454a

Please sign in to comment.