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

ddl: support add global index operation on partition table #18402

Merged
merged 23 commits into from
Aug 21, 2020
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
47f82e1
ddl: support add global index
ldeng-ustc Jul 5, 2020
55b09e2
Merge branch 'master' of github.com:pingcap/tidb
ldeng-ustc Jul 7, 2020
58f90b7
ddl: add config value 'EnableGlobalIndex'
ldeng-ustc Jul 9, 2020
5d47ec6
Merge branch 'master' of github.com:pingcap/tidb into global_index
ldeng-ustc Jul 9, 2020
b15b1f2
fix
ldeng-ustc Jul 9, 2020
2e04b6c
tests: update testcase for creating global index
ldeng-ustc Jul 9, 2020
e5284c6
go.mod: updade parser to support IndexInfo.Global
ldeng-ustc Jul 9, 2020
2faf246
go.mod: updade parser to support IndexInfo.Global
ldeng-ustc Jul 9, 2020
505a620
fix
ldeng-ustc Jul 10, 2020
19b0a97
Merge branch 'master' of github.com:pingcap/tidb into global_index
ldeng-ustc Jul 27, 2020
2febc3a
tablecodec: refactor GenIndexValue for extensibility
ldeng-ustc Jul 27, 2020
9ef4eab
tablecodec: add decodeIndexKvGeneral for extensible index decoding
ldeng-ustc Aug 1, 2020
c933af0
tablecodec: use decodeIndexKvGeneral in DecodeIndexKV
ldeng-ustc Aug 2, 2020
5616d86
Merge branch 'master' of github.com:pingcap/tidb into global_index
ldeng-ustc Aug 2, 2020
be23f72
table: refine comments of index value layout
ldeng-ustc Aug 2, 2020
2ad6198
fix comment
ldeng-ustc Aug 2, 2020
4d99cc3
fix fmt
ldeng-ustc Aug 3, 2020
3dab738
Merge remote-tracking branch 'pingcap/master' into global_index
ldeng-ustc Aug 17, 2020
cf92788
fix comment
ldeng-ustc Aug 18, 2020
3d75458
fix make check fail
ldeng-ustc Aug 18, 2020
4286a90
Merge remote-tracking branch 'pingcap/master' into global_index
ldeng-ustc Aug 20, 2020
516bbf0
Merge branch 'master' of github.com:pingcap/tidb into global_index
ldeng-ustc Aug 20, 2020
2862e06
Merge branch 'master' into global_index
ti-srebot Aug 21, 2020
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 config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ type Config struct {
SkipRegisterToDashboard bool `toml:"skip-register-to-dashboard" json:"skip-register-to-dashboard"`
// EnableTelemetry enables the usage data report to PingCAP.
EnableTelemetry bool `toml:"enable-telemetry" json:"enable-telemetry"`
// EnableGlobalIndex enables creating global index.
EnableGlobalIndex bool `toml:"enable-global-index" json:"enable-global-index"`
}

// UpdateTempStoragePath is to update the `TempStoragePath` if port/statusPort was changed
Expand Down Expand Up @@ -702,6 +704,7 @@ var defaultConf = Config{
},
EnableCollectExecutionInfo: true,
EnableTelemetry: true,
EnableGlobalIndex: false,
}

var (
Expand Down
137 changes: 137 additions & 0 deletions ddl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ import (
"github.com/pingcap/tidb/tablecodec"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/admin"
"github.com/pingcap/tidb/util/codec"
"github.com/pingcap/tidb/util/collate"
"github.com/pingcap/tidb/util/domainutil"
"github.com/pingcap/tidb/util/israce"
"github.com/pingcap/tidb/util/mock"
"github.com/pingcap/tidb/util/rowcodec"
"github.com/pingcap/tidb/util/testkit"
"github.com/pingcap/tidb/util/testutil"
)
Expand Down Expand Up @@ -1763,6 +1765,141 @@ func (s *testDBSuite4) TestAddIndexWithDupCols(c *C) {
tk.MustExec("drop table test_add_index_with_dup")
}

// checkGlobalIndexRow reads one record from global index and check. Only support int handle.
func checkGlobalIndexRow(c *C, ctx sessionctx.Context, tblInfo *model.TableInfo, indexInfo *model.IndexInfo,
pid int64, idxVals []types.Datum, rowVals []types.Datum) {
ctx.NewTxn(context.Background())
txn, err := ctx.Txn(true)
sc := ctx.GetSessionVars().StmtCtx
c.Assert(err, IsNil)

tblColMap := make(map[int64]*types.FieldType, len(tblInfo.Columns))
for _, col := range tblInfo.Columns {
tblColMap[col.ID] = &col.FieldType
}
idxColInfos := make([]rowcodec.ColInfo, 0, len(indexInfo.Columns))
for _, idxCol := range indexInfo.Columns {
col := tblInfo.Columns[idxCol.Offset]
idxColInfos = append(idxColInfos, rowcodec.ColInfo{
ID: col.ID,
IsPKHandle: tblInfo.PKIsHandle && mysql.HasPriKeyFlag(col.Flag),
Ft: rowcodec.FieldTypeFromModelColumn(col),
})
}

// Check local index entry does not exist.
localPrefix := tablecodec.EncodeTableIndexPrefix(pid, indexInfo.ID)
it, err := txn.Iter(localPrefix, nil)
c.Assert(err, IsNil)
// no local index entry.
c.Assert(it.Valid() && it.Key().HasPrefix(localPrefix), IsFalse)
it.Close()

// Check global index entry.
encodedValue, err := codec.EncodeKey(sc, nil, idxVals...)
c.Assert(err, IsNil)
key := tablecodec.EncodeIndexSeekKey(tblInfo.ID, indexInfo.ID, encodedValue)
c.Assert(err, IsNil)
value, err := txn.Get(context.Background(), key)
c.Assert(err, IsNil)
colVals, err := tablecodec.DecodeIndexKV(key, value, len(indexInfo.Columns),
tablecodec.HandleDefault, idxColInfos)
c.Assert(err, IsNil)
c.Assert(colVals, HasLen, len(idxVals)+2)
for i, val := range idxVals {
_, d, err := codec.DecodeOne(colVals[i])
c.Assert(err, IsNil)
c.Assert(d, DeepEquals, val)
}
_, d, err := codec.DecodeOne(colVals[len(idxVals)+1]) //pid
c.Assert(err, IsNil)
c.Assert(d.GetInt64(), Equals, pid)

_, d, err = codec.DecodeOne(colVals[len(idxVals)]) //handle
c.Assert(err, IsNil)
h := kv.IntHandle(d.GetInt64())
rowKey := tablecodec.EncodeRowKey(pid, h.Encoded())
rowValue, err := txn.Get(context.Background(), rowKey)
c.Assert(err, IsNil)
rowValueDatums, err := tablecodec.DecodeRow(rowValue, tblColMap, time.UTC)
c.Assert(err, IsNil)
c.Assert(rowValueDatums, NotNil)
for i, val := range rowVals {
c.Assert(rowValueDatums[tblInfo.Columns[i].ID], DeepEquals, val)
}
}

func (s *testSerialDBSuite) TestAddGlobalIndex(c *C) {
config.UpdateGlobal(func(conf *config.Config) {
conf.EnableGlobalIndex = true
})
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test_db")
tk.MustExec("create table test_t1 (a int, b int) partition by range (b)" +
" (partition p0 values less than (10), " +
" partition p1 values less than (maxvalue));")
tk.MustExec("insert test_t1 values (1, 1)")
tk.MustExec("alter table test_t1 add unique index p_a (a);")
tk.MustExec("insert test_t1 values (2, 11)")
t := s.testGetTable(c, "test_t1")
tblInfo := t.Meta()
indexInfo := tblInfo.FindIndexByName("p_a")
c.Assert(indexInfo, NotNil)
c.Assert(indexInfo.Global, IsTrue)

ctx := s.s.(sessionctx.Context)
ctx.NewTxn(context.Background())
txn, err := ctx.Txn(true)
c.Assert(err, IsNil)

// check row 1
pid := tblInfo.Partition.Definitions[0].ID
idxVals := []types.Datum{types.NewDatum(1)}
rowVals := []types.Datum{types.NewDatum(1), types.NewDatum(1)}
checkGlobalIndexRow(c, ctx, tblInfo, indexInfo, pid, idxVals, rowVals)

// check row 2
pid = tblInfo.Partition.Definitions[1].ID
idxVals = []types.Datum{types.NewDatum(2)}
rowVals = []types.Datum{types.NewDatum(2), types.NewDatum(11)}
checkGlobalIndexRow(c, ctx, tblInfo, indexInfo, pid, idxVals, rowVals)
txn.Commit(context.Background())
tiancaiamao marked this conversation as resolved.
Show resolved Hide resolved

// Test add global Primary Key index
tk.MustExec("create table test_t2 (a int, b int) partition by range (b)" +
" (partition p0 values less than (10), " +
" partition p1 values less than (maxvalue));")
tk.MustExec("insert test_t2 values (1, 1)")
tk.MustExec("alter table test_t2 add primary key (a);")
tk.MustExec("insert test_t2 values (2, 11)")
t = s.testGetTable(c, "test_t2")
tblInfo = t.Meta()
indexInfo = t.Meta().FindIndexByName("primary")
c.Assert(indexInfo, NotNil)
c.Assert(indexInfo.Global, IsTrue)

ctx.NewTxn(context.Background())
txn, err = ctx.Txn(true)
c.Assert(err, IsNil)

// check row 1
pid = tblInfo.Partition.Definitions[0].ID
idxVals = []types.Datum{types.NewDatum(1)}
rowVals = []types.Datum{types.NewDatum(1), types.NewDatum(1)}
checkGlobalIndexRow(c, ctx, tblInfo, indexInfo, pid, idxVals, rowVals)

// check row 2
pid = tblInfo.Partition.Definitions[1].ID
idxVals = []types.Datum{types.NewDatum(2)}
rowVals = []types.Datum{types.NewDatum(2), types.NewDatum(11)}
checkGlobalIndexRow(c, ctx, tblInfo, indexInfo, pid, idxVals, rowVals)

txn.Commit(context.Background())
config.UpdateGlobal(func(conf *config.Config) {
conf.EnableGlobalIndex = false
})
}

func (s *testDBSuite) showColumns(tk *testkit.TestKit, c *C, tableName string) [][]interface{} {
return s.mustQuery(tk, c, fmt.Sprintf("show columns from %s", tableName))
}
Expand Down
29 changes: 25 additions & 4 deletions ddl/ddl_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4260,6 +4260,7 @@ func (d *ddl) CreatePrimaryKey(ctx sessionctx.Context, ti ast.Ident, indexName m
// to job queue, the fail path logic is super fast.
// After DDL job is put to the queue, and if the check fail, TiDB will run the DDL cancel logic.
// The recover step causes DDL wait a few seconds, makes the unit test painfully slow.
// For same reason, decide whether index is global here.
indexColumns, err := buildIndexColumns(tblInfo.Columns, indexPartSpecifications)
if err != nil {
return errors.Trace(err)
Expand All @@ -4268,10 +4269,19 @@ func (d *ddl) CreatePrimaryKey(ctx sessionctx.Context, ti ast.Ident, indexName m
return err
}

global := false
if tblInfo.GetPartitionInfo() != nil {
if err := checkPartitionKeysConstraint(tblInfo.GetPartitionInfo(), indexColumns, tblInfo, true); err != nil {
ck, err := checkPartitionKeysConstraint(tblInfo.GetPartitionInfo(), indexColumns, tblInfo)
if err != nil {
return err
}
if !ck {
if !config.GetGlobalConfig().EnableGlobalIndex {
return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("PRIMARY")
}
//index columns does not contain all partition columns, must set global
global = true
}
}

// May be truncate comment here, when index comment too long and sql_mode is't strict.
Expand All @@ -4287,7 +4297,7 @@ func (d *ddl) CreatePrimaryKey(ctx sessionctx.Context, ti ast.Ident, indexName m
SchemaName: schema.Name.L,
Type: model.ActionAddPrimaryKey,
BinlogInfo: &model.HistoryInfo{},
Args: []interface{}{unique, indexName, indexPartSpecifications, indexOption, sqlMode},
Args: []interface{}{unique, indexName, indexPartSpecifications, indexOption, sqlMode, nil, global},
Priority: ctx.GetSessionVars().DDLReorgPriority,
}

Expand Down Expand Up @@ -4425,14 +4435,25 @@ func (d *ddl) CreateIndex(ctx sessionctx.Context, ti ast.Ident, keyType ast.Inde
// to job queue, the fail path logic is super fast.
// After DDL job is put to the queue, and if the check fail, TiDB will run the DDL cancel logic.
// The recover step causes DDL wait a few seconds, makes the unit test painfully slow.
// For same reason, decide whether index is global here.
indexColumns, err := buildIndexColumns(append(tblInfo.Columns, hiddenCols...), indexPartSpecifications)
if err != nil {
return errors.Trace(err)
}

global := false
if unique && tblInfo.GetPartitionInfo() != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tiny requirement question:

do we need support non-unique global index?

cc: @tiancaiamao @zz-jason @coocood

Copy link
Contributor

@lysu lysu Aug 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it's useful to support "using a non-unique index without include partition columns", maybe there is a real-world example:

create table order (
  oid_id int,
  user_id  int,
  seller_id int,
  primary key(oid, user_id),
  index uid (user_id),
) partition by hash(user_id) partition 1000

this table will conventional in the "user end side" to let the order-system query by user_id.

but the seller also want query the orders belongs itself in seller system and delivery order, and query by seller_id and it didn't know partition_id(also seller_id isn't unique in order table), and maybe we need a global non-unique index --- index sid (seller_id) to help seller request can be prunning?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. In some case global non-unique index is useful. Maybe we should support keywords Local and Global just like Oracle. @zhaox1n had submitted a PR about it, but it is closed now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR.
Support global unique index is complex enough, we should do it step by step.

if err := checkPartitionKeysConstraint(tblInfo.GetPartitionInfo(), indexColumns, tblInfo, false); err != nil {
ck, err := checkPartitionKeysConstraint(tblInfo.GetPartitionInfo(), indexColumns, tblInfo)
if err != nil {
return err
}
if !ck {
if !config.GetGlobalConfig().EnableGlobalIndex {
return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("UNIQUE INDEX")
}
//index columns does not contain all partition columns, must set global
global = true
}
}
// May be truncate comment here, when index comment too long and sql_mode is't strict.
if _, err = validateCommentLength(ctx.GetSessionVars(), indexName.String(), indexOption); err != nil {
Expand All @@ -4444,7 +4465,7 @@ func (d *ddl) CreateIndex(ctx sessionctx.Context, ti ast.Ident, keyType ast.Inde
SchemaName: schema.Name.L,
Type: model.ActionAddIndex,
BinlogInfo: &model.HistoryInfo{},
Args: []interface{}{unique, indexName, indexPartSpecifications, indexOption, hiddenCols},
Args: []interface{}{unique, indexName, indexPartSpecifications, indexOption, hiddenCols, global},
Priority: ctx.GetSessionVars().DDLReorgPriority,
}

Expand Down
6 changes: 4 additions & 2 deletions ddl/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo

var (
unique bool
global bool
indexName model.CIStr
indexPartSpecifications []*ast.IndexPartSpecification
indexOption *ast.IndexOption
Expand All @@ -409,9 +410,9 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo
)
if isPK {
// Notice: sqlMode and warnings is used to support non-strict mode.
err = job.DecodeArgs(&unique, &indexName, &indexPartSpecifications, &indexOption, &sqlMode, &warnings)
err = job.DecodeArgs(&unique, &indexName, &indexPartSpecifications, &indexOption, &sqlMode, &warnings, &global)
} else {
err = job.DecodeArgs(&unique, &indexName, &indexPartSpecifications, &indexOption, &hiddenCols)
err = job.DecodeArgs(&unique, &indexName, &indexPartSpecifications, &indexOption, &hiddenCols, &global)
}
if err != nil {
job.State = model.JobStateCancelled
Expand Down Expand Up @@ -481,6 +482,7 @@ func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK boo
indexInfo.Primary = true
}
indexInfo.Unique = unique
indexInfo.Global = global
indexInfo.ID = allocateIndexID(tblInfo)
tblInfo.Indices = append(tblInfo.Indices, indexInfo)

Expand Down
17 changes: 6 additions & 11 deletions ddl/partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -1289,7 +1289,7 @@ func checkPartitioningKeysConstraints(sctx sessionctx.Context, s *ast.CreateTabl
return nil
}

func checkPartitionKeysConstraint(pi *model.PartitionInfo, indexColumns []*model.IndexColumn, tblInfo *model.TableInfo, isPK bool) error {
func checkPartitionKeysConstraint(pi *model.PartitionInfo, indexColumns []*model.IndexColumn, tblInfo *model.TableInfo) (bool, error) {
tiancaiamao marked this conversation as resolved.
Show resolved Hide resolved
var (
partCols []*model.ColumnInfo
err error
Expand All @@ -1300,29 +1300,24 @@ func checkPartitionKeysConstraint(pi *model.PartitionInfo, indexColumns []*model
// Parse partitioning key, extract the column names in the partitioning key to slice.
partCols, err = extractPartitionColumns(partExpr, tblInfo)
if err != nil {
return err
return false, err
}
} else {
partCols = make([]*model.ColumnInfo, 0, len(pi.Columns))
for _, col := range pi.Columns {
colInfo := getColumnInfoByName(tblInfo, col.L)
if colInfo == nil {
return infoschema.ErrColumnNotExists.GenWithStackByArgs(col, tblInfo.Name)
return false, infoschema.ErrColumnNotExists.GenWithStackByArgs(col, tblInfo.Name)
}
partCols = append(partCols, colInfo)
}
}

// Every unique key on the table must use every column in the table's partitioning expression.(This
// In MySQL, every unique key on the table must use every column in the table's partitioning expression.(This
// also includes the table's primary key.)
// In TiDB, global index will be built when this constraint is not satisfied and EnableGlobalIndex is set.
// See https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html
if !checkUniqueKeyIncludePartKey(columnInfoSlice(partCols), indexColumns) {
if isPK {
return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("PRIMARY")
}
return ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("UNIQUE INDEX")
}
return nil
return checkUniqueKeyIncludePartKey(columnInfoSlice(partCols), indexColumns), nil
}

type columnNameExtractor struct {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ require (
github.com/pingcap/parser v0.0.0-20200730092557-34a468e9b774
github.com/pingcap/pd/v4 v4.0.0-rc.2.0.20200714122454-1a64f969cb3c
github.com/pingcap/sysutil v0.0.0-20200715082929-4c47bcac246a
github.com/pingcap/tidb-tools v4.0.1+incompatible
github.com/pingcap/tidb-tools v4.0.2+incompatible
github.com/pingcap/tipb v0.0.0-20200618092958-4fad48b4c8c3
github.com/prometheus/client_golang v1.5.1
github.com/prometheus/client_model v0.2.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,8 @@ github.com/pingcap/tidb-tools v4.0.0-rc.1.0.20200514040632-f76b3e428e19+incompat
github.com/pingcap/tidb-tools v4.0.0+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tidb-tools v4.0.1+incompatible h1:/Z0FEc0Os4CZ0Wun7uyg9teEwcHIhNz1iwGTIC86uhg=
github.com/pingcap/tidb-tools v4.0.1+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tidb-tools v4.0.2+incompatible h1:RcdarmYrxrDgStqJDiXTcxBI6a1MPbUz2xi27S2MPzU=
github.com/pingcap/tidb-tools v4.0.2+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tipb v0.0.0-20190428032612-535e1abaa330/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pingcap/tipb v0.0.0-20200417094153-7316d94df1ee/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pingcap/tipb v0.0.0-20200604070248-508f03b0b342/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
Expand Down
Loading