Skip to content

Commit aeb008c

Browse files
GuptaManan100systayharshit-gangal
authored
Feature: Adding support for Vindex Hints to allow for greater control over shard routing (#15172)
Signed-off-by: Manan Gupta <manan@planetscale.com> Signed-off-by: Andres Taylor <andres@planetscale.com> Signed-off-by: Harshit Gangal <harshit@planetscale.com> Co-authored-by: Andres Taylor <andres@planetscale.com> Co-authored-by: Harshit Gangal <harshit@planetscale.com>
1 parent aaf82ee commit aeb008c

29 files changed

+4459
-3779
lines changed

changelog/20.0/20.0.0/summary.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Summary
2+
3+
### Table of Contents
4+
5+
- **[Major Changes](#major-changes)**
6+
- **[Query Serving](#query-serving)**
7+
- [Vindex Hints](#vindex-hints)
8+
- **[Minor Changes](#minor-changes)**
9+
10+
11+
## <a id="major-changes"/>Major Changes
12+
13+
14+
### <a id="query-serving"/>Query Serving
15+
16+
#### <a id="vindex-hints"/> Vindex Hints
17+
18+
Vitess now supports Vindex hints that provide a way for users to influence the shard routing of queries in Vitess by specifying, which vindexes should be considered or ignored by the query planner. This feature enhances the control over query execution, allowing for potentially more efficient data access patterns in sharded databases.
19+
20+
Example:
21+
```sql
22+
SELECT * FROM user USE VINDEX (hash_user_id, secondary_vindex) WHERE user_id = 123;
23+
SELECT * FROM order IGNORE VINDEX (range_order_id) WHERE order_date = '2021-01-01';
24+
```
25+
26+
For more information about Vindex hints and its usage, please consult the documentation.
27+
28+
## <a id="minor-changes"/>Minor Changes
29+

changelog/20.0/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## v20.0
2+
* **[20.0.0](20.0.0)**

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## Releases
2+
* [20.0](20.0)
23
* [19.0](19.0)
34
* [18.0](18.0)
45
* [17.0](17.0)

go/mysql/sqlerror/sql_error.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ var stateToMysqlCode = map[vterrors.State]mysqlCode{
217217
vterrors.WrongValueCountOnRow: {num: ERWrongValueCountOnRow, state: SSWrongValueCountOnRow},
218218
vterrors.WrongArguments: {num: ERWrongArguments, state: SSUnknownSQLState},
219219
vterrors.UnknownStmtHandler: {num: ERUnknownStmtHandler, state: SSUnknownSQLState},
220+
vterrors.KeyDoesNotExist: {num: ERKeyDoesNotExist, state: SSClientError},
220221
vterrors.UnknownTimeZone: {num: ERUnknownTimeZone, state: SSUnknownSQLState},
221222
vterrors.RegexpStringNotTerminated: {num: ERRegexpStringNotTerminated, state: SSUnknownSQLState},
222223
vterrors.RegexpBufferOverflow: {num: ERRegexpBufferOverflow, state: SSUnknownSQLState},

go/test/endtoend/vtgate/queries/misc/misc_test.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func start(t *testing.T) (utils.MySQLCompare, func()) {
3636
require.NoError(t, err)
3737

3838
deleteAll := func() {
39-
tables := []string{"t1", "uks.unsharded"}
39+
tables := []string{"t1", "tbl", "unq_idx", "nonunq_idx", "uks.unsharded"}
4040
for _, table := range tables {
4141
_, _ = mcmp.ExecAndIgnore("delete from " + table)
4242
}
@@ -137,6 +137,41 @@ func TestCast(t *testing.T) {
137137
mcmp.AssertMatches("select cast('3.2' as unsigned)", `[[UINT64(3)]]`)
138138
}
139139

140+
// TestVindexHints tests that vindex hints work as intended.
141+
func TestVindexHints(t *testing.T) {
142+
utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate")
143+
mcmp, closer := start(t)
144+
defer closer()
145+
146+
mcmp.Exec("insert into tbl(id, unq_col, nonunq_col) values (1,0,10), (2,10,10), (3,4,20), (4,30,20), (5,40,10)")
147+
mcmp.AssertMatches("select id, unq_col, nonunq_col from tbl where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", "[[INT64(2) INT64(10) INT64(10)]]")
148+
149+
// Verify that without any vindex hints, the query plan uses a hash vindex.
150+
res, err := mcmp.VtConn.ExecuteFetch("vexplain plan select id, unq_col, nonunq_col from tbl where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false)
151+
require.NoError(t, err)
152+
require.Contains(t, fmt.Sprintf("%v", res.Rows), "hash")
153+
154+
// Now we make the query explicitly use the unique lookup vindex.
155+
// We make sure the query still works.
156+
res, err = mcmp.VtConn.ExecuteFetch("select id, unq_col, nonunq_col from tbl USE VINDEX (unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false)
157+
require.NoError(t, err)
158+
require.EqualValues(t, fmt.Sprintf("%v", res.Rows), "[[INT64(2) INT64(10) INT64(10)]]")
159+
// Verify that we are using the unq_vdx, that we requested explicitly.
160+
res, err = mcmp.VtConn.ExecuteFetch("vexplain plan select id, unq_col, nonunq_col from tbl USE VINDEX (unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false)
161+
require.NoError(t, err)
162+
require.Contains(t, fmt.Sprintf("%v", res.Rows), "unq_vdx")
163+
164+
// Now we make the query explicitly refuse two of the three vindexes.
165+
// We make sure the query still works.
166+
res, err = mcmp.VtConn.ExecuteFetch("select id, unq_col, nonunq_col from tbl IGNORE VINDEX (hash, unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false)
167+
require.NoError(t, err)
168+
require.EqualValues(t, fmt.Sprintf("%v", res.Rows), "[[INT64(2) INT64(10) INT64(10)]]")
169+
// Verify that we are using the nonunq_vdx, which is the only one left to be used.
170+
res, err = mcmp.VtConn.ExecuteFetch("vexplain plan select id, unq_col, nonunq_col from tbl IGNORE VINDEX (hash, unq_vdx) where unq_col = 10 and id = 2 and nonunq_col in (10, 20)", 100, false)
171+
require.NoError(t, err)
172+
require.Contains(t, fmt.Sprintf("%v", res.Rows), "nonunq_vdx")
173+
}
174+
140175
func TestOuterJoinWithPredicate(t *testing.T) {
141176
mcmp, closer := start(t)
142177
defer closer()

go/test/endtoend/vtgate/queries/misc/schema.sql

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,28 @@ create table if not exists t1(
22
id1 bigint,
33
id2 bigint,
44
primary key(id1)
5-
) Engine=InnoDB;
5+
) Engine=InnoDB;
6+
7+
create table unq_idx
8+
(
9+
unq_col bigint,
10+
keyspace_id varbinary(20),
11+
primary key (unq_col)
12+
) Engine = InnoDB;
13+
14+
create table nonunq_idx
15+
(
16+
nonunq_col bigint,
17+
id bigint,
18+
keyspace_id varbinary(20),
19+
primary key (nonunq_col, id)
20+
) Engine = InnoDB;
21+
22+
create table tbl
23+
(
24+
id bigint,
25+
unq_col bigint,
26+
nonunq_col bigint,
27+
primary key (id),
28+
unique (unq_col)
29+
) Engine = InnoDB;

go/test/endtoend/vtgate/queries/misc/vschema.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@
33
"vindexes": {
44
"hash": {
55
"type": "hash"
6+
},
7+
"unq_vdx": {
8+
"type": "consistent_lookup_unique",
9+
"params": {
10+
"table": "unq_idx",
11+
"from": "unq_col",
12+
"to": "keyspace_id",
13+
"ignore_nulls": "true"
14+
},
15+
"owner": "tbl"
16+
},
17+
"nonunq_vdx": {
18+
"type": "consistent_lookup",
19+
"params": {
20+
"table": "nonunq_idx",
21+
"from": "nonunq_col,id",
22+
"to": "keyspace_id",
23+
"ignore_nulls": "true"
24+
},
25+
"owner": "tbl"
626
}
727
},
828
"tables": {
@@ -13,6 +33,41 @@
1333
"name": "hash"
1434
}
1535
]
36+
},
37+
"tbl": {
38+
"column_vindexes": [
39+
{
40+
"column": "id",
41+
"name": "hash"
42+
},
43+
{
44+
"column": "unq_col",
45+
"name": "unq_vdx"
46+
},
47+
{
48+
"columns": [
49+
"nonunq_col",
50+
"id"
51+
],
52+
"name": "nonunq_vdx"
53+
}
54+
]
55+
},
56+
"unq_idx": {
57+
"column_vindexes": [
58+
{
59+
"column": "unq_col",
60+
"name": "hash"
61+
}
62+
]
63+
},
64+
"nonunq_idx": {
65+
"column_vindexes": [
66+
{
67+
"column": "nonunq_col",
68+
"name": "hash"
69+
}
70+
]
1671
}
1772
}
1873
}

go/vt/sqlparser/ast_format.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ func (node IndexHints) Format(buf *TrackedBuffer) {
12411241

12421242
// Format formats the node.
12431243
func (node *IndexHint) Format(buf *TrackedBuffer) {
1244-
buf.astPrintf(node, " %sindex ", node.Type.ToString())
1244+
buf.astPrintf(node, " %s ", node.Type.ToString())
12451245
if node.ForType != NoForType {
12461246
buf.astPrintf(node, "for %s ", node.ForType.ToString())
12471247
}

go/vt/sqlparser/ast_format_fast.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/vt/sqlparser/ast_funcs.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1559,14 +1559,28 @@ func (ty IndexHintType) ToString() string {
15591559
case UseOp:
15601560
return UseStr
15611561
case IgnoreOp:
1562-
return IgnoreStr
1562+
return IgnoreIndexStr
15631563
case ForceOp:
15641564
return ForceStr
1565+
case UseVindexOp:
1566+
return UseVindexStr
1567+
case IgnoreVindexOp:
1568+
return IgnoreVindexStr
15651569
default:
15661570
return "Unknown IndexHintType"
15671571
}
15681572
}
15691573

1574+
// IsVindexHint returns if the given hint is a Vindex hint or not.
1575+
func (ty IndexHintType) IsVindexHint() bool {
1576+
switch ty {
1577+
case UseVindexOp, IgnoreVindexOp:
1578+
return true
1579+
default:
1580+
return false
1581+
}
1582+
}
1583+
15701584
// ToString returns the type as a string
15711585
func (ty IndexHintForType) ToString() string {
15721586
switch ty {

go/vt/sqlparser/constants.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,15 @@ const (
122122
NaturalLeftJoinStr = "natural left join"
123123
NaturalRightJoinStr = "natural right join"
124124

125-
// Index hints.
126-
UseStr = "use "
125+
// IgnoreStr string.
127126
IgnoreStr = "ignore "
128-
ForceStr = "force "
127+
128+
// Index hints.
129+
UseStr = "use index"
130+
IgnoreIndexStr = "ignore index"
131+
ForceStr = "force index"
132+
UseVindexStr = "use vindex"
133+
IgnoreVindexStr = "ignore vindex"
129134

130135
// Index hints For types.
131136
JoinForStr = "join"
@@ -760,6 +765,8 @@ const (
760765
UseOp IndexHintType = iota
761766
IgnoreOp
762767
ForceOp
768+
UseVindexOp
769+
IgnoreVindexOp
763770
)
764771

765772
// Constant for Enum Type - IndexHintForType

go/vt/sqlparser/parse_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,20 @@ var (
26592659
}, {
26602660
input: "SELECT id FROM blog_posts USE INDEX (PRIMARY) WHERE id = 10",
26612661
output: "select id from blog_posts use index (`PRIMARY`) where id = 10",
2662+
}, {
2663+
input: "select * from payment_pulls ignore vindex (lookup_vindex_name) where customer_id in (1, 10) and payment_id = 5",
2664+
}, {
2665+
input: "select * from payment_pulls ignore vindex (lookup_vindex_name, x, t) order by id",
2666+
output: "select * from payment_pulls ignore vindex (lookup_vindex_name, x, t) order by id asc",
2667+
}, {
2668+
input: "select * from payment_pulls use vindex (lookup_vindex_name) where customer_id in (1, 10) and payment_id = 5",
2669+
}, {
2670+
input: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) order by id",
2671+
output: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) order by id asc",
2672+
}, {
2673+
input: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) ignore vindex (x, t)",
2674+
}, {
2675+
input: "select * from payment_pulls use vindex (lookup_vindex_name, x, t) ignore vindex (x, t) join tab ignore vindex (y)",
26622676
}, {
26632677
input: "select name, group_concat(score) from t group by name",
26642678
output: "select `name`, group_concat(score) from t group by `name`",

0 commit comments

Comments
 (0)