Files
tidb/pkg/planner/indexadvisor/model.go

165 lines
4.7 KiB
Go

// 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
}
// 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
}
// ImpactedQuery represents the impacted query.
type ImpactedQuery struct {
Query string
Improvement float64
}
// WorkloadImpact represents the workload impact.
type WorkloadImpact struct {
WorkloadImprovement float64
}
// IndexDetail represents the detail of the index.
type IndexDetail struct {
Reason string // why recommend this index
IndexSize uint64 // byte
}
// Recommendation represents the result of the index advisor.
type Recommendation struct {
Database string
Table string
IndexName string
IndexColumns []string
IndexDetail *IndexDetail
WorkloadImpact *WorkloadImpact
TopImpactedQueries []*ImpactedQuery
}