From 81a9f9636a8f25a557e216d10510aba3fb47894d Mon Sep 17 00:00:00 2001 From: TonsnakeLin Date: Mon, 27 Dec 2021 20:44:07 +0800 Subject: [PATCH] add a RFC doc for integer shard index Signed-off-by: TonsnakeLin --- media/hot-index-scalability.png | Bin 0 -> 38908 bytes media/shard-index-scalability.png | Bin 0 -> 36533 bytes text/0084-integer-shard-index.md | 581 ++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 media/hot-index-scalability.png create mode 100644 media/shard-index-scalability.png create mode 100644 text/0084-integer-shard-index.md diff --git a/media/hot-index-scalability.png b/media/hot-index-scalability.png new file mode 100644 index 0000000000000000000000000000000000000000..84e21a33d583c67fe0e7f62f496c400ffd4028ec GIT binary patch literal 38908 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 ZCrqA-Z>RiK!fvAdc$uuc%3p`JzXQ@tM`-{6 literal 0 HcmV?d00001 diff --git a/media/shard-index-scalability.png b/media/shard-index-scalability.png new file mode 100644 index 0000000000000000000000000000000000000000..3eb2a94e536a1819238da5d268da0bc4140e8b1f GIT binary patch literal 36533 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 N{H?S|(HmXg{|mAEa$o=e literal 0 HcmV?d00001 diff --git a/text/0084-integer-shard-index.md b/text/0084-integer-shard-index.md new file mode 100644 index 00000000..847c4179 --- /dev/null +++ b/text/0084-integer-shard-index.md @@ -0,0 +1,581 @@ +# Integer shard index + +- RFC PR: https://github.com/tikv/rfcs/pull/0084 +- Tracking Issue: https://github.com/pingcap/tidb/issues/31040 + +## Summary + +Hot index reduces the write scalability of the TiDB cluster when the written data is monotonically increasing. We find out a proposal to solve the problem that is using an expression index to scatter the hot index, the new expression index is called a shard index. + +The followers is the scalability of hot index and shard index. + +image-20211227152156961 + +​ hot index scalability + + + +image-20211227152156961 + +​ shard index scalability + +## Motivation + +We made a new built-in function "tidb_shard()" as the expression prefix of shard index. There is only one integer parameter for tidb_shard function. What index is a shard index? It must meet the follower rules. + +- It is an unqie index +- The first field of index is tidb_shard(`a`) , the column `a` is integer type and is the second field of the index. + +When querying based on the original index field, the TiDB optimizer should automatically recognize the shard index and add the expression field `tidb_shard(a) = val` to the query condition. + +How to solve the hot index problem by shard index? For example, the definition of a table is `test(id int primary key clustered, a int, b int, unique key idx_a(a))` . The index `idx_a` is a hot index because of monotonically increasing data. We modify the definition of the index `idx_a` as follows `unique key idx_a((tidb_shard(a)),a)`. The written data of index `idx_a` is not monotonically increasing now. But here is a question, how the optimizer of TiDB chooses the index for original queries that are designed for `idx_a(a)`? e.g. `SELECT * FROM test WHERE a = 100`, it can't use the unique index 'idx_a((tidb_shard(a)),a' any more. We add a new rule for TiDB optimizer, if the access condition of a query contains all the fields of shard index except the first `tidb_shard(a)` expression, The optimizer adds the expression`tidb_shard(a) = xxx` to the access condition and the new query statement is like `SELECT * FROM test WHERE tidb_shard(a) = 8 AND a = 100`, so that the query can use the shard index. + +## Detailed design + +### tidb_shard function + +#### dsciription + +Only support an integer parameter. It transforms an integer number to a value in the range [0, 255]. e.g. `tidb_shard(100)` is the value `8` . + +#### principle + +It calculate the hash value of the input parameter, and get the mod value by 256. + +```go +func (b *builtinTidbShardSig) evalInt(row chunk.Row) (int64, bool, error) { + shardKeyInt, isNull, err := b.args[0].EvalInt(b.ctx, row) + if isNull || err != nil { + return 0, true, err + } + var hashed uint64 + if hashed, err = vitess.HashUint64(uint64(shardKeyInt)); err != nil { + return 0, true, err + } + hashed = hashed % tidbShardBucketCount + return int64(hashed), false, nil +} +``` + + + +### Add expression to access condition by optimizer + +The optimizer should add the expresion `tidb_shard(xxx) = yyy` to the access condition, so that a the query can use shard index. + +The follower are the functions used to add the expresion `tidb_shard(xxx) = yyy` to the access condition. They are Introduced in order of function call. + + + +#### (ds *DataSource) PredicatePushDown + +The entry point to add the `tidb_shard` expression is the function as bellow. We must add it before access condition was pushed down. Maybe this work should be down after sql parse, we do it here just for simplify the work. + +```go + +func (ds *DataSource) PredicatePushDown(predicates []expression.Expression, opt *logicalOptimizeOp) ([]expression.Expression, LogicalPlan) { + predicates = expression.PropagateConstant(ds.ctx, predicates) + predicates = DeleteTrueExprs(ds, predicates) + // Add tidb_shard() prefix to the condtion for shard index in some scenarios + // TODO: remove it to the place building logical plan + predicates = ds.AddPrefix4ShardIndexes(ds.ctx, predicates) + ds.allConds = predicates + ds.pushedDownConds, predicates = expression.PushDownExprs(ds.ctx.GetSessionVars().StmtCtx, predicates, ds.ctx.GetClient(), kv.UnSpecified) + appendDataSourcePredicatePushDownTraceStep(ds, opt) + return predicates, ds +} +``` + +#### (ds *DataSource) AddPrefix4ShardIndexes + +`AddPrefix4ShardIndexes` is the interface to add expression prefix for shard index. Pay attention, just for shard index! + +```go +// AddPrefix4ShardIndexes Add expression prefix for shard index. e.g. an index is test.uk(tidb_shard(a), a). +// It transforms the sql "SELECT * FROM test WHERE a = 10" to +// "SELECT * FROM test WHERE tidb_shard(a) = val AND a = 10", val is the value of tidb_shard(10). +// It also transforms the sql "SELECT * FROM test WHERE a IN (10, 20, 30)" to +// "SELECT * FROM test WHERE tidb_shard(a) = val1 AND a = 10 OR tidb_shard(a) = val2 AND a = 20" +// @param[in] conds the original condtion of this datasource +// @retval - the new condition after adding expression prefix +func (ds *DataSource) AddPrefix4ShardIndexes(sc sessionctx.Context, conds []expression.Expression) []expression.Expression { + if !ds.containExprPrefixUk { + return conds + } + + var err error + newConds := make([]expression.Expression, 0, len(conds)) + newConds = append(newConds, conds...) + + for _, path := range ds.possibleAccessPaths { + if path.IsTablePath() || !path.IsUkShardIndex() { + continue + } + newConds, err = ds.addExprPrefixCond(sc, path, newConds) + if err != nil { + logutil.BgLogger().Error("Add tidb_shard expression failed", zap.Error(err)) + return conds + } + } + + return newConds +} +``` + +#### (ds *DataSource) addExprPrefixCond + +`addExprPrefixCond` is the wrapper for `addExprPrefix4ShardIndex` + +```go +func (ds *DataSource) addExprPrefixCond(sc sessionctx.Context, path *util.AccessPath, + conds []expression.Expression) ([]expression.Expression, error) { + IdxCols, IdxColLens := + expression.IndexInfo2PrefixCols(ds.Columns, ds.schema.Columns, path.Index) + if len(IdxCols) == 0 { + return conds, nil + } + + adder := &exprPrefixAdder{ + sctx: sc, + OrigConds: conds, + cols: IdxCols, + lengths: IdxColLens, + } + + return adder.addExprPrefix4ShardIndex() +} +``` + +#### (adder *exprPrefixAdder) addExprPrefix4ShardIndex + +`addExprPrefix4ShardIndex` is the wrapper for `addExprPrefix4DNFCond` and `addExprPrefix4CNFCond`. `addExprPrefix4DNFCond` is used to process `OR` expression, `addExprPrefix4CNFCond` is used to process `AND` expression. + +```Go +// if original condition is a LogicOr expression, such as `WHERE a = 1 OR a = 10`, +// call the function AddExprPrefix4DNFCond to add prefix expression tidb_shard(a) = xxx for shard index. +// Otherwise, if the condition is `WHERE a = 1`, `WHERE a = 1 AND b = 10`, `WHERE a IN (1, 2, 3)`......, +// call the function AddExprPrefix4CNFCond to add prefix expression for shard index. +func (adder *exprPrefixAdder) addExprPrefix4ShardIndex() ([]expression.Expression, error) { + if len(adder.OrigConds) == 1 { + if sf, ok := adder.OrigConds[0].(*expression.ScalarFunction); ok && sf.FuncName.L == ast.LogicOr { + return adder.addExprPrefix4DNFCond(sf) + } + } + return adder.addExprPrefix4CNFCond(adder.OrigConds) +} +``` + + + +#### (adder *exprPrefixAdder) addExprPrefix4DNFCond + +add the prefix expression for DNF condition + +```go +// add the prefix expression for DNF condition, e.g. `WHERE a = 1 OR a = 10`, ...... +// The condition returned is `WHERE (tidb_shard(a) = 214 AND a = 1) OR (tidb_shard(a) = 142 AND a = 10)` +// @param[in] condition the original condtion of the datasoure. e.g. `WHERE a = 1 OR a = 10`. +// condtion is `a = 1 OR a = 10` +// @return - the new condition after adding expression prefix. It's still a LogicOr expression. +func (adder *exprPrefixAdder) addExprPrefix4DNFCond(condition *expression.ScalarFunction) ([]expression.Expression, error) { + + var err error + dnfItems := expression.FlattenDNFConditions(condition) + newAccessItems := make([]expression.Expression, 0, len(dnfItems)) + + for _, item := range dnfItems { + if sf, ok := item.(*expression.ScalarFunction); ok { + var accesses []expression.Expression + if sf.FuncName.L == ast.LogicAnd { + cnfItems := expression.FlattenCNFConditions(sf) + accesses, err = adder.addExprPrefix4CNFCond(cnfItems) + if err != nil { + return []expression.Expression{condition}, err + } + newAccessItems = append(newAccessItems, expression.ComposeCNFCondition(adder.sctx, accesses...)) + } else if sf.FuncName.L == ast.EQ || sf.FuncName.L == ast.In { + // only add prefix expression for EQ or IN function + accesses, err = adder.addExprPrefix4CNFCond([]expression.Expression{sf}) + if err != nil { + return []expression.Expression{condition}, err + } + newAccessItems = append(newAccessItems, expression.ComposeCNFCondition(adder.sctx, accesses...)) + } else { + newAccessItems = append(newAccessItems, item) + } + } else { + newAccessItems = append(newAccessItems, item) + } + } + + return []expression.Expression{expression.ComposeDNFCondition(adder.sctx, newAccessItems...)}, nil +} +``` + + + +#### (adder *exprPrefixAdder) addExprPrefix4CNFCond + +Call the function `ranger.AddExpr4EqAndInCondition` to process `AND` expression. + +```go +// add the prefix expression for CNF condition, e.g. `WHERE a = 1`, `WHERE a = 1 AND b = 10`, ...... +// @param[in] conds the original condtion of the datasoure. e.g. `WHERE t1.a = 1 AND t1.b = 10 AND t2.a = 20`. +// if current datasource is `t1`, conds is {t1.a = 1, t1.b = 10}. if current datasource is +// `t2`, conds is {t2.a = 20} +// @return - the new condition after adding expression prefix +func (adder *exprPrefixAdder) addExprPrefix4CNFCond(conds []expression.Expression) ([]expression.Expression, error) { + + newCondtionds, err := ranger.AddExpr4EqAndInCondition(adder.sctx, + conds, adder.cols) + + return newCondtionds, err +} +``` + +##### AddExpr4EqAndInCondition + +The core function that adds `tidb_shard(x) = xxx` to the accessCond. + +```go +// AddExpr4EqAndInCondition add the `tidb_shard(x) = xxx` prefix +// Add tidb_shard() for EQ and IN function. e.g. input condition is `WHERE a = 1`, +// output condition is `WHERE tidb_shard(a) = 214 AND a = 1`. e.g. input condition +// is `WHERE a IN (1, ,2 ,3)`, output condition is `WHERE (tidb_shard(a) = 214 AND a = 1) +// OR (tidb_shard(a) = 143 AND a = 2) OR (tidb_shard(a) = 156 AND a = 3)` +// @param[in] conditions the original condition to be processed +// @param[in] cols the columns of shard index, such as [tidb_shard(a), a, ...] +// @param[in] lengths the length for every column of shard index +// @retval - the new condition after adding tidb_shard() prefix +func AddExpr4EqAndInCondition(sctx sessionctx.Context, conditions []expression.Expression, + cols []*expression.Column) ([]expression.Expression, error) { + + accesses := make([]expression.Expression, len(cols)) + columnValues := make([]*valueInfo, len(cols)) + offsets := make([]int, len(conditions)) + addGcCond := true + + // the array accesses stores conditions of every column in the index in the definition order + // e.g. the original condition is `WHERE b = 100 AND a = 200 AND c = 300`, the definition of + // index is (tidb_shard(a), a, b), then accesses is "[a = 200, b = 100]" + for i, cond := range conditions { + offset := getPotentialEqOrInColOffset(sctx, cond, cols) + offsets[i] = offset + if offset == -1 { + continue + } + if accesses[offset] == nil { + accesses[offset] = cond + continue + } + // if the same field appear twice or more, don't add tidb_shard() + // e.g. `WHERE a > 100 and a < 200` + addGcCond = false + } + + for i, cond := range accesses { + if cond == nil { + continue + } + if !allEqOrIn(cond) { + addGcCond = false + break + } + columnValues[i] = extractValueInfo(cond) + } + + if !addGcCond || !NeedAddGcColumn4ShardIndex(cols, accesses, columnValues) { + return conditions, nil + } + + // remove the accesses from newConditions + newConditions := make([]expression.Expression, 0, len(conditions)) + newConditions = append(newConditions, conditions...) + newConditions = removeAccessConditions(newConditions, accesses) + + // add Gc condtion for accesses and return new condition to newAccesses + newAccesses, err := AddGcColumnCond(sctx, cols, accesses, columnValues) + if err != nil { + return conditions, err + } + + // merge newAccesses and original condition execept accesses + newConditions = append(newConditions, newAccesses...) + + return newConditions, nil +} +``` + + + +##### NeedAddGcColumn4ShardIndex + +Check whether to add `tidb_shard(x) = xxx` to the access condition. + +```go +// NeedAddGcColumn4ShardIndex check whether to add `tidb_shard(x) = xxx` +// @param[in] cols the columns of shard index, such as [tidb_shard(a), a, ...] +// @param[in] accessCond the condtions relative to the index and arranged by the index column order. +// e.g. the index is uk(tidb_shard(a), a, b) and the where clause is +// `WHERE b = 1 AND a = 2 AND c = 3`, the param accessCond is {a = 2, b = 1} that is +// only relative to uk's columns. +// @param[in] columnValues the values of index columns in param accessCond. if accessCond is {a = 2, b = 1}, +// columnValues is {2, 1}. if accessCond the "IN" function like `a IN (1, 2)`, columnValues +// is empty. +// @retval - return true if it needs to addr tidb_shard() prefix, ohterwise return false +func NeedAddGcColumn4ShardIndex( + cols []*expression.Column, + accessCond []expression.Expression, + columnValues []*valueInfo) bool { + + // the columns of shard index shoude be more than 2, like (tidb_shard(a),a,...) + // check cols and columnValues in the sub call function + if len(accessCond) < 2 { + return false + } + + if !IsValidShardIndex(cols) { + return false + } + + // accessCond[0] shoudle be nil, because it has no access condition for + // the prefix tidb_shard() of the shard index + if cond := accessCond[1]; cond != nil { + if f, ok := cond.(*expression.ScalarFunction); ok { + switch f.FuncName.L { + case ast.EQ: + return NeedAddColumn4EqCond(cols, accessCond, columnValues) + case ast.In: + return NeedAddColumn4InCond(cols, accessCond, f) + } + } + } + + return false +} +``` + + + +##### AddGcColumnCond + +`AddGcColumnCond` is the wrapper function for `AddGcColumn4EqCond` and `AddGcColumn4InCond`. + +```go +// AddGcColumnCond add the `tidb_shard(x) = xxx` to the condition +// @param[in] cols the columns of shard index, such as [tidb_shard(a), a, ...] +// @param[in] accessCond the condtions relative to the index and arranged by the index column order. +// e.g. the index is uk(tidb_shard(a), a, b) and the where clause is +// `WHERE b = 1 AND a = 2 AND c = 3`, the param accessCond is {a = 2, b = 1} that is +// only relative to uk's columns. +// @param[in] columnValues the values of index columns in param accessCond. if accessCond is {a = 2, b = 1}, +// columnValues is {2, 1}. if accessCond the "IN" function like `a IN (1, 2)`, columnValues +// is empty. +// @retval - []expression.Expression the new condtions after adding `tidb_shard() = xxx` prefix +// error if error gernerated, return error +func AddGcColumnCond(sctx sessionctx.Context, + cols []*expression.Column, + accessesCond []expression.Expression, + columnValues []*valueInfo) ([]expression.Expression, error) { + + if cond := accessesCond[1]; cond != nil { + if f, ok := cond.(*expression.ScalarFunction); ok { + switch f.FuncName.L { + case ast.EQ: + return AddGcColumn4EqCond(sctx, cols, accessesCond, columnValues) + case ast.In: + return AddGcColumn4InCond(sctx, cols, accessesCond) + } + } + } + + return accessesCond, nil +} +``` + + + +##### AddGcColumn4EqCond + +Add the `tidb_shard(x) = xxx` prefix for equal access condition. + +```go +// AddGcColumn4EqCond add the `tidb_shard(x) = xxx` prefix for equal condition +// For param explanation, please refer to the function `AddGcColumnCond`. +// @retval - []expression.Expression the new condtions after adding `tidb_shard() = xxx` prefix +// []*valueInfo the values of every columns in the returned new condtions +// error if error gernerated, return error +func AddGcColumn4EqCond(sctx sessionctx.Context, + cols []*expression.Column, + accessesCond []expression.Expression, + columnValues []*valueInfo) ([]expression.Expression, error) { + + expr := cols[0].VirtualExpr.Clone() + record := make([]types.Datum, len(columnValues)-1) + + for i := 1; i < len(columnValues); i++ { + cv := columnValues[i] + if cv == nil { + break + } + record[i-1] = *cv.value + } + + mutRow := chunk.MutRowFromDatums(record) + evaluated, err := expr.Eval(mutRow.ToRow()) + if err != nil { + return accessesCond, err + } + vi := &valueInfo{false, &evaluated} + con := &expression.Constant{Value: evaluated, RetType: cols[0].RetType} + // make a tidb_shard() function, e.g. `tidb_shard(a) = 8` + cond, err := expression.NewFunction(sctx, ast.EQ, cols[0].RetType, cols[0], con) + if err != nil { + return accessesCond, err + } + + accessesCond[0] = cond + columnValues[0] = vi + return accessesCond, nil +} +``` + + + +##### AddGcColumn4InCond + + Add the `tidb_shard(x) = xxx` for `IN` condition. + +```go +// AddGcColumn4InCond add the `tidb_shard(x) = xxx` for `IN` condition +// For param explanation, please refer to the function `AddGcColumnCond`. +// @retval - []expression.Expression the new condtions after adding `tidb_shard() = xxx` prefix +// error if error gernerated, return error +func AddGcColumn4InCond(sctx sessionctx.Context, + cols []*expression.Column, + accessesCond []expression.Expression) ([]expression.Expression, error) { + + var errRes error + var newAccessCond []expression.Expression + record := make([]types.Datum, 1) + + expr := cols[0].VirtualExpr.Clone() + andType := types.NewFieldType(mysql.TypeTiny) + + sf := accessesCond[1].(*expression.ScalarFunction) + c, _ := sf.GetArgs()[0].(*expression.Column) + var AndOrExpr expression.Expression + for i, arg := range sf.GetArgs()[1:] { + // get every const value and calculate tidb_shar(val) + con, _ := arg.(*expression.Constant) + conVal, err := con.Eval(chunk.Row{}) + if err != nil { + return accessesCond, err + } + + record[0] = conVal + mutRow := chunk.MutRowFromDatums(record) + exprVal, err := expr.Eval(mutRow.ToRow()) + if err != nil { + return accessesCond, err + } + + // tmpArg1 is like `tidb_shard(a) = 8`, tmpArg1 is like `a = 100` + exprCon := &expression.Constant{Value: exprVal, RetType: cols[0].RetType} + tmpArg1, err1 := expression.NewFunction(sctx, ast.EQ, cols[0].RetType, cols[0], exprCon) + if err1 != nil { + return accessesCond, err1 + } + tmpArg2, err2 := expression.NewFunction(sctx, ast.EQ, c.RetType, c.Clone(), arg) + if err2 != nil { + return accessesCond, err2 + } + + // make a LogicAnd, e.g. `tidb_shard(a) = 8 AND a = 100` + andExpr, err3 := expression.NewFunction(sctx, ast.LogicAnd, andType, tmpArg1, tmpArg2) + if err3 != nil { + return accessesCond, err3 + } + + if i == 0 { + AndOrExpr = andExpr + } else { + // if the LogicAnd more than one, make a LogicOr, + // e.g. `(tidb_shard(a) = 8 AND a = 100) OR (tidb_shard(a) = 161 AND a = 200)` + AndOrExpr, errRes = expression.NewFunction(sctx, ast.LogicOr, andType, AndOrExpr, andExpr) + if errRes != nil { + return accessesCond, errRes + } + } + } + + newAccessCond = append(newAccessCond, AndOrExpr) + + return newAccessCond, nil +} +``` + + + +## Drawbacks + +Using the shard index to replace the hot index my decrease a little of the performance when the TiDB cluster is small and the pressure of workload is not high, But it can increase the scalability obviously. + + + +## Alternatives + +We can use expression index directly other than as the prefix of a index. e.g. replace `index idx(a)` to `indx idx(expr(a))` , it seems more simpler. But `expr(a)` and `(a)` don't correspond one to one, the PointGet plan becomes IndexRangeScan and the selection operation `WEHRE a = xxx` must be executed in the tidb-server or tikv. So the performance may be dropped a lot. + + + +## Questions + +As I know @tiancaiamao is intended to do the shard index more comprehensively. + + + +## Unresolved questions + +### Shard Index Only Support Unique Secondary Index + +The shard index is only valid for unique secondary index, not for primary key or non-unique secondary index, and it must meet the follower rules. + +- It is an unqie index +- The first field of index is tidb_shard(`a`) , the column `a` is integer type and is the second field of the index. + +### Non-equivalent Condition Can't Use Index + +The proposal only adds expression `tidb_shard(?) = ?` for equivalent condition in `WHERE` clause. e.g. If there is a unique index `uk(a)` on table `A` , optimizer chooses index scan by `uk(a)` for query ` SELECT * FROM A WHERE a > 100 `.The index definition is changed to `uk(tidb_shard(a), a)` by this proposal, optimizer doesn't choose index for the query above any more. + +### `AND` Expression Mixing With `OR` Expression Can't Use Index + +The proposal only adds expression `tidb_shard(?) = ?` for all "AND" expression or all "OR" expression or none of them in `WHERE` clause. If "and" expression mixing with "or" expression in `WHERE` clause, it can't use index. e.g. If there is a unique index `uk(a)` on table `A` , optimizer chooses index scan by `uk(a)` for query ` SELECT * FROM A WHERE ((a=100 and b = 100) or a = 200) and b = 300 `. The index definition is changed to `uk(tidb_shard(a), a)` by this proposal, optimizer doesn't choose index for the query above any more. + +### Group By Can't Use Index + +The proposal only adds expression `tidb_shard(?) = ?` for equivalent condition in `WHERE` clause. It does nothings for `GROUP BY ` clause. e.g. If there is a unique index `uk(a)` on table `A` , optimizer chooses index scan by `uk(a)` for query ` SELECT SUM(a) FROM A GROUP BY a `. The index definition is changed to `uk(tidb_shard(a), a)` by this proposal, optimizer doesn't choose index for the query above any more and it needs to be grouped in tidb-server. + +### Order By Can't Use Index + +The proposal only adds expression `tidb_shard(?) = ?` for equivalent condition in `WHERE` clause. It does nothing for `ORDER BY ` clause. e.g. If there is a unique index `uk(a)` on table `A` , optimizer chooses index scan by `uk(a)` for query ` SELECT a FROM A ORDER BY a `. The index definition is changed to `uk(tidb_shard(a), a)` by this proposal, optimizer doesn't choose index for the query above any more and it needs to be orderedin tidb-server. + +### Join Condition Can't Use Index + +The proposal only adds expression `tidb_shard(?) = ?` for equivalent condition in `WHERE` clause. It does nothing for the `ON` clause. e.g. If there is a unique index `uk(a)` on table `A` , optimizer chooses index scan by `uk(a)` for query ` SELECT * FROM B JOIN A ON B.a = A.a`. The index definition is changed to `uk(tidb_shard(a), a)` by this proposal, optimizer doesn't choose index for the query above any more. + +### Where Subquery Can't Use Index + +The proposal only adds expression `tidb_shard(?) = ?` for equivalent condition in `WHERE` clause. It does nothing for the sub-query clause. e.g. If there is a unique index `uk(a)` on table `A` , optimizer chooses index scan by `uk(a)` for query `SELECT * FROM A WHERE A.a IN (SELECT B.a FROM B WHERE B.a > 100) `. The index definition is changed to `uk(tidb_shard(a), a)` by this proposal, optimizer doesn't choose index for the query above any more. + +### Joint Index May Be Invalid + +The proposal only adds expression `tidb_shard(?) = ?` for equivalent condition in `WHERE` clause. If the access condition in `WHERE` clause only use part of a joint index, this proposal may leads to index useless. e.g. If there is a unique index `uk(a)` on table `A` , optimizer chooses index scan by `uk(a, b)` for query ` SELECT * FROM A WHERE A.a = 10`. The index definition is changed to `uk(tidb_shard(a), a, b)` by this proposal, optimizer doesn't choose index for the query above any more. + +### FastPlan May Be Invalid + +It can't adds expression `tidb_shard(?) = ?` to access condition in the FastPlan process. So, the FastPlan process is invalid for shard index. e.g. If there is a unique index `uk(a)` on table `A` , optimizer makes a PointGet plan by FastPlan process for query ` SELECT * FROM A WHERE A.a = 100`. The index definition is changed to `uk(tidb_shard(a), a)` by this proposal, FastPlan can't recognize it is a shard index field in `WHERE` clause. + +### Shard Index Can't Use Prepare Plan Cache + +If there is a generated column on a table, the queries about this table can't use prepare plan cache. Shard index uses the expression `tidb_shard(a)` as the first field, and it makes a generated column. So the table with shard index can't use prepare plan cache.