Skip to content

Commit

Permalink
Spm fix hint issue (#18534) (#19484)
Browse files Browse the repository at this point in the history
  • Loading branch information
ti-chi-bot authored Jan 7, 2025
1 parent 0f92a6e commit ba238e0
Showing 1 changed file with 23 additions and 28 deletions.
51 changes: 23 additions & 28 deletions sql-plan-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
```

> **注意:**
Expand All @@ -85,25 +80,25 @@ 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。如果存在多个可匹配的执行计划,优化器会从中选择代价最小的一个进行绑定。

`标准化`:把 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 > ?
```

> **注意:**
Expand All @@ -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` 表达式被标准化后,会被识别为同一条语句,因此只需要创建一条绑定,对这些表达式同时生效。
Expand Down

0 comments on commit ba238e0

Please sign in to comment.