Skip to content

Commit e3250a3

Browse files
*: add batch operation for creating/dropping binding by specifying digests (#18556)
1 parent 2d19dec commit e3250a3

File tree

3 files changed

+237
-84
lines changed

3 files changed

+237
-84
lines changed

sql-plan-management.md

+13-13
Original file line numberDiff line numberDiff line change
@@ -231,25 +231,25 @@ The original SQL statement and the bound statement must have the same text after
231231

232232
#### Create a binding according to a historical execution plan
233233

234-
To make the execution plan of a SQL statement fixed to a historical execution plan, you can use `plan_digest` to bind that historical execution plan to the SQL statement, which is more convenient than binding it according to a SQL statement.
234+
To make the execution plan of a SQL statement fixed to a historical execution plan, you can use Plan Digest to bind that historical execution plan to the SQL statement, which is more convenient than binding it according to a SQL statement. In addition, you can bind the execution plan for multiple SQL statements at once. For more details and examples, see [`CREATE [GLOBAL|SESSION] BINDING`](/sql-statements/sql-statement-create-binding.md).
235235

236236
When using this feature, note the following:
237237

238238
- The feature generates hints according to historical execution plans and uses the generated hints for binding. Because historical execution plans are stored in [Statement Summary Tables](/statement-summary-tables.md), before using this feature, you need to enable the [`tidb_enable_stmt_summary`](/system-variables.md#tidb_enable_stmt_summary-new-in-v304) system variable first.
239239
- For TiFlash queries, Join queries with three or more tables, and queries that contain subqueries, the auto-generated hints are not adequate, which might result in the plan not being fully bound. In such cases, a warning will occur when creating a binding.
240-
- If a historical execution plan is for a SQL statement with hints, the hints will be added to the binding. For example, after executing `SELECT /*+ max_execution_time(1000) */ * FROM t`, the binding created with its `plan_digest` will include `max_execution_time(1000)`.
240+
- If a historical execution plan is for a SQL statement with hints, the hints will be added to the binding. For example, after executing `SELECT /*+ max_execution_time(1000) */ * FROM t`, the binding created with its Plan Digest will include `max_execution_time(1000)`.
241241

242242
The SQL statement of this binding method is as follows:
243243

244244
```sql
245-
CREATE [GLOBAL | SESSION] BINDING FROM HISTORY USING PLAN DIGEST 'plan_digest';
245+
CREATE [GLOBAL | SESSION] BINDING FROM HISTORY USING PLAN DIGEST StringLiteralOrUserVariableList;
246246
```
247247

248-
This statement binds an execution plan to a SQL statement using `plan_digest`. The default scope is SESSION. The applicable SQL statements, priorities, scopes, and effective conditions of the created bindings are the same as that of [bindings created according to SQL statements](#create-a-binding-according-to-a-sql-statement).
248+
The preceding statement binds an execution plan to a SQL statement using Plan Digest. The default scope is SESSION. The applicable SQL statements, priorities, scopes, and effective conditions of the created bindings are the same as that of [bindings created according to SQL statements](#create-a-binding-according-to-a-sql-statement).
249249

250-
To use this binding method, you need to first get the `plan_digest` corresponding to the target historical execution plan in `statements_summary`, and then create a binding using the `plan_digest`. The detailed steps are as follows:
250+
To use this binding method, you need to first get the Plan Digest corresponding to the target historical execution plan in `statements_summary`, and then create a binding using the Plan Digest. The detailed steps are as follows:
251251

252-
1. Get the `plan_digest` corresponding to the target execution plan in `statements_summary`.
252+
1. Get the Plan Digest corresponding to the target execution plan in `statements_summary`.
253253

254254
For example:
255255

@@ -274,9 +274,9 @@ To use this binding method, you need to first get the `plan_digest` correspondin
274274
BINARY_PLAN: 6QOYCuQDCg1UYWJsZVJlYWRlcl83Ev8BCgtTZWxlY3Rpb25fNhKOAQoPBSJQRnVsbFNjYW5fNSEBAAAAOA0/QSkAAQHwW4jDQDgCQAJKCwoJCgR0ZXN0EgF0Uh5rZWVwIG9yZGVyOmZhbHNlLCBzdGF0czpwc2V1ZG9qInRpa3ZfdGFzazp7dGltZTo1NjAuOMK1cywgbG9vcHM6MH1w////CQMEAXgJCBD///8BIQFzCDhVQw19BAAkBX0QUg9lcSgBfCAudC5hLCAxKWrmYQAYHOi0gc6hBB1hJAFAAVIQZGF0YTo9GgRaFAW4HDQuMDVtcywgCbYcMWKEAWNvcF8F2agge251bTogMSwgbWF4OiA1OTguNsK1cywgcHJvY19rZXlzOiAwLCBycGNfBSkAMgkMBVcQIDYwOS4pEPBDY29wcl9jYWNoZV9oaXRfcmF0aW86IDAuMDAsIGRpc3RzcWxfY29uY3VycmVuY3k6IDE1fXCwAXj///////////8BGAE=
275275
```
276276

277-
In this example, you can see that the execution plan corresponding to `plan_digest` is `4e3159169cc63c14b139a4e7d72eae1759875c9a9581f94bb2079aae961189cb`.
277+
In this example, you can see that the execution plan corresponding to Plan Digest is `4e3159169cc63c14b139a4e7d72eae1759875c9a9581f94bb2079aae961189cb`.
278278

279-
2. Use `plan_digest` to create a binding:
279+
2. Use Plan Digest to create a binding:
280280

281281
```sql
282282
CREATE BINDING FROM HISTORY USING PLAN DIGEST '4e3159169cc63c14b139a4e7d72eae1759875c9a9581f94bb2079aae961189cb';
@@ -317,7 +317,7 @@ SELECT @@LAST_PLAN_FROM_BINDING;
317317

318318
### Remove a binding
319319

320-
You can remove a binding according to a SQL statement or `sql_digest`.
320+
You can remove a binding according to a SQL statement or SQL Digest.
321321

322322
#### Remove a binding according to a SQL statement
323323

@@ -343,15 +343,15 @@ explain SELECT * FROM t1,t2 WHERE t1.id = t2.id;
343343

344344
In the example above, the dropped binding in the SESSION scope shields the corresponding binding in the GLOBAL scope. The optimizer does not add the `sm_join(t1, t2)` hint to the statement. The top node of the execution plan in the `explain` result is not fixed to MergeJoin by this hint. Instead, the top node is independently selected by the optimizer according to the cost estimation.
345345

346-
#### Remove a binding according to `sql_digest`
346+
#### Remove a binding according to SQL Digest
347347

348-
In addition to removing a binding according to a SQL statement, you can also remove a binding according to `sql_digest`.
348+
In addition to removing a binding according to a SQL statement, you can also remove a binding according to SQL Digest. For more details and examples, see [`DROP [GLOBAL|SESSION] BINDING`](/sql-statements/sql-statement-drop-binding.md).
349349

350350
```sql
351-
DROP [GLOBAL | SESSION] BINDING FOR SQL DIGEST 'sql_digest';
351+
DROP [GLOBAL | SESSION] BINDING FOR SQL DIGEST StringLiteralOrUserVariableList;
352352
```
353353

354-
This statement removes an execution plan binding corresponding to `sql_digest` at the GLOBAL or SESSION level. The default scope is SESSION. You can get the `sql_digest` by [viewing bindings](#view-bindings).
354+
This statement removes an execution plan binding corresponding to SQL Digest at the GLOBAL or SESSION level. The default scope is SESSION. You can get the SQL Digest by [viewing bindings](#view-bindings).
355355

356356
> **Note:**
357357
>

sql-statements/sql-statement-create-binding.md

+160-18
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,19 @@ The bound SQL statement is parameterized and stored in the system table. When a
1717
```ebnf+diagram
1818
CreateBindingStmt ::=
1919
'CREATE' GlobalScope 'BINDING' ( 'FOR' BindableStmt 'USING' BindableStmt
20-
| 'FROM' 'HISTORY' 'USING' 'PLAN' 'DIGEST' PlanDigest )
20+
| 'FROM' 'HISTORY' 'USING' 'PLAN' 'DIGEST' StringLiteralOrUserVariableList )
2121
2222
GlobalScope ::=
2323
( 'GLOBAL' | 'SESSION' )?
2424
2525
BindableStmt ::=
2626
( SelectStmt | UpdateStmt | InsertIntoStmt | ReplaceIntoStmt | DeleteStmt )
27+
28+
StringLiteralOrUserVariableList ::=
29+
( StringLitOrUserVariable | StringLiteralOrUserVariableList ',' StringLitOrUserVariable )
30+
31+
StringLiteralOrUserVariable ::=
32+
( stringLiteral | UserVariable )
2733
```
2834

2935
****
@@ -32,6 +38,11 @@ BindableStmt ::=
3238

3339
You can create a binding according to a SQL statement or a historical execution plan.
3440

41+
When you create a binding according to a historical execution plan, you need to specify the corresponding Plan Digest:
42+
43+
- You can use either the string literal or user variable of the string type to specify the Plan Digest.
44+
- You can specify multiple Plan Digests to create bindings for multiple statements at the same time. In this case, you can specify multiple strings, and include multiple digests in each string. Note that the strings or digests need to be separated by commas.
45+
3546
The following example shows how to create a binding according to a SQL statement.
3647

3748
{{< copyable "sql" >}}
@@ -139,34 +150,165 @@ mysql> EXPLAIN ANALYZE SELECT * FROM t1 WHERE b = 123;
139150
The following example shows how to create a binding according to a historical execution plan.
140151

141152
```sql
142-
mysql> CREATE TABLE t(id INT PRIMARY KEY , a INT, KEY(a));
143-
Query OK, 0 rows affected (0.06 sec)
153+
USE test;
154+
CREATE TABLE t1(a INT, b INT, c INT, INDEX ia(a));
155+
CREATE TABLE t2(a INT, b INT, c INT, INDEX ia(a));
156+
INSERT INTO t1 SELECT * FROM t2 WHERE a = 1;
157+
SELECT @@LAST_PLAN_FROM_BINDING;
158+
UPDATE /*+ INL_JOIN(t2) */ t1, t2 SET t1.a = 1 WHERE t1.b = t2.a;
159+
SELECT @@LAST_PLAN_FROM_BINDING;
160+
DELETE /*+ HASH_JOIN(t1) */ t1 FROM t1 JOIN t2 WHERE t1.b = t2.a;
161+
SELECT @@LAST_PLAN_FROM_BINDING;
162+
SELECT * FROM t1 WHERE t1.a IN (SELECT a FROM t2);
163+
SELECT @@LAST_PLAN_FROM_BINDING;
164+
```
165+
166+
Method 1:
167+
168+
```sql
169+
SELECT query_sample_text, stmt_type, table_names, plan_digest FROM information_schema.statements_summary_history WHERE table_names LIKE '%test.t1%' AND stmt_type != 'CreateTable';
170+
CREATE GLOBAL BINDING FROM HISTORY USING PLAN DIGEST 'e72819cf99932f63a548156dbf433adda60e10337e89dcaa8638b4caf16f64d8,c291edc36b2482738d3389d335f37efc76290be2930330fe5034c5f4c42eeb36,8dc146249484f4a6ab219bfe9effa6b7a18aeed3764d49b610da61ac347ab914,73b2dec866595688ea416675f88ccb3456eb8e7443a79cd816695b688e07ac6b';
171+
```
172+
173+
Method 2:
174+
175+
```sql
176+
SELECT @digests:=GROUP_CONCAT(plan_digest) FROM information_schema.statements_summary_history WHERE table_names LIKE '%test.t1%' AND stmt_type != 'CreateTable';
177+
CREATE GLOBAL BINDING FROM HISTORY USING PLAN DIGEST @digests;
178+
```
179+
180+
```sql
181+
SHOW GLOBAL BINDINGS;
182+
INSERT INTO t1 SELECT * FROM t2 WHERE a = 1;
183+
SELECT @@LAST_PLAN_FROM_BINDING;
184+
UPDATE t1, t2 SET t1.a = 1 WHERE t1.b = t2.a;
185+
SELECT @@LAST_PLAN_FROM_BINDING;
186+
DELETE t1 FROM t1 JOIN t2 WHERE t1.b = t2.a;
187+
SELECT @@LAST_PLAN_FROM_BINDING;
188+
SELECT * FROM t1 WHERE t1.a IN (SELECT a FROM t2);
189+
SELECT @@LAST_PLAN_FROM_BINDING;
190+
```
191+
192+
```sql
193+
> CREATE TABLE t1(a INT, b INT, c INT, INDEX ia(a));
194+
Query OK, 0 rows affected (0.048 sec)
195+
196+
> CREATE TABLE t2(a INT, b INT, c INT, INDEX ia(a));
197+
Query OK, 0 rows affected (0.035 sec)
198+
199+
> INSERT INTO t1 SELECT * FROM t2 WHERE a = 1;
200+
Query OK, 0 rows affected (0.002 sec)
201+
Records: 0 Duplicates: 0 Warnings: 0
202+
203+
> SELECT @@LAST_PLAN_FROM_BINDING;
204+
+--------------------------+
205+
| @@LAST_PLAN_FROM_BINDING |
206+
+--------------------------+
207+
| 0 |
208+
+--------------------------+
209+
1 row in set (0.001 sec)
210+
211+
> UPDATE /*+ INL_JOIN(t2) */ t1, t2 SET t1.a = 1 WHERE t1.b = t2.a;
212+
Query OK, 0 rows affected (0.005 sec)
213+
Rows matched: 0 Changed: 0 Warnings: 0
214+
215+
> SELECT @@LAST_PLAN_FROM_BINDING;
216+
+--------------------------+
217+
| @@LAST_PLAN_FROM_BINDING |
218+
+--------------------------+
219+
| 0 |
220+
+--------------------------+
221+
1 row in set (0.000 sec)
144222

145-
mysql> SELECT /*+ IGNORE_INDEX(t, a) */ * FROM t WHERE a = 1;
146-
Empty set (0.01 sec)
223+
> DELETE /*+ HASH_JOIN(t1) */ t1 FROM t1 JOIN t2 WHERE t1.b = t2.a;
224+
Query OK, 0 rows affected (0.003 sec)
147225

148-
mysql> SELECT plan_digest FROM INFORMATION_SCHEMA.STATEMENTS_SUMMARY WHERE QUERY_SAMPLE_TEXT = 'SELECT /*+ IGNORE_INDEX(t, a) */ * FROM t WHERE a = 1';
149-
+------------------------------------------------------------------+
150-
| plan_digest |
151-
+------------------------------------------------------------------+
152-
| 4e3159169cc63c14b139a4e7d72eae1759875c9a9581f94bb2079aae961189cb |
153-
+------------------------------------------------------------------+
154-
1 row in set (0.01 sec)
226+
> SELECT @@LAST_PLAN_FROM_BINDING;
227+
+--------------------------+
228+
| @@LAST_PLAN_FROM_BINDING |
229+
+--------------------------+
230+
| 0 |
231+
+--------------------------+
232+
1 row in set (0.000 sec)
155233

156-
mysql> CREATE BINDING FROM HISTORY USING PLAN DIGEST '4e3159169cc63c14b139a4e7d72eae1759875c9a9581f94bb2079aae961189cb';
157-
Query OK, 0 rows affected (0.02 sec)
234+
> SELECT * FROM t1 WHERE t1.a IN (SELECT a FROM t2);
235+
Empty set (0.002 sec)
158236

159-
mysql> SELECT * FROM t WHERE a = 1;
160-
Empty set (0.01 sec)
237+
> SELECT @@LAST_PLAN_FROM_BINDING;
238+
+--------------------------+
239+
| @@LAST_PLAN_FROM_BINDING |
240+
+--------------------------+
241+
| 0 |
242+
+--------------------------+
243+
1 row in set (0.001 sec)
244+
245+
> SELECT @digests:=GROUP_CONCAT(plan_digest) FROM information_schema.statements_summary_history WHERE table_names LIKE '%test.t1%' AND stmt_type != 'CreateTable';
246+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
247+
| @digests:=GROUP_CONCAT(plan_digest) |
248+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
249+
| 73b2dec866595688ea416675f88ccb3456eb8e7443a79cd816695b688e07ac6b,8dc146249484f4a6ab219bfe9effa6b7a18aeed3764d49b610da61ac347ab914,c291edc36b2482738d3389d335f37efc76290be2930330fe5034c5f4c42eeb36,e72819cf99932f63a548156dbf433adda60e10337e89dcaa8638b4caf16f64d8 |
250+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
251+
1 row in set (0.001 sec)
252+
253+
> CREATE GLOBAL BINDING FROM HISTORY USING PLAN DIGEST @digests;
254+
Query OK, 0 rows affected (0.060 sec)
255+
256+
> SHOW GLOBAL BINDINGS;
257+
+----------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+
258+
| Original_sql | Bind_sql | Default_db | Status | Create_time | Update_time | Charset | Collation | Source | Sql_digest | Plan_digest |
259+
+----------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+
260+
| insert into `test` . `t1` select * from `test` . `t2` where `a` = ? | INSERT INTO `test`.`t1` SELECT /*+ use_index(@`sel_1` `test`.`t2` `ia`) no_order_index(@`sel_1` `test`.`t2` `ia`)*/ * FROM `test`.`t2` WHERE `a` = 1 | test | enabled | 2024-08-11 05:27:19.669 | 2024-08-11 05:27:19.669 | utf8 | utf8_general_ci | history | bd23e6af17e7b77b25383e50e258f0dece18583d19772f08caacb2021945a300 | e72819cf99932f63a548156dbf433adda60e10337e89dcaa8638b4caf16f64d8 |
261+
| update ( `test` . `t1` ) join `test` . `t2` set `t1` . `a` = ? where `t1` . `b` = `t2` . `a` | UPDATE /*+ inl_join(`test`.`t2`) use_index(@`upd_1` `test`.`t1` ) use_index(@`upd_1` `test`.`t2` `ia`) no_order_index(@`upd_1` `test`.`t2` `ia`)*/ (`test`.`t1`) JOIN `test`.`t2` SET `t1`.`a`=1 WHERE `t1`.`b` = `t2`.`a` | test | enabled | 2024-08-11 05:27:19.667 | 2024-08-11 05:27:19.667 | utf8 | utf8_general_ci | history | 987e91af17eb40e36fecfc0634cce0b6a736de02bb009091810f932804fc02e9 | c291edc36b2482738d3389d335f37efc76290be2930330fe5034c5f4c42eeb36 |
262+
| delete `test` . `t1` from `test` . `t1` join `test` . `t2` where `t1` . `b` = `t2` . `a` | DELETE /*+ hash_join_build(`test`.`t2`) use_index(@`del_1` `test`.`t1` ) use_index(@`del_1` `test`.`t2` )*/ `test`.`t1` FROM `test`.`t1` JOIN `test`.`t2` WHERE `t1`.`b` = `t2`.`a` | test | enabled | 2024-08-11 05:27:19.664 | 2024-08-11 05:27:19.664 | utf8 | utf8_general_ci | history | 70ef3d442d95c51020a76c7c86a3ab674258606d4dd24bbd16ac6f69d87a4316 | 8dc146249484f4a6ab219bfe9effa6b7a18aeed3764d49b610da61ac347ab914 |
263+
| select * from `test` . `t1` where `t1` . `a` in ( select `a` from `test` . `t2` ) | SELECT /*+ use_index(@`sel_1` `test`.`t1` ) stream_agg(@`sel_2`) use_index(@`sel_2` `test`.`t2` `ia`) order_index(@`sel_2` `test`.`t2` `ia`) agg_to_cop(@`sel_2`)*/ * FROM `test`.`t1` WHERE `t1`.`a` IN (SELECT `a` FROM `test`.`t2`) | test | enabled | 2024-08-11 05:27:19.649 | 2024-08-11 05:27:19.649 | utf8 | utf8_general_ci | history | b58508a5e29d7889adf98cad50343d7a575fd32ad55dbdaa88e14ecde54f3d93 | 73b2dec866595688ea416675f88ccb3456eb8e7443a79cd816695b688e07ac6b |
264+
+----------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+---------+-------------------------+-------------------------+---------+-----------------+---------+------------------------------------------------------------------+------------------------------------------------------------------+
265+
4 rows in set (0.001 sec)
266+
267+
> INSERT INTO t1 SELECT * FROM t2 WHERE a = 1;
268+
Query OK, 0 rows affected (0.002 sec)
269+
Records: 0 Duplicates: 0 Warnings: 0
270+
271+
> SELECT @@LAST_PLAN_FROM_BINDING;
272+
+--------------------------+
273+
| @@LAST_PLAN_FROM_BINDING |
274+
+--------------------------+
275+
| 1 |
276+
+--------------------------+
277+
1 row in set (0.000 sec)
161278

162-
mysql> SELECT @@LAST_PLAN_FROM_BINDING;
279+
> UPDATE t1, t2 SET t1.a = 1 WHERE t1.b = t2.a;
280+
Query OK, 0 rows affected (0.002 sec)
281+
Rows matched: 0 Changed: 0 Warnings: 0
282+
283+
> SELECT @@LAST_PLAN_FROM_BINDING;
284+
+--------------------------+
285+
| @@LAST_PLAN_FROM_BINDING |
286+
+--------------------------+
287+
| 1 |
288+
+--------------------------+
289+
1 row in set (0.000 sec)
290+
291+
> DELETE t1 FROM t1 JOIN t2 WHERE t1.b = t2.a;
292+
Query OK, 0 rows affected (0.002 sec)
293+
294+
> SELECT @@LAST_PLAN_FROM_BINDING;
163295
+--------------------------+
164296
| @@LAST_PLAN_FROM_BINDING |
165297
+--------------------------+
166298
| 1 |
167299
+--------------------------+
168-
1 row in set (0.01 sec)
300+
1 row in set (0.000 sec)
301+
302+
> SELECT * FROM t1 WHERE t1.a IN (SELECT a FROM t2);
303+
Empty set (0.002 sec)
169304

305+
> SELECT @@LAST_PLAN_FROM_BINDING;
306+
+--------------------------+
307+
| @@LAST_PLAN_FROM_BINDING |
308+
+--------------------------+
309+
| 1 |
310+
+--------------------------+
311+
1 row in set (0.002 sec)
170312
```
171313

172314
## MySQL compatibility

0 commit comments

Comments
 (0)