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

planner: move index advisor into the kernel #55820

Merged
merged 29 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
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
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