From fd4c157af7e8495014c0e37c40b2ebf323dcb0f3 Mon Sep 17 00:00:00 2001 From: Roger Song Date: Tue, 7 Jan 2025 11:36:34 +0800 Subject: [PATCH] Spm fix hint issue (#18534) --- sql-plan-management.md | 51 +++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/sql-plan-management.md b/sql-plan-management.md index 4bb4fbcb8c89..346f6f8a8349 100644 --- a/sql-plan-management.md +++ b/sql-plan-management.md @@ -30,50 +30,45 @@ CREATE [GLOBAL | SESSION] BINDING [FOR BindableStmt] USING BindableStmt; 该语句可以在 GLOBAL 或者 SESSION 作用域内为 SQL 绑定执行计划。目前,可创建执行计划绑定的 SQL 语句类型 (BindableStmt) 包括:`SELECT`、`DELETE`、`UPDATE` 和带有 `SELECT` 子查询的 `INSERT`/`REPLACE`。使用示例如下: ```sql -CREATE GLOBAL BINDING USING SELECT /*+ use_index(t1, idx_a) */ * FROM t1; -CREATE GLOBAL BINDING FOR SELECT * FROM t1 USING SELECT /*+ use_index(t1, idx_a) */ * FROM t1; +CREATE GLOBAL BINDING USING SELECT /*+ use_index(orders, orders_book_id_idx) */ * FROM orders; +CREATE GLOBAL BINDING FOR SELECT * FROM orders USING SELECT /*+ use_index(orders, orders_book_id_idx) */ * FROM orders; ``` > **注意:** > > 绑定的优先级高于手工添加的 Hint,即在有绑定的时候执行带有 Hint 的语句时,该语句中控制优化器行为的 Hint 不会生效,但是其他类别的 Hint 仍然能够生效。 -其中,有两类特定的语法由于语法冲突不能创建执行计划绑定,例如: +其中,有两类特定的语法由于语法冲突不能创建执行计划绑定,创建时会报语法错误,例如: ```sql -- 类型一:使用 `JOIN` 关键字但不通过 `USING` 关键字指定关联列的笛卡尔积 CREATE GLOBAL BINDING for - SELECT * FROM t t1 JOIN t t2 + SELECT * FROM orders o1 JOIN orders o2 USING - SELECT * FROM t t1 JOIN t t2; + SELECT * FROM orders o1 JOIN orders o2; -- 类型二:包含了 `USING` 关键字的 `delete` 语句 CREATE GLOBAL BINDING for - delete FROM t1 USING t1 JOIN t2 ON t1.a = t2.a + DELETE FROM users USING users JOIN orders ON users.id = orders.user_id USING - delete FROM t1 USING t1 JOIN t2 ON t1.a = t2.a; + DELETE FROM users USING users JOIN orders ON users.id = orders.user_id; ``` 可以通过等价的 SQL 改写绕过这个语法冲突的问题。例如,上述两个例子可以改写为: ```sql --- 类型一的第一种改写:为 `JOIN` 关键字添加 `USING` 子句 -CREATE GLOBAL BINDING for - SELECT * FROM t t1 JOIN t t2 USING (a) -USING - SELECT * FROM t t1 JOIN t t2 USING (a); --- 类型一的第二种改写:去掉 `JOIN` 关键字 +-- 类型一的改写:去掉 `JOIN` 关键字,用逗号代替 CREATE GLOBAL BINDING for - SELECT * FROM t t1, t t2 + SELECT * FROM orders o1, orders o2 USING - SELECT * FROM t t1, t t2; + SELECT * FROM orders o1, orders o2; --- 类型二的改写:去掉 `delete` 语句中的 `USING` 关键字 +-- 类型二的改写:去掉 `DELETE` 语句中的 `USING` 关键字 CREATE GLOBAL BINDING for - delete t1 FROM t1 JOIN t2 ON t1.a = t2.a + DELETE users FROM users JOIN orders ON users.id = orders.user_id USING - delete t1 FROM t1 JOIN t2 ON t1.a = t2.a; + DELETE users FROM users JOIN orders ON users.id = orders.user_id; ``` > **注意:** @@ -85,15 +80,15 @@ USING ```sql -- Hint 能生效的用法 CREATE GLOBAL BINDING for - INSERT INTO t1 SELECT * FROM t2 WHERE a > 1 AND b = 1 + INSERT INTO orders SELECT * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR) USING - INSERT INTO t1 SELECT /*+ use_index(@sel_1 t2, idx_a) */ * FROM t2 WHERE a > 1 AND b = 1; + INSERT INTO orders SELECT /*+ use_index(@sel_1 pre_orders, idx_created) */ * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR); -- Hint 不能生效的用法 CREATE GLOBAL BINDING for - INSERT INTO t1 SELECT * FROM t2 WHERE a > 1 AND b = 1 + INSERT INTO orders SELECT * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR) USING - INSERT /*+ use_index(@sel_1 t2, idx_a) */ INTO t1 SELECT * FROM t2 WHERE a > 1 AND b = 1; + INSERT /*+ use_index(@sel_1 pre_orders, idx_created) */ INTO orders SELECT * FROM pre_orders WHERE status = 'VALID' AND created <= (NOW() - INTERVAL 1 HOUR); ``` 如果在创建执行计划绑定时不指定作用域,隐式作用域 SESSION 会被使用。TiDB 优化器会将被绑定的 SQL 进行“标准化”处理,然后存储到系统表中。在处理 SQL 查询时,只要“标准化”后的 SQL 和系统表中某个被绑定的 SQL 语句一致,并且系统变量 [`tidb_use_plan_baselines`](/system-variables.md#tidb_use_plan_baselines-从-v40-版本开始引入) 的值为 `on`(其默认值为 `on`),即可使用相应的优化器 Hint。如果存在多个可匹配的执行计划,优化器会从中选择代价最小的一个进行绑定。 @@ -101,9 +96,9 @@ USING `标准化`:把 SQL 中的常量变成变量参数,对空格和换行符等做标准化处理,并对查询引用到的表显式指定数据库。例如: ```sql -SELECT * FROM t WHERE a > 1 +SELECT * FROM users WHERE balance > 100 -- 以上语句标准化后如下: -SELECT * FROM test . t WHERE a > ? +SELECT * FROM bookshop . users WHERE balance > ? ``` > **注意:** @@ -113,11 +108,11 @@ SELECT * FROM test . t WHERE a > ? > 例如: > > ```sql -> SELECT * FROM t WHERE a IN (1) -> SELECT * FROM t WHERE a IN (1,2,3) +> SELECT * FROM books WHERE type IN ('Novel') +> SELECT * FROM books WHERE type IN ('Novel','Life','Education') > -- 以上语句标准化后如下: -> SELECT * FROM test . t WHERE a IN ( ... ) -> SELECT * FROM test . t WHERE a IN ( ... ) +> SELECT * FROM bookshop . books WHERE type IN ( ... ) +> SELECT * FROM bookshop . books WHERE type IN ( ... ) > ``` > > 不同长度的 `IN` 表达式被标准化后,会被识别为同一条语句,因此只需要创建一条绑定,对这些表达式同时生效。