Skip to content

feat: add docs about tidb cloud #20529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: release-8.1
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
857 changes: 857 additions & 0 deletions TOC-tidb-cloud.md

Large diffs are not rendered by default.

40 changes: 20 additions & 20 deletions accelerated-table-creation.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
---
title: 提升 TiDB 建表性能
aliases: ['/zh/tidb/stable/ddl-v2/']
summary: 介绍 TiDB 加速建表中的概念、原理、实现和影响
title: TiDB 加速表创建
aliases: ['/tidb/stable/ddl-v2/']
summary: 了解 TiDB 中创建表的性能优化的概念、原理和实现细节
---

# 提升 TiDB 建表性能
# TiDB 加速表创建

TiDB v7.6.0 引入了系统变量 [`tidb_ddl_version`](https://docs.pingcap.com/zh/tidb/v7.6/system-variables#tidb_ddl_version-从-v760-版本开始引入) 实现支持加速建表,可提升大批量建表的速度。从 v8.0.0 开始,该系统变量更名为 [`tidb_enable_fast_create_table`](/system-variables.md#tidb_enable_fast_create_table-从-v800-版本开始引入)
TiDB v7.6.0 引入了系统变量 [`tidb_ddl_version`](https://docs.pingcap.com/tidb/v7.6/system-variables#tidb_enable_fast_create_table-new-in-v800) 以支持加速表创建,提高批量创建表的效率。从 v8.0.0 开始,该系统变量被重命名为 [`tidb_enable_fast_create_table`](/system-variables.md#tidb_enable_fast_create_table-new-in-v800)

TiDB 中,对元数据对象的更改采用的是 online DDL 算法(即在线异步变更算法)。所有的 DDL Job 会提交到 `mysql.tidb_ddl_job` 表里,由 owner 节点拉取 DDL Job,执行完 online DDL 算法中的各个阶段后,将该 DDL Job 标记为已完成,移入 `mysql.tidb_ddl_history` 表中。因此 DDL 只能在 owner 节点执行,无法线性拓展
TiDB 使用在线异步 schema 变更算法来更改元数据。所有 DDL 任务都提交到 `mysql.tidb_ddl_job` 表中,由 owner 节点拉取 DDL 任务执行。在执行完在线 DDL 算法的每个阶段后,DDL 任务被标记为完成并移动到 `mysql.tidb_ddl_history` 表中。因此DDL 语句只能在 owner 节点上执行,无法线性扩展

然而,对于某些 DDL 而言,并不需要严格按照 online DDL 算法执行。如 `CREATE TABLE` 语句,Job 只有 `none``public` 两个状态,因此可以简化 DDL 的运行流程,使得建表语句可以在非 owner 节点执行,从而实现加速建表
然而,对于某些 DDL 语句,不需要严格遵循在线 DDL 算法。例如,`CREATE TABLE` 语句的任务只有 `none``public` 两个状态。因此,TiDB 可以简化 DDL 的执行过程,在非 owner 节点上执行 `CREATE TABLE` 语句以加速表创建

> **警告:**
>
> TiDB 加速建表目前为实验特性,不建议在生产环境中使用。该功能可能会在未事先通知的情况下发生变化或删除。如果发现 bug,请在 GitHub 上提 [issue](https://github.com/pingcap/tidb/issues) 反馈
> 该功能目前是实验性功能,不建议在生产环境中使用。该功能可能会在没有事先通知的情况下发生变更或被移除。如果发现 bug,请通过在 GitHub 上提交 [issue](https://github.com/pingcap/tidb/issues) 来反馈
## 与 TiDB 工具的兼容性

- [TiCDC](/ticdc/ticdc-overview.md) 暂不支持同步通过 TiDB 加速创建的表
- [TiCDC](https://docs.pingcap.com/tidb/stable/ticdc-overview) 不支持复制通过 `tidb_enable_fast_create_table` 创建的表

## 限制

TiDB 加速建表目前仅适用于 [`CREATE TABLE`](/sql-statements/sql-statement-create-table.md) 语句,且该建表语句不带任何外键约束
目前,表创建性能优化仅支持 [`CREATE TABLE`](/sql-statements/sql-statement-create-table.md) 语句,且该语句不能包含任何外键约束

## 使用方法
## 使用 `tidb_enable_fast_create_table` 加速表创建

你可以通过设置系统变量 [`tidb_enable_fast_create_table`](/system-variables.md#tidb_enable_fast_create_table-从-v800-版本开始引入) 的值来开启或关闭加速建表的功能
你可以通过指定系统变量 [`tidb_enable_fast_create_table`](/system-variables.md#tidb_enable_fast_create_table-new-in-v800) 的值来启用或禁用表创建性能优化

要开启该功能,将该变量的值设置为 `ON`
要启用表创建性能优化,将该变量的值设置为 `ON`

```sql
SET GLOBAL tidb_enable_fast_create_table = ON;
```

要关闭该功能,将该变量的值设置为 `OFF`
要禁用表创建性能优化,将该变量的值设置为 `OFF`

```sql
SET GLOBAL tidb_enable_fast_create_table = OFF;
```

## 实现原理

TiDB 加速建表的实现步骤如下
表创建性能优化的详细实现原理如下

1. 创建 `CREATE TABLE` Job
1. 创建 `CREATE TABLE` 任务

通过解析 `CREATE TABLE` 语句生成相应的 DDL Job
通过解析 `CREATE TABLE` 语句生成相应的 DDL 任务

2. 执行 `CREATE TABLE` Job
2. 执行 `CREATE TABLE` 任务

由接收该 `CREATE TABLE` 语句的 TiDB 节点直接执行建表语句,将表结构持久化到 TiKV。同时,将 `CREATE TABLE` Job 标记为已完成,插入到 `mysql.tidb_ddl_history` 表中。
接收 `CREATE TABLE` 语句的 TiDB 节点直接执行该语句,然后将表结构持久化到 TiKV。同时,将 `CREATE TABLE` 任务标记为完成并插入到 `mysql.tidb_ddl_history` 表中。

3. 同步表信息。

TiDB 通知其他节点同步该新建的表结构
TiDB 通知其他节点同步新创建的表结构
32 changes: 22 additions & 10 deletions agg-distinct-optimization.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
---
title: Distinct 优化
summary: 本文介绍了对于 DISTINCT 的优化,包括简单 DISTINCT 和聚合函数 DISTINCT 的优化。简单的 DISTINCT 通常会被优化成 GROUP BY 来执行。而带有 DISTINCT 的聚合函数会在 TiDB 侧单线程执行,可以通过系统变量或 TiDB 配置项控制优化器是否执行。在优化后,DISTINCT 被下推到了 Coprocessor,在 HashAgg 里新增了一个 group by 列
title: DISTINCT 优化
summary: 介绍 TiDB 查询优化器中的 `distinct` 优化
---

# Distinct 优化
# DISTINCT 优化

本文档介绍可用于 `DISTINCT` 的优化,包括简单 `DISTINCT` 和聚合函数 `DISTINCT` 的优化
本文档介绍 TiDB 查询优化器中的 `distinct` 优化,包括 `SELECT DISTINCT` 和聚合函数中的 `DISTINCT`

## 简单 DISTINCT
## `SELECT` 语句中的 `DISTINCT` 修饰符

通常简单的 `DISTINCT` 会被优化成 GROUP BY 来执行。例如:
`DISTINCT` 修饰符用于从结果集中删除重复的行。`SELECT DISTINCT` 会被转换为 `GROUP BY`例如:

```sql
mysql> explain select DISTINCT a from t;
mysql> explain SELECT DISTINCT a from t;
+--------------------------+---------+-----------+---------------+-------------------------------------------------------+
| id | estRows | task | access object | operator info |
+--------------------------+---------+-----------+---------------+-------------------------------------------------------+
@@ -23,11 +23,23 @@ mysql> explain select DISTINCT a from t;
3 rows in set (0.00 sec)
```

## 聚合函数 DISTINCT
## 聚合函数中的 `DISTINCT` 选项

通常来说,带有 `DISTINCT` 的聚合函数会单线程的在 TiDB 侧执行。使用系统变量 [`tidb_opt_distinct_agg_push_down`](/system-variables.md#tidb_opt_distinct_agg_push_down) 或者 TiDB 的配置项 [distinct-agg-push-down](/tidb-configuration-file.md#distinct-agg-push-down) 控制优化器是否执行带有 `DISTINCT` 的聚合函数(比如 `select count(distinct a) from t`)下推到 Coprocessor 的优化操作
通常,带有 `DISTINCT` 选项的聚合函数在 TiDB 层使用单线程执行模型执行

在以下示例中,`tidb_opt_distinct_agg_push_down` 开启前,TiDB 需要从 TiKV 读取所有数据,并在 TiDB 侧执行 `disctinct``tidb_opt_distinct_agg_push_down` 开启后,`distinct a` 被下推到了 Coprocessor,在 `HashAgg_5` 里新增了一个 `group by``test.t.a`
<CustomContent platform="tidb">

[`tidb_opt_distinct_agg_push_down`](/system-variables.md#tidb_opt_distinct_agg_push_down) 系统变量或 TiDB 中的 [`distinct-agg-push-down`](/tidb-configuration-file.md#distinct-agg-push-down) 配置项控制是否重写 distinct 聚合查询并将其下推到 TiKV 或 TiFlash Coprocessor。

</CustomContent>

<CustomContent platform="tidb-cloud">

TiDB 中的 [`tidb_opt_distinct_agg_push_down`](/system-variables.md#tidb_opt_distinct_agg_push_down) 系统变量控制是否重写 distinct 聚合查询并将其下推到 TiKV 或 TiFlash Coprocessor。

</CustomContent>

以下面的查询为例说明这个优化。`tidb_opt_distinct_agg_push_down` 默认是禁用的,这意味着聚合函数在 TiDB 层执行。通过将其值设置为 `1` 启用此优化后,`count(distinct a)` 中的 `distinct a` 部分会被下推到 TiKV 或 TiFlash Coprocessor:在 TiKV Coprocessor 中有一个 HashAgg_5 用于删除列 a 上的重复值。这可能会减少 TiDB 层 `HashAgg_8` 的计算开销。

```sql
mysql> desc select count(distinct a) from test.t;
70 changes: 35 additions & 35 deletions as-of-timestamp.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
---
title: 使用 AS OF TIMESTAMP 语法读取历史数据
summary: 了解如何使用 AS OF TIMESTAMP 语法读取历史数据
title: 使用 `AS OF TIMESTAMP` 子句读取历史数据
summary: 了解如何使用 `AS OF TIMESTAMP` 语句子句读取历史数据
---

# 使用 AS OF TIMESTAMP 语法读取历史数据
# 使用 `AS OF TIMESTAMP` 子句读取历史数据

本文档介绍如何通过 `AS OF TIMESTAMP` 语句使用 [Stale Read](/stale-read.md) 功能来读取 TiDB 历史版本数据,包括具体的操作示例以及历史数据的保存策略
本文档介绍如何使用 `AS OF TIMESTAMP` 子句执行[历史读取](/stale-read.md)功能来读取 TiDB 中的历史数据,包括具体的使用示例和保存历史数据的策略

TiDB 支持通过标准 SQL 接口,即通过 `AS OF TIMESTAMP` SQL 语法的形式读取历史数据,无需特殊的服务器或者驱动器。当数据被更新或删除后,你可以通过 SQL 接口将更新或删除前的数据读取出来
TiDB 支持通过标准 SQL 接口读取历史数据,即 `AS OF TIMESTAMP` SQL 子句,无需特殊的客户端或驱动程序。在数据更新或删除后,您可以使用此 SQL 接口读取更新或删除之前的历史数据

> **注意:**
>
> 读取历史数据时,即使当前数据的表结构相较于历史数据的表结构已经发生改变,历史数据也会以当时的历史表结构来返回
> 在读取历史数据时,即使当前表结构已经发生变化,TiDB 也会返回旧表结构的数据
## 语法方式
## 语法

你可以通过以下三种方式使用 `AS OF TIMESTAMP` 语法
您可以通过以下三种方式使用 `AS OF TIMESTAMP` 子句

- [`SELECT ... FROM ... AS OF TIMESTAMP`](/sql-statements/sql-statement-select.md)
- [`START TRANSACTION READ ONLY AS OF TIMESTAMP`](/sql-statements/sql-statement-start-transaction.md)
- [`SET TRANSACTION READ ONLY AS OF TIMESTAMP`](/sql-statements/sql-statement-set-transaction.md)

如果你想要指定一个精确的时间点,可在 `AS OF TIMESTAMP` 中使用日期时间和时间函数,日期时间的格式为:"2016-10-08 16:45:26.999",最小时间精度范围为毫秒,通常可只写到秒,例如 "2016-10-08 16:45:26"。你也可以通过 `NOW(3)` 函数获得精确到毫秒的当前时间。如果想读取几秒前的数据,推荐使用例如 `NOW() - INTERVAL 10 SECOND` 的表达式。(推荐)
如果要指定精确的时间点,可以在 `AS OF TIMESTAMP` 子句中设置日期时间值或使用时间函数。日期时间的格式类似于 "2016-10-08 16:45:26.999",最小时间单位为毫秒,但大多数情况下,秒级的时间单位足以指定日期时间,例如 "2016-10-08 16:45:26"。您也可以使用 `NOW(3)` 函数获取精确到毫秒的当前时间。如果要读取几秒前的数据,**建议**使用 `NOW() - INTERVAL 10 SECOND` 这样的表达式。

如果你想要指定一个时间范围,需要使用 [`TIDB_BOUNDED_STALENESS()`](/functions-and-operators/tidb-functions.md#tidb_bounded_staleness) 函数。使用该函数,TiDB 会在指定的时间范围内选择一个合适的时间戳,该时间戳能保证所访问的副本上不存在开始于这个时间戳之前且还没有提交的相关事务,即能保证所访问的可用副本上执行读取操作而且不会被阻塞。用法为 `TIDB_BOUNDED_STALENESS(t1, t2)`,其中 `t1``t2` 为时间范围的两端,支持使用日期时间和时间函数
如果要指定时间范围,可以在子句中使用 [`TIDB_BOUNDED_STALENESS()`](/functions-and-operators/tidb-functions.md#tidb_bounded_staleness) 函数。使用此函数时,TiDB 会在指定的时间范围内选择一个合适的时间戳。"合适"意味着在访问的副本上没有在此时间戳之前开始且尚未提交的事务,即 TiDB 可以在访问的副本上执行读取操作,且读取操作不会被阻塞。您需要使用 `TIDB_BOUNDED_STALENESS(t1, t2)` 来调用此函数。`t1``t2` 是时间范围的两端,可以使用日期时间值或时间函数指定

示例如下
以下是 `AS OF TIMESTAMP` 子句的一些示例

- `AS OF TIMESTAMP '2016-10-08 16:45:26'` 表示读取在 2016 年 10 月 8 日 16 点 45 分 26 秒时最新的数据
- `AS OF TIMESTAMP NOW() - INTERVAL 10 SECOND` 表示读取 10 秒前最新的数据
- `AS OF TIMESTAMP TIDB_BOUNDED_STALENESS('2016-10-08 16:45:26', '2016-10-08 16:45:29')` 表示读取在 2016 年 10 月 8 日 16 点 45 分 26 秒到 29 秒的时间范围内尽可能新的数据
- `AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(NOW() - INTERVAL 20 SECOND, NOW())` 表示读取 20 秒前到现在的时间范围内尽可能新的数据
- `AS OF TIMESTAMP '2016-10-08 16:45:26'`:告诉 TiDB 读取 2016 年 10 月 8 日 16:45:26 存储的最新数据
- `AS OF TIMESTAMP NOW() - INTERVAL 10 SECOND`:告诉 TiDB 读取 10 秒前存储的最新数据
- `AS OF TIMESTAMP TIDB_BOUNDED_STALENESS('2016-10-08 16:45:26', '2016-10-08 16:45:29')`:告诉 TiDB 读取 2016 年 10 月 8 日 16:45:26 到 16:45:29 时间范围内尽可能新的数据
- `AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(NOW() - INTERVAL 20 SECOND, NOW())`:告诉 TiDB 读取 20 秒前到现在时间范围内尽可能新的数据

> **注意:**
>
> 除了指定时间戳`AS OF TIMESTAMP` 语法最常用使用的方式是读几秒前的数据。如果采用这种方式,推荐读 5 秒以上的历史数据
> 除了指定时间戳外`AS OF TIMESTAMP` 子句最常见的用法是读取几秒前的数据。如果采用这种方法,建议读取 5 秒以前的历史数据
>
> 使用 Stale Read 时需要为 TiDB 和 PD 节点部署 NTP 服务,防止 TiDB 指定的时间戳超过当前最新的 TSO 分配进度(如几秒后的时间戳),或者落后于 GC safe point 的时间戳。当指定的时间戳超过服务范围,TiDB 会返回错误。
> 使用历史读取时,需要为 TiDB 和 PD 节点部署 NTP 服务。这可以避免 TiDB 使用的指定时间戳超前于最新的 TSO 分配进度(例如超前几秒的时间戳),或晚于 GC 安全点时间戳的情况。当指定的时间戳超出服务范围时,TiDB 会返回错误。
>
> 你可以通过调整 TiKV 的 `advance-ts-interval` 配置项提高 Stale Read 数据的时效性(即减少延时)。详情参见[减少 Stale Read 延时](/stale-read.md#减少-stale-read-延时)
> 为了减少延迟并提高历史读取数据的时效性,您可以修改 TiKV 的 `advance-ts-interval` 配置项。详情请参见[减少历史读取延迟](/stale-read.md#reduce-stale-read-latency)
## 示例
## 使用示例

本节通过多个示例介绍 `AS OF TIMESTAMP` 语法的不同使用方法。在本节中,先介绍如何准备用于恢复的数据,再分别展示如何通过 `SELECT``START TRANSACTION READ ONLY AS OF TIMESTAMP``SET TRANSACTION READ ONLY AS OF TIMESTAMP` 使用 `AS OF TIMESTAMP`
本节通过几个示例介绍使用 `AS OF TIMESTAMP` 子句的不同方式。首先介绍如何准备用于恢复的数据,然后分别展示如何在 `SELECT``START TRANSACTION READ ONLY AS OF TIMESTAMP``SET TRANSACTION READ ONLY AS OF TIMESTAMP` 中使用 `AS OF TIMESTAMP`

### 准备数据
### 准备数据样本

在准备数据阶段,创建一张表,并插入若干行数据
要准备用于恢复的数据,首先创建一个表并插入几行数据

```sql
create table t (c int);
@@ -96,7 +96,7 @@ select now();
1 row in set (0.00 sec)
```

更新某一行数据
更新一行数据

```sql
update t set c=22 where c=2;
@@ -106,7 +106,7 @@ update t set c=22 where c=2;
Query OK, 1 row affected (0.00 sec)
```

确认数据已经被更新
确认该行数据已更新

```sql
select * from t;
@@ -123,9 +123,9 @@ select * from t;
3 rows in set (0.00 sec)
```

### 通过 `SELECT` 读取历史数据
### 使用 `SELECT` 语句读取历史数据

通过 [`SELECT ... FROM ... AS OF TIMESTAMP`](/sql-statements/sql-statement-select.md) 语句读取一个基于历史时间的数据
您可以使用 [`SELECT ... FROM ... AS OF TIMESTAMP`](/sql-statements/sql-statement-select.md) 语句读取过去某个时间点的数据

```sql
select * from t as of timestamp '2021-05-26 16:45:26';
@@ -144,11 +144,11 @@ select * from t as of timestamp '2021-05-26 16:45:26';

> **注意:**
>
> 通过 `SELECT` 语句读取多个表时要保证 TIMESTAMP EXPRESSION 是一致的。比如:`select * from t as of timestamp NOW() - INTERVAL 2 SECOND, c as of timestamp NOW() - INTERVAL 2 SECOND;`。此外, `SELECT` 语句中,你必须要指定相关数据表的 as of 信息,若不指定,`SELECT` 语句会默认读最新的数据
> 在使用一个 `SELECT` 语句读取多个表时,需要确保 TIMESTAMP EXPRESSION 的格式一致。例如 `select * from t as of timestamp NOW() - INTERVAL 2 SECOND, c as of timestamp NOW() - INTERVAL 2 SECOND;`。此外,必须为 `SELECT` 语句中的相关表指定 `AS OF` 信息,否则 `SELECT` 语句默认读取最新数据
### 通过 `START TRANSACTION READ ONLY AS OF TIMESTAMP` 读取历史数据
### 使用 `START TRANSACTION READ ONLY AS OF TIMESTAMP` 语句读取历史数据

通过 [`START TRANSACTION READ ONLY AS OF TIMESTAMP`](/sql-statements/sql-statement-start-transaction.md) 语句,你可以开启一个基于历史时间的只读事务,该事务基于所提供的历史时间来读取历史数据
您可以使用 [`START TRANSACTION READ ONLY AS OF TIMESTAMP`](/sql-statements/sql-statement-start-transaction.md) 语句基于过去的某个时间点启动只读事务。该事务读取给定时间的历史数据

```sql
start transaction read only as of timestamp '2021-05-26 16:45:26';
@@ -181,7 +181,7 @@ commit;
Query OK, 0 rows affected (0.00 sec)
```

当事务结束后,即可读取最新数据
事务提交后,您可以读取最新数据

```sql
select * from t;
@@ -200,11 +200,11 @@ select * from t;

> **注意:**
>
> 通过 `START TRANSACTION READ ONLY AS OF TIMESTAMP` 开启的事务为只读事务。假如在该事务中执行写入操作,操作将会被该事务拒绝
> 如果使用 `START TRANSACTION READ ONLY AS OF TIMESTAMP` 语句启动事务,则该事务为只读事务。在此事务中,写入操作将被拒绝
### 通过 `SET TRANSACTION READ ONLY AS OF TIMESTAMP` 读取历史数据
### 使用 `SET TRANSACTION READ ONLY AS OF TIMESTAMP` 语句读取历史数据

通过 [`SET TRANSACTION READ ONLY AS OF TIMESTAMP`](/sql-statements/sql-statement-set-transaction.md) 语句,你可以将下一个事务设置为基于指定历史时间的只读事务。该事务将会基于所提供的历史时间来读取历史数据
您可以使用 [`SET TRANSACTION READ ONLY AS OF TIMESTAMP`](/sql-statements/sql-statement-set-transaction.md) 语句将下一个事务设置为基于过去某个时间点的只读事务。该事务读取给定时间的历史数据

```sql
set transaction read only as of timestamp '2021-05-26 16:45:26';
@@ -245,7 +245,7 @@ commit;
Query OK, 0 rows affected (0.00 sec)
```

当事务结束后,即可读取最新数据
事务提交后,您可以读取最新数据

```sql
select * from t;
@@ -264,4 +264,4 @@ select * from t;

> **注意:**
>
> 通过 `SET TRANSACTION READ ONLY AS OF TIMESTAMP` 开启的事务为只读事务。假如在该事务中执行写入操作,操作将会被该事务拒绝
> 如果使用 `SET TRANSACTION READ ONLY AS OF TIMESTAMP` 语句启动事务,则该事务为只读事务。在此事务中,写入操作将被拒绝
203 changes: 119 additions & 84 deletions auto-increment.md

Large diffs are not rendered by default.

105 changes: 55 additions & 50 deletions auto-random.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
---
title: AUTO_RANDOM
summary: 本文介绍了 TiDB 的 `AUTO_RANDOM` 列属性
summary: 了解 AUTO_RANDOM 属性
---

# AUTO_RANDOM <span class="version-mark">v3.1.0 版本开始引入</span>
# AUTO_RANDOM <span class="version-mark">v3.1.0 新功能</span>

## 使用场景

由于 `AUTO_RANDOM` 的值具有随机性和唯一性,因此 `AUTO_RANDOM` 通常用于代替 [`AUTO_INCREMENT`](/auto-increment.md),以避免 TiDB 分配连续的 ID 值造成单个存储节点的写热点问题。如果当前表的 `AUTO_INCREMENT` 列是主键列,且列类型为 `BIGINT`可以通过 `ALTER TABLE t MODIFY COLUMN id BIGINT AUTO_RANDOM(5);` `AUTO_INCREMENT` 切换成 `AUTO_RANDOM`
由于 `AUTO_RANDOM` 的值是随机且唯一的,`AUTO_RANDOM` 通常用于替代 [`AUTO_INCREMENT`](/auto-increment.md),以避免 TiDB 分配连续 ID 导致单个存储节点出现写入热点。如果当前 `AUTO_INCREMENT` 列是主键且类型为 `BIGINT`你可以执行 `ALTER TABLE t MODIFY COLUMN id BIGINT AUTO_RANDOM(5);` 语句将其从 `AUTO_INCREMENT` 切换为 `AUTO_RANDOM`

关于如何在高并发写入场景下调优 TiDB,请参阅 [TiDB 高并发写入场景最佳实践](/best-practices/high-concurrency-best-practices.md)
<CustomContent platform="tidb">

[`CREATE TABLE`](/sql-statements/sql-statement-create-table.md) 语句中的 `AUTO_RANDOM_BASE` 参数,也可以用来指定 `AUTO_RANDOM` 自增部分的初始值,该参数可以被认为属于内部接口的一部分,对于用户而言请忽略。
有关如何在 TiDB 中处理高并发写入工作负载的更多信息,请参见[高并发写入最佳实践](/best-practices/high-concurrency-best-practices.md)

</CustomContent>

[CREATE TABLE](/sql-statements/sql-statement-create-table.md) 语句中的 `AUTO_RANDOM_BASE` 参数用于设置 `auto_random` 的初始增量部分值。此选项可以视为内部接口的一部分。你可以忽略此参数。

## 基本概念

`AUTO_RANDOM` 是应用在 `BIGINT` 类型列的属性,用于列值的自动分配。其自动分配的值满足**随机性****唯一性**
`AUTO_RANDOM` 是一个用于自动为 `BIGINT` 列分配值的列属性。自动分配的值是**随机****唯一**

以下语句均可创建包含 `AUTO_RANDOM` 列的表,其中 `AUTO_RANDOM` 列必须被包含在主键中,并且是主键的第一列
要创建带有 `AUTO_RANDOM` 列的表,你可以使用以下语句。`AUTO_RANDOM` 列必须包含在主键中,且 `AUTO_RANDOM` 列是主键中的第一列

```sql
CREATE TABLE t (a BIGINT AUTO_RANDOM, b VARCHAR(255), PRIMARY KEY (a));
@@ -27,7 +31,7 @@ CREATE TABLE t (a BIGINT AUTO_RANDOM(5, 54), b VARCHAR(255), PRIMARY KEY (a));
CREATE TABLE t (a BIGINT AUTO_RANDOM(5, 54), b VARCHAR(255), PRIMARY KEY (a, b));
```

`AUTO_RANDOM` 关键字可以被包裹在 TiDB 可执行注释中,注释语法请参考 [TiDB 可执行注释](/comment-syntax.md#tidb-可执行的注释语法)
你可以将关键字 `AUTO_RANDOM` 包装在可执行注释中。有关更多详细信息,请参考 [TiDB 特定注释语法](/comment-syntax.md#tidb-specific-comment-syntax)

```sql
CREATE TABLE t (a bigint /*T![auto_rand] AUTO_RANDOM */, b VARCHAR(255), PRIMARY KEY (a));
@@ -36,10 +40,10 @@ CREATE TABLE t (a BIGINT /*T![auto_rand] AUTO_RANDOM(6) */, b VARCHAR(255), PRIM
CREATE TABLE t (a BIGINT /*T![auto_rand] AUTO_RANDOM(5, 54) */, b VARCHAR(255), PRIMARY KEY (a));
```

在用户执行 `INSERT` 语句时:
当你执行 `INSERT` 语句时:

- 如果语句中显式指定了 `AUTO_RANDOM` 列的值,则该值会被正常插入到表中
- 如果语句中没有显式指定 `AUTO_RANDOM` 列的值,TiDB 会自动生成一个随机的值插入到表中
- 如果你显式指定 `AUTO_RANDOM` 列的值,它将按原样插入表中
- 如果你未显式指定 `AUTO_RANDOM` 列的值,TiDB 将生成一个随机值并将其插入表中

```sql
tidb> CREATE TABLE t (a BIGINT PRIMARY KEY AUTO_RANDOM, b VARCHAR(255)) /*T! PRE_SPLIT_REGIONS=2 */ ;
@@ -96,54 +100,55 @@ tidb> SHOW TABLE t REGIONS;
4 rows in set (0.00 sec)
```

TiDB 自动分配的 `AUTO_RANDOM(S, R)` 列值共有 64 位:
TiDB 自动分配的 `AUTO_RANDOM(S, R)` 列值总共有 64 位:

- `S` 表示分片位的数量,取值范围是 `1``15`默认为 `5`
- `R` 表示自动分配值域的总长度,取值范围是 `32``64`默认为 `64`
- `S` 是分片位数。值范围从 `1``15`默认值为 `5`
- `R` 是自动分配范围的总长度。值范围从 `32``64`默认值为 `64`

有符号位的 `AUTO_RANDOM` 列值的具体结构如下
带有符号位的 `AUTO_RANDOM` 值的结构如下

| 符号位 | 保留位 | 分片位 | 自增位 |
|--------|-------------|--------|--------------|
| 1 bit | `64-R` bits | `S` bits | `R-1-S` bits |
| 符号位 | 保留位 | 分片位 | 自增位 |
|---------|-------------|--------|--------------|
| 1 | `64-R` | `S` | `R-1-S` |

无符号位的 `AUTO_RANDOM` 列值的具体结构如下
不带符号位的 `AUTO_RANDOM` 值的结构如下

| 保留位 | 分片位 | 自增位 |
|-------------|--------|------------|
| `64-R` bits | `S` bits | `R-S` bits |
| 保留位 | 分片位 | 自增位 |
|-------------|--------|--------------|
| `64-R` | `S` | `R-S` |

- 是否有符号位取决于该列是否存在 `UNSIGNED` 属性。
- 保留位的长度为 `64-R`,保留位的内容始终为 `0`
- 分片位的内容通过计算当前事务的开始时间的哈希值而得。要使用不同的分片位数量(例如 10),可以在建表时指定 `AUTO_RANDOM(10)`
- 自增位的值保存在存储引擎中,按顺序分配,每次分配完值后会自增 1。自增位保证了 `AUTO_RANDOM` 列值全局唯一。当自增位耗尽后,再次自动分配时会报 `Failed to read auto-increment value from storage engine` 的错误。
- 关于取值范围:最终生成值包含的最大位数 = 分片位 + 自增位。有符号位的列的取值范围是 `[-(2^(R-1))+1, (2^(R-1))-1]`。无符号位的列的取值范围是 `[0, (2^R)-1]`
- `AUTO_RANDOM` 可以与 [`PRE_SPLIT_REGIONS`](/sql-statements/sql-statement-split-region.md#pre_split_regions) 结合使用,用来在建表成功后就开始将表中的数据预均匀切分 `2^(PRE_SPLIT_REGIONS)` 个 Region。
- 值是否有符号位取决于相应列是否有 `UNSIGNED` 属性。
- 符号位的长度由是否存在 `UNSIGNED` 属性决定。如果有 `UNSIGNED` 属性,长度为 `0`。否则,长度为 `1`
- 保留位的长度为 `64-R`。保留位始终为 `0`
- 分片位的内容是通过计算当前事务开始时间的哈希值获得的。要使用不同长度的分片位(例如 10),可以在创建表时指定 `AUTO_RANDOM(10)`
- 自增位的值存储在存储引擎中并按顺序分配。每次分配新值时,该值增加 1。自增位确保 `AUTO_RANDOM` 的值在全局范围内是唯一的。当自增位用尽时,再次分配值时会报错 `Failed to read auto-increment value from storage engine`
- 值范围:最终生成值的最大位数 = 分片位 + 自增位。有符号列的范围是 `[-(2^(R-1))+1, (2^(R-1))-1]`,无符号列的范围是 `[0, (2^R)-1]`
- 你可以将 `AUTO_RANDOM``PRE_SPLIT_REGIONS` 一起使用。当表创建成功时,`PRE_SPLIT_REGIONS` 会将表中的数据预先分割为 `2^(PRE_SPLIT_REGIONS)` 个 Region。

> **注意:**
>
> 分片位长度 (`S`) 的选取
> 分片位(`S`)的选择
>
> - 由于总位数固定为 64 位,分片位的数量会影响到自增位的数量:当分片位数增加时,自增位数会减少,反之亦然。因此,你需要权衡“自动分配值的随机性”以及“可用空间”
> - 最佳实践是将分片位设置为 `log(2, x)`,其中 `x` 为当前集群存储引擎的数量。例如,一个 TiDB 集群中存在 16 个 TiKV,分片位可以设置为 `log(2, 16)`,即 `4`。在所有 Region 被均匀调度到各个 TiKV 上以后,此时大批量写入的负载可被均匀分布到不同 TiKV 节点,以实现资源最大化利用
> - 由于总共有 64 个可用位,分片位长度会影响自增位长度。也就是说,随着分片位长度的增加,自增位长度会减少,反之亦然。因此,你需要平衡分配值的随机性和可用空间
> - 最佳实践是将分片位设置为 `log(2, x)`,其中 `x` 是当前存储引擎的数量。例如,如果 TiDB 集群中有 16 个 TiKV 节点,你可以将分片位设置为 `log(2, 16)`,即 `4`。在所有 Region 均匀调度到每个 TiKV 节点后,批量写入的负载可以均匀分布到不同的 TiKV 节点,以最大化资源利用率
>
> 值域长度 (`R`) 的选取
> 范围(`R`)的选择
>
> - 通常,在应用程序的数值类型无法表示完整的 64 位整数时需要设置 `R` 参数。
> - 例如JSON number 类型的取值范围为 `[-(2^53)+1, (2^53)-1]`,而 TiDB 很容易会为 `AUTO_RANDOM(5)` 的列分配超出该范围的整数,导致应用程序读取该列的值时出现异常。此时,对于有符号的列你可以用 `AUTO_RANDOM(5, 54)` 代替 `AUTO_RANDOM(5)`无符号列可以用 `AUTO_RANDOM(5, 53)` 代替 `AUTO_RANDOM(5)`这样使得 TiDB 不会分配出大于 `9007199254740991` (2^53-1) 的整数。
> - 通常,当应用程序的数值类型无法表示完整的 64 位整数时,需要设置 `R` 参数。
> - 例如JSON 数字的范围是 `[-(2^53)+1, (2^53)-1]`TiDB 可以轻松地为定义为 `AUTO_RANDOM(5)` 的列分配超出此范围的整数,导致应用程序读取该列时出现意外行为。在这种情况下,你可以将有符号列的 `AUTO_RANDOM(5)` 替换为 `AUTO_RANDOM(5, 54)`将无符号列的 `AUTO_RANDOM(5)` 替换为 `AUTO_RANDOM(5, 53)`确保 TiDB 不会为该列分配大于 `9007199254740991`2^53-1的整数。
`AUTO RANDOM` 列隐式分配的值会影响 `last_insert_id()`可以使用 `SELECT last_insert_id()` 获取上一次 TiDB 隐式分配的 ID
隐式分配给 `AUTO_RANDOM` 列的值会影响 `last_insert_id()`要获取 TiDB 最后隐式分配的 ID,你可以使用 `SELECT last_insert_id ()` 语句

要查看某张含有 `AUTO_RANDOM` 属性的表的分片位数量,除了 `SHOW CREATE TABLE` 以外,还可以在系统表 `INFORMATION_SCHEMA.TABLES` `TIDB_ROW_ID_SHARDING_INFO` 一列中查到模式为 `PK_AUTO_RANDOM_BITS=x` 的值,其中 `x` 为分片位的数量
要查看带有 `AUTO_RANDOM` 列的表的分片位数,你可以执行 `SHOW CREATE TABLE` 语句。你还可以在 `information_schema.tables` 系统表的 `TIDB_ROW_ID_SHARDING_INFO` 列中看到 `PK_AUTO_RANDOM_BITS=x` 模式的值。`x` 是分片位数

创建完一张含有 `AUTO_RANDOM` 属性的表后,可以使用 `SHOW WARNINGS` 查看当前表可支持的最大隐式分配的次数
创建带有 `AUTO_RANDOM` 列的表后,你可以使用 `SHOW WARNINGS` 查看最大隐式分配次数

```sql
CREATE TABLE t (a BIGINT AUTO_RANDOM, b VARCHAR(255), PRIMARY KEY (a));
SHOW WARNINGS;
```

输出结果如下
输出如下

```sql
+-------+------+---------------------------------------------------------+
@@ -154,19 +159,19 @@ SHOW WARNINGS;
1 row in set (0.00 sec)
```

## ID 隐式分配规则
## ID 的隐式分配规则

`AUTO_RANDOM` 列隐式分配的值和自增列类似,也遵循 session 变量 [`auto_increment_increment`](/system-variables.md#auto_increment_increment)[`auto_increment_offset`](/system-variables.md#auto_increment_offset) 的控制,其中隐式分配值的自增位 (ID) 满足等式 `(ID - auto_increment_offset) % auto_increment_increment == 0`
TiDB 对 `AUTO_RANDOM` 列的隐式分配值与 `AUTO_INCREMENT` 列类似。它们也受会话级系统变量 [`auto_increment_increment`](/system-variables.md#auto_increment_increment)[`auto_increment_offset`](/system-variables.md#auto_increment_offset) 的控制。隐式分配值的自增位(ID)符合方程 `(ID - auto_increment_offset) % auto_increment_increment == 0`

## 使用限制
## 限制

目前在 TiDB 中使用 `AUTO_RANDOM` 有以下限制
使用 `AUTO_RANDOM` 时请注意以下限制

- 要使用显式插入的功能,需要将系统变量 `@@allow_auto_random_explicit_insert` 的值设置为 `1`默认值为 `0`)。不建议自行显式指定含有 `AUTO_RANDOM` 列的值。不恰当地显式赋值,可能会导致该表提前耗尽用于自动分配的数值
- 该属性必须指定在 `BIGINT` 类型的主键列上,否则会报错。此外,当主键属性为 `NONCLUSTERED` 时,即使是整型主键列,也不支持使用 `AUTO_RANDOM`要了解关于 `CLUSTERED` 主键的详细信息,请参考[聚簇索引](/clustered-indexes.md)
- 不支持使用 `ALTER TABLE` 来修改 `AUTO_RANDOM` 属性,包括添加或移除该属性
- 支持将 `AUTO_INCREMENT` 属性改为 `AUTO_RANDOM` 属性。但在 `AUTO_INCREMENT` 的列数据最大值已接近 `BIGINT` 类型最大值的情况下,修改可能会失败
- 不支持修改含有 `AUTO_RANDOM` 属性的主键列的列类型。
- 不支持与 `AUTO_INCREMENT` 同时指定在同一列上
- 不支持与列的默认值 `DEFAULT` 同时指定在同一列上
- `AUTO_RANDOM` 列的数据很难迁移到 `AUTO_INCREMENT` 列上,因为 `AUTO_RANDOM` 列自动分配的值通常都很大
- 要显式插入值,你需要将 `@@allow_auto_random_explicit_insert` 系统变量的值设置为 `1`默认为 `0`)。**不建议**在插入数据时显式指定具有 `AUTO_RANDOM` 属性的列的值。否则,此表可自动分配的数值可能会提前用尽
- 仅为 `BIGINT` 类型的主键列指定此属性。否则,会出现错误。此外,当主键的属性为 `NONCLUSTERED` 时,即使在整数主键上也不支持 `AUTO_RANDOM`有关 `CLUSTERED` 类型的主键的更多详细信息,请参考[聚簇索引](/clustered-indexes.md)
- 你不能使用 `ALTER TABLE` 修改 `AUTO_RANDOM` 属性,包括添加或删除此属性
- 如果最大值接近列类型的最大值,则不能使用 `ALTER TABLE` `AUTO_INCREMENT` 更改为 `AUTO_RANDOM`
- 你不能更改指定了 `AUTO_RANDOM` 属性的主键列的列类型。
- 你不能同时为同一列指定 `AUTO_RANDOM``AUTO_INCREMENT`
- 你不能同时为同一列指定 `AUTO_RANDOM``DEFAULT`(列的默认值)
- 当在列上使用 `AUTO_RANDOM` 时,很难将列属性改回 `AUTO_INCREMENT`,因为自动生成的值可能会很大
116 changes: 63 additions & 53 deletions basic-sql-operations.md
Original file line number Diff line number Diff line change
@@ -1,93 +1,99 @@
---
title: SQL 基本操作
summary: TiDB 是一个兼容 MySQL 的数据库,可以执行 DDL、DML、DQL 和 DCL 操作。可以使用 SHOW DATABASES 查看数据库列表,使用 CREATE DATABASE 创建数据库,使用 DROP DATABASE 删除数据库。使用 CREATE TABLE 创建表,使用 SHOW CREATE TABLE 查看建表语句,使用 DROP TABLE 删除表。使用 CREATE INDEX 创建索引,使用 SHOW INDEX 查看表内所有索引,使用 DROP INDEX 删除索引。使用 INSERT 向表内插入记录,使用 UPDATE 修改记录,使用 DELETE 删除记录。使用 SELECT 检索表内数据,使用 WHERE 子句进行筛选。使用 CREATE USER 创建用户,使用 GRANT 授权用户,使用 DROP USER 删除用户
title: 使用 TiDB 探索 SQL
summary: 了解 TiDB 数据库的基本 SQL 语句
---

# SQL 基本操作
# 使用 TiDB 探索 SQL

成功部署 TiDB 集群之后,便可以在 TiDB 中执行 SQL 语句了。因为 TiDB 兼容 MySQL,你可以使用 MySQL 客户端连接 TiDB,并且[大多数情况下](/mysql-compatibility.md)可以直接执行 MySQL 语句
TiDB 与 MySQL 兼容,在大多数情况下您可以直接使用 MySQL 语句。有关不支持的功能,请参阅[与 MySQL 的兼容性](/mysql-compatibility.md#unsupported-features)

SQL 是一门声明性语言,它是数据库用户与数据库交互的方式。它更像是一种自然语言,好像在用英语与数据库进行对话。本文档介绍基本的 SQL 操作。完整的 TiDB SQL 语句列表,参见 [SQL 语句概览](/sql-statements/sql-statement-overview.md)
<CustomContent platform="tidb">

要试验 SQL 并测试 TiDB 与 MySQL 查询的兼容性,您可以尝试使用 [TiDB Playground](https://play.tidbcloud.com/?utm_source=docs&utm_medium=basic-sql-operations)。您也可以先部署一个 TiDB 集群,然后在其中运行 SQL 语句。

</CustomContent>

本页将指导您了解基本的 TiDB SQL 语句,如 DDL、DML 和 CRUD 操作。有关 TiDB 语句的完整列表,请参阅 [SQL 语句概览](/sql-statements/sql-statement-overview.md)

## 分类

SQL 语言通常按照功能划分成以下的 4 个部分
SQL 根据其功能分为以下 4 种类型

- DDL (Data Definition Language):数据定义语言,用来定义数据库对象,包括库、表、视图和索引等
- DDL数据定义语言):用于定义数据库对象,包括数据库、表、视图和索引

- DML (Data Manipulation Language):数据操作语言,用来操作和业务相关的记录
- DML数据操作语言):用于操作应用程序相关的记录

- DQL (Data Query Language):数据查询语言,用来查询经过条件筛选的记录
- DQL数据查询语言):用于在条件过滤后查询记录

- DCL (Data Control Language):数据控制语言,用来定义访问权限和安全级别
- DCL数据控制语言):用于定义访问权限和安全级别

常用的 DDL 功能是对象(如表、索引等)的创建、属性修改和删除,对应的命令分别是 CREATEALTER 和 DROP。
常见的 DDL 功能是创建、修改和删除对象(如表和索引)。相应的命令是 `CREATE``ALTER``DROP`

## 查看、创建和删除数据库
## 显示、创建和删除数据库

TiDB 语境中的 Database 或者说数据库,可以认为是表和索引等对象的集合
TiDB 中的数据库可以被视为表和索引等对象的集合

使用 `SHOW DATABASES` 语句查看系统中数据库列表
要显示数据库列表,使用 `SHOW DATABASES` 语句

{{< copyable "sql" >}}

```sql
SHOW DATABASES;
```

使用名为 `mysql` 的数据库:
要使用名为 `mysql` 的数据库,使用以下语句

{{< copyable "sql" >}}

```sql
USE mysql;
```

使用 `SHOW TABLES` 语句查看数据库中的所有表。例如
要显示数据库中的所有表,使用 `SHOW TABLES` 语句

{{< copyable "sql" >}}

```sql
SHOW TABLES FROM mysql;
```

使用 `CREATE DATABASE` 语句创建数据库。语法如下
要创建数据库,使用 `CREATE DATABASE` 语句

{{< copyable "sql" >}}

```sql
CREATE DATABASE db_name [options];
```

例如,要创建一个名为 `samp_db` 的数据库,可使用以下语句
要创建名为 `samp_db` 的数据库,使用以下语句

{{< copyable "sql" >}}

```sql
CREATE DATABASE IF NOT EXISTS samp_db;
```

添加 `IF NOT EXISTS` 可防止发生错误
添加 `IF NOT EXISTS` 可以防止在数据库已存在时出现错误

使用 `DROP DATABASE` 语句删除数据库。例如
要删除数据库,使用 `DROP DATABASE` 语句

{{< copyable "sql" >}}

```sql
DROP DATABASE samp_db;
```

## 创建、查看和删除表
## 创建、显示和删除表

使用 `CREATE TABLE` 语句创建表。语法如下
要创建表,使用 `CREATE TABLE` 语句

{{< copyable "sql" >}}

```sql
CREATE TABLE table_name column_name data_type constraint;
```

例如,要创建一个名为 `person` 的表,包括编号、名字、生日等字段,可使用以下语句
例如,要创建一个名为 `person` 的表,其中包括编号、姓名和生日等字段,使用以下语句

{{< copyable "sql" >}}

@@ -99,65 +105,65 @@ CREATE TABLE person (
);
```

使用 `SHOW CREATE` 语句查看建表语句,即 DDL。例如
要查看创建表的语句(DDL),使用 `SHOW CREATE` 语句

{{< copyable "sql" >}}

```sql
SHOW CREATE TABLE person;
SHOW CREATE table person;
```

使用 `DROP TABLE` 语句删除表。例如
要删除表,使用 `DROP TABLE` 语句

{{< copyable "sql" >}}

```sql
DROP TABLE person;
```

## 创建、查看和删除索引
## 创建、显示和删除索引

索引通常用于加速索引列上的查询。对于值不唯一的列,可使用 `CREATE INDEX` `ALTER TABLE` 语句创建普通索引。例如
索引用于加快对索引列的查询。要为值不唯一的列创建索引,使用 `CREATE INDEX` 语句

{{< copyable "sql" >}}

```sql
CREATE INDEX person_id ON person (id);
```

或者
或使用 `ALTER TABLE` 语句

{{< copyable "sql" >}}

```sql
ALTER TABLE person ADD INDEX person_id (id);
```

对于值唯一的列,可以创建唯一索引。例如
要为值唯一的列创建唯一索引,使用 `CREATE UNIQUE INDEX` 语句

{{< copyable "sql" >}}

```sql
CREATE UNIQUE INDEX person_unique_id ON person (id);
```

或者
或使用 `ALTER TABLE` 语句

{{< copyable "sql" >}}

```sql
ALTER TABLE person ADD UNIQUE person_unique_id (id);
```

使用 `SHOW INDEX` 语句查看表内所有索引
要显示表中的所有索引,使用 `SHOW INDEX` 语句

{{< copyable "sql" >}}

```sql
SHOW INDEX FROM person;
```

使用 `ALTER TABLE``DROP INDEX` 语句来删除索引。与 `CREATE INDEX` 语句类似,`DROP INDEX` 也可以嵌入 `ALTER TABLE` 语句。例如
要删除索引,使用 `DROP INDEX``ALTER TABLE` 语句。`DROP INDEX` 可以嵌套在 `ALTER TABLE`

{{< copyable "sql" >}}

@@ -171,59 +177,63 @@ DROP INDEX person_id ON person;
ALTER TABLE person DROP INDEX person_unique_id;
```

注意:DDL 操作不是事务,在执行 DDL 时,不需要对应 COMMIT 语句。
> **注意:**
>
> DDL 操作不是事务。执行 DDL 操作时不需要运行 `COMMIT` 语句。
常用的 DML 功能是对表记录的新增、修改和删除,对应的命令分别是 INSERT、UPDATE 和 DELETE。
## 插入、更新和删除数据

## 记录的增删改
常见的 DML 功能是添加、修改和删除表记录。相应的命令是 `INSERT``UPDATE``DELETE`

使用 `INSERT` 语句向表内插入表记录。例如
要向表中插入数据,使用 `INSERT` 语句

{{< copyable "sql" >}}

```sql
INSERT INTO person VALUES(1,'tom','20170912');
```

使用 `INSERT` 语句向表内插入包含部分字段数据的表记录。例如
要向表中插入包含某些字段数据的记录,使用 `INSERT` 语句

{{< copyable "sql" >}}

```sql
INSERT INTO person(id,name) VALUES('2','bob');
```

使用 `UPDATE` 语句向表内修改表记录的部分字段数据。例如
要更新表中记录的某些字段,使用 `UPDATE` 语句

{{< copyable "sql" >}}

```sql
UPDATE person SET birthday='20180808' WHERE id=2;
```

使用 `DELETE` 语句向表内删除部分表记录。例如
要删除表中的数据,使用 `DELETE` 语句

{{< copyable "sql" >}}

```sql
DELETE FROM person WHERE id=2;
```

注意:UPDATE 和 DELETE 操作如果不带 WHERE 过滤条件是对全表进行操作。

DQL 数据查询语言是从一个表或多个表中检索出想要的数据行,通常是业务开发的核心内容
> **注意:**
>
> 没有 `WHERE` 子句作为过滤器的 `UPDATE``DELETE` 语句会对整个表进行操作
## 查询数据

使用 `SELECT` 语句检索表内数据。例如:
DQL 用于从一个或多个表中检索所需的数据行。

要查看表中的数据,使用 `SELECT` 语句:

{{< copyable "sql" >}}

```sql
SELECT * FROM person;
```

SELECT 后面加上要查询的列名。例如
要查询特定列,在 `SELECT` 关键字后添加列名

{{< copyable "sql" >}}

@@ -240,43 +250,43 @@ SELECT name FROM person;
1 rows in set (0.00 sec)
```

使用 WHERE 子句,对所有记录进行是否符合条件的筛选后再返回。例如
使用 `WHERE` 子句过滤所有匹配条件的记录,然后返回结果

{{< copyable "sql" >}}

```sql
SELECT * FROM person WHERE id<5;
SELECT * FROM person where id<5;
```

常用的 DCL 功能是创建或删除用户,和对用户权限的管理。

## 创建、授权和删除用户

使用 `CREATE USER` 语句创建一个用户 `tiuser`,密码为 `123456`
DCL 通常用于创建或删除用户,以及管理用户权限。

要创建用户,使用 `CREATE USER` 语句。以下示例创建一个名为 `tiuser`,密码为 `123456` 的用户:

{{< copyable "sql" >}}

```sql
CREATE USER 'tiuser'@'localhost' IDENTIFIED BY '123456';
```

授权用户 `tiuser` 可检索数据库 `samp_db` 内的表
要授予 `tiuser` 检索 `samp_db` 数据库中表的权限

{{< copyable "sql" >}}

```sql
GRANT SELECT ON samp_db.* TO 'tiuser'@'localhost';
```

查询用户 `tiuser` 的权限:
要检查 `tiuser` 的权限:

{{< copyable "sql" >}}

```sql
SHOW GRANTS for tiuser@localhost;
```

删除用户 `tiuser`
要删除 `tiuser`

{{< copyable "sql" >}}

48 changes: 35 additions & 13 deletions best-practices/uuid.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,55 @@
---
title: UUID 最佳实践
summary: 了解在 TiDB 中使用 UUID 的最佳实践和策略
summary: UUID 作为主键时具有以下优势:减少网络往返、支持大多数编程语言和数据库、防止枚举攻击。建议将 UUID 以二进制形式存储在 `BINARY(16)` 列中。同时建议在 TiDB 中不要设置 `swap_flag` 以防止热点。MySQL 也支持 UUID
---

# UUID 最佳实践

## UUID 概述

通用唯一标识符 (UUID) 用作主键而不是 [`AUTO_INCREMENT`](/auto-increment.md) 整数值时,可以提供以下好处
与使用 [`AUTO_INCREMENT`](/auto-increment.md) 整数值相比,使用通用唯一标识符(UUID)作为主键具有以下优势

- UUID 可以在多个系统生成,而不会产生冲突。某些情况下可以减少到 TiDB 的网络往返次数,从而提高性能。
- 绝大多数编程语言和数据库系统都支持 UUID。
- 用在 URL 中时,UUID 不容易被枚举攻击。相比之下,使用 `AUTO_INCREMENT` 数字,则很容易让发票 ID 或用户 ID 被猜出
- UUID 可以在多个系统上生成而不会发生冲突。在某些情况下,这意味着可以减少与 TiDB 的网络往返次数,从而提高性能。
- 大多数编程语言和数据库系统都支持 UUID。
- 当作为 URL 的一部分使用时,UUID 不容易受到枚举攻击。相比之下,使用 `AUTO_INCREMENT` 数字时,可能会被猜测到发票 ID 或用户 ID。

## 最佳实践

### 二进制存储
### 以二进制形式存储

UUID 文本是一个包含 36 字符的字符串,如 `ab06f63e-8fe7-11ec-a514-5405db7aad56`。使用 [`UUID_TO_BIN()`](/functions-and-operators/miscellaneous-functions.md#uuid_to_bin) 可将 UUID 文本格式转换为 16 字节的二进制格式。这样,你可以将文本存储在 [`BINARY(16)`](/data-type-string.md#binary-类型) 列中。检索 UUID 时,可以使用 [`BIN_TO_UUID()`](/functions-and-operators/miscellaneous-functions.md#bin_to_uuid) 函数再将其转换回文本格式
文本形式的 UUID 格式如下:`ab06f63e-8fe7-11ec-a514-5405db7aad56`,这是一个 36 个字符的字符串。通过使用 [`UUID_TO_BIN()`](/functions-and-operators/miscellaneous-functions.md#uuid_to_bin),可以将文本格式转换为 16 字节的二进制格式。这样你就可以将文本存储在 [`BINARY(16)`](/data-type-string.md#binary-type) 列中。在检索 UUID 时,你可以使用 [`BIN_TO_UUID()`](/functions-and-operators/miscellaneous-functions.md#bin_to_uuid) 函数将其转换回文本格式

### UUID 格式二进制顺序和聚簇主键

`UUID_TO_BIN()` 函数可以接收一个参数 (UUID) 或两个参数(第一个为 UUID,第二个为 `swap_flag`。建议不要在 TiDB 中设置 `swap_flag`,以避免出现[热点](/best-practices/high-concurrency-best-practices.md)问题
`UUID_TO_BIN()` 函数可以使用一个参数(UUID或两个参数(第二个参数是 `swap_flag`)。

同时,你也可以在 UUID 主键上显式设置 [`CLUSTERED` 选项](/clustered-indexes.md)来避免热点问题。
<CustomContent platform="tidb">

为了演示 `swap_flag` 的效果,本文以表结构相同的两张表为例。区别在于,`uuid_demo_1` 表中插入的数据使用 `UUID_TO_BIN(?, 0)`,而 `uuid_demo_2` 表中使用 `UUID_TO_BIN(?, 1)`
建议在 TiDB 中不要设置 `swap_flag`,以避免[热点问题](/best-practices/high-concurrency-best-practices.md)

在如下的[流量可视化页面](/dashboard/dashboard-key-visualizer.md),你可以看到写入操作集中在 `uuid_demo_2` 表的单个 Region 中,而这个表中的二进制格式字段顺序被调换过。
</CustomContent>

<CustomContent platform="tidb-cloud">

建议在 TiDB 中不要设置 `swap_flag`,以避免热点问题。

</CustomContent>

你还可以为基于 UUID 的主键显式设置 [`CLUSTERED` 选项](/clustered-indexes.md),以避免热点。

为了演示 `swap_flag` 的效果,这里有两个结构相同的表。区别在于插入到 `uuid_demo_1` 的数据使用 `UUID_TO_BIN(?, 0)`,而 `uuid_demo_2` 使用 `UUID_TO_BIN(?, 1)`

<CustomContent platform="tidb">

在下面的 [Key Visualizer](/dashboard/dashboard-key-visualizer.md) 截图中,你可以看到写入集中在 `uuid_demo_2` 表的单个区域,这个表在二进制格式中交换了字段的顺序。

</CustomContent>

<CustomContent platform="tidb-cloud">

在下面的 [Key Visualizer](/tidb-cloud/tune-performance.md#key-visualizer) 截图中,你可以看到写入集中在 `uuid_demo_2` 表的单个区域,这个表在二进制格式中交换了字段的顺序。

</CustomContent>

![Key Visualizer](/media/best-practices/uuid_keyviz.png)

@@ -47,6 +69,6 @@ CREATE TABLE `uuid_demo_2` (
)
```

## MySQL 兼容性
## MySQL 兼容性

UUID 也可以在 MySQL 中使用。MySQL 8.0 引入了 `BIN_TO_UUID()``UUID_TO_BIN()` 函数`UUID()` 函数在较早的 MySQL 版本中也可以使用
MySQL 也可以使用 UUID。`BIN_TO_UUID()``UUID_TO_BIN()` 函数是在 MySQL 8.0 中引入的`UUID()` 函数在早期的 MySQL 版本中也可用
121 changes: 64 additions & 57 deletions blocklist-control-plan.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,90 @@
---
title: 优化规则与表达式下推的黑名单
summary: 了解优化规则与表达式下推的黑名单
title: 优化规则和表达式下推的黑名单
summary: 了解如何使用黑名单来控制优化规则和表达式下推的行为
---

# 优化规则与表达式下推的黑名单
# 优化规则和表达式下推的黑名单

本文主要介绍优化规则的黑名单与表达式下推的黑名单
本文档介绍如何使用优化规则黑名单和表达式下推黑名单来控制 TiDB 的行为

## 优化规则黑名单

**优化规则黑名单**是针对优化规则的调优手段之一,主要用于手动禁用一些优化规则
优化规则黑名单是调整优化规则的一种方式,主要用于手动禁用某些优化规则

### 重要的优化规则

|**优化规则**|**规则名称**|**简介**|
|**优化规则**|**规则名称**|**描述**|
| :--- | :--- | :--- |
| 列裁剪 | column_prune | 对于上层算子不需要的列,不在下层算子输出该列,减少计算 |
| 子查询去关联 | decorrelate | 尝试对相关子查询进行改写,将其转换为普通 join 或 aggregation 计算 |
| 聚合消除 | aggregation_eliminate | 尝试消除执行计划中的某些不必要的聚合算子 |
| 投影消除 | projection_eliminate | 消除执行计划中不必要的投影算子 |
| 最大最小消除 | max_min_eliminate | 改写聚合中的 max/min 计算,转化为 `order by` + `limit 1` |
| 谓词下推 | predicate_push_down | 尝试将执行计划中过滤条件下推到离数据源更近的算子上 |
| 外连接消除 | outer_join_eliminate | 尝试消除执行计划中不必要的 left join 或者 right join |
| 分区裁剪 | partition_processor | 将分区表查询改成为用 union all,并裁剪掉不满足过滤条件的分区 |
| 聚合下推 | aggregation_push_down | 尝试将执行计划中的聚合算子下推到更底层的计算节点 |
| TopN 下推 | topn_push_down | 尝试将执行计划中的 TopN 算子下推到离数据源更近的算子上 |
| Join 重排序 | join_reorder | 对多表 join 确定连接顺序 |
| 从窗口函数中推导 TopN 或 Limit | derive_topn_from_window | 从窗口函数中推导出 TopN 或者 Limit |
| 列剪裁 | column_prune | 如果上层执行器不需要某列,则该操作符会将其剪裁掉。 |
| 子查询去相关 | decorrelate | 尝试将相关子查询重写为非相关连接或聚合。 |
| 聚合消除 | aggregation_eliminate | 尝试从执行计划中移除不必要的聚合操作符。 |
| 投影消除 | projection_eliminate | 从执行计划中移除不必要的投影操作符。 |
| 最大/最小值消除 | max_min_eliminate | 将聚合中的某些 max/min 函数重写为 `order by` + `limit 1` 形式。 |
| 谓词下推 | predicate_push_down | 尝试将谓词下推到更接近数据源的操作符。 |
| 外连接消除 | outer_join_eliminate | 尝试从执行计划中移除不必要的左连接或右连接。 |
| 分区裁剪 | partition_processor | 裁剪被谓词拒绝的分区,并将分区表查询重写为 `UnionAll + Partition Datasource` 形式。 |
| 聚合下推 | aggregation_push_down | 尝试将聚合下推到其子节点。 |
| TopN 下推 | topn_push_down | 尝试将 TopN 操作符下推到更接近数据源的位置。 |
| 连接重排序 | join_reorder | 决定多表连接的顺序。 |
| 从窗口函数推导 TopN 或 Limit | derive_topn_from_window | 从窗口函数推导出 TopN Limit 操作符。 |

### 禁用优化规则

当某些优化规则在一些特殊查询中的优化结果不理想时,可以使用**优化规则黑名单**禁用一些优化规则
如果某些规则导致特定查询的执行计划不够优化,你可以使用优化规则黑名单来禁用这些规则

#### 使用方法

> **注意:**
>
> 以下操作都需要数据库的 super privilege 权限。每个优化规则都有各自的名字,比如列裁剪的名字是 "column_prune"。所有优化规则的名字都可以在[重要的优化规则](#重要的优化规则)表格中第二列查到
> 以下所有操作都需要数据库的 `super privilege` 权限。每个优化规则都有一个名称。例如,列剪裁的名称是 `column_prune`。所有优化规则的名称可以在[重要的优化规则](#重要的优化规则)表的第二列中找到
- 如果你想禁用某些规则,可以在 `mysql.opt_rule_blacklist` 表中写入规则的名字,例如:
- 如果你想禁用某些规则,将其名称写入 `mysql.opt_rule_blacklist` 表。例如:

{{< copyable "sql" >}}

```sql
INSERT INTO mysql.opt_rule_blacklist VALUES("join_reorder"), ("topn_push_down");
```

执行以下 SQL 语句可让禁用规则立即生效,包括相应 TiDB Server 的所有旧链接
执行以下 SQL 语句可以使上述操作立即生效。生效范围包括相应 TiDB 服务器的所有旧连接

{{< copyable "sql" >}}

```sql
ADMIN reload opt_rule_blacklist;
admin reload opt_rule_blacklist;
```

> **注意:**
>
> `admin reload opt_rule_blacklist` 只对执行该 SQL 语句的 TiDB server 生效。若需要集群中所有 TiDB server 生效,需要在每台 TiDB server 上执行该 SQL 语句
> `admin reload opt_rule_blacklist` 只对运行该语句的 TiDB 服务器生效。如果你想让集群中的所有 TiDB 服务器都生效,需要在每个 TiDB 服务器上运行此命令

- 需要解除一条规则的禁用时,需要删除表中禁用该条规则的相应数据,再执行 `admin reload`
- 如果你想重新启用某个规则,删除表中相应的数据,然后运行 `admin reload` 语句

{{< copyable "sql" >}}

```sql
DELETE FROM mysql.opt_rule_blacklist WHERE name IN ("join_reorder", "topn_push_down");
```

{{< copyable "sql" >}}

```sql
admin reload opt_rule_blacklist;
```

## 表达式下推黑名单

**表达式下推黑名单**是针对表达式下推的调优手段之一,主要用于对于某些存储类型手动禁用一些表达式
表达式下推黑名单是调整表达式下推的一种方式,主要用于手动禁用某些特定数据类型的表达式

### 已支持下推的表达式
### 支持下推的表达式

目前已经支持下推的表达式信息,请参考[表达式列表](/functions-and-operators/expressions-pushed-down.md#已支持下推的表达式列表)。
关于支持下推的表达式的更多信息,请参见[支持下推到 TiKV 的表达式](/functions-and-operators/expressions-pushed-down.md#支持下推到-tikv-的表达式)。

### 禁止特定表达式下推
### 禁用特定表达式的下推

当函数的计算过程由于下推而出现异常时,可通过黑名单功能禁止其下推来快速恢复业务。具体而言,你可以将上述支持的函数或运算符名加入黑名单 `mysql.expr_pushdown_blacklist` 中,以禁止特定表达式下推
当由于表达式下推导致结果错误时,你可以使用黑名单来快速恢复应用程序。具体来说,你可以将一些支持的函数或运算符添加到 `mysql.expr_pushdown_blacklist` 表中,以禁用特定表达式的下推

`mysql.expr_pushdown_blacklist` 的 schema 如下
`mysql.expr_pushdown_blacklist` 的表结构如下

{{< copyable "sql" >}}

@@ -98,44 +103,46 @@ DESC mysql.expr_pushdown_blacklist;
3 rows in set (0.00 sec)
```

以上结果字段解释如下
以下是每个字段的说明

+ `name`禁止下推的函数名
+ `store_type`用于指明希望禁止该函数下推到哪些组件进行计算。组件可选 `tidb``tikv``tiflash``store_type` 不区分大小写,如果需要禁止向多个存储引擎下推,各个存储之间需用逗号隔开
- `store_type``tidb` 时表示在读取 TiDB 内存表时,是否允许该函数在其他 TiDB Server 上执行
- `store_type``tikv` 时表示是否允许该函数在 TiKV Server 的 Coprocessor 模块中执行
- `store_type``tiflash` 时表示是否允许该函数在 TiFlash Server 的 Coprocessor 模块中执行
+ `reason`用于记录该函数被加入黑名单的原因
+ `name`禁止下推的函数名称
+ `store_type`指定要阻止函数下推到哪个组件进行计算。可用的组件有 `tidb``tikv``tiflash``store_type` 不区分大小写。如果需要指定多个组件,使用逗号分隔每个组件
- `store_type``tidb` 时,表示在读取 TiDB 内存表时该函数是否可以在其他 TiDB 服务器中执行
- `store_type``tikv` 时,表示该函数是否可以在 TiKV 服务器的 Coprocessor 组件中执行
- `store_type``tiflash` 时,表示该函数是否可以在 TiFlash 服务器的 Coprocessor 组件中执行
+ `reason`记录将此函数添加到黑名单的原因

### 使用方法

#### 加入黑名单
本节描述如何使用表达式下推黑名单。

#### 添加到黑名单

如果要将一个或多个函数或运算符加入黑名单,执行以下步骤
要将一个或多个表达式(函数或运算符)添加到黑名单,请执行以下步骤

1. `mysql.expr_pushdown_blacklist` 插入对应的函数名或运算符名以及希望禁止下推的存储引擎集合
1. 将相应的函数名称或运算符名称,以及你想禁用下推的组件集合,插入到 `mysql.expr_pushdown_blacklist` 表中

2. 执行 `admin reload expr_pushdown_blacklist;`
2. 执行 `admin reload expr_pushdown_blacklist`

#### 移出黑名单
### 从黑名单中移除

如果要将一个或多个函数及运算符移出黑名单,执行以下步骤
要从黑名单中移除一个或多个表达式,请执行以下步骤

1. 从 `mysql.expr_pushdown_blacklist` 表中删除对应的函数名或运算符名
1. 从 `mysql.expr_pushdown_blacklist` 表中删除相应的函数名称或运算符名称,以及你想禁用下推的组件集合

2. 执行 `admin reload expr_pushdown_blacklist;`
2. 执行 `admin reload expr_pushdown_blacklist`

> **注意:**
>
> `admin reload expr_pushdown_blacklist` 只对执行该 SQL 语句的 TiDB server 生效。若需要集群中所有 TiDB server 生效,需要在每台 TiDB server 上执行该 SQL 语句
> `admin reload expr_pushdown_blacklist` 只对运行该语句的 TiDB 服务器生效。如果你想让集群中的所有 TiDB 服务器都生效,需要在每个 TiDB 服务器上运行此命令

### 表达式黑名单用法示例
## 表达式黑名单使用示例

以下示例首先将运算符 `<` `>` 加入黑名单,然后将运算符 `>` 从黑名单中移出
在以下示例中,将 `<` `>` 运算符添加到黑名单,然后将 `>` 运算符从黑名单中移除

黑名单是否生效可以从 `explain` 结果中进行观察(参见 [TiDB 执行计划概览](/explain-overview.md))。
要判断黑名单是否生效,请观察 `EXPLAIN` 的结果(参见 [TiDB 执行计划概览](/explain-overview.md))。

1. 对于以下 SQL 语句,`where` 条件中的 `a < 2``a > 2` 可以下推到 TiKV 进行计算
1. 以下 SQL 语句中 `WHERE` 子句中的谓词 `a < 2``a > 2` 可以下推到 TiKV。

{{< copyable "sql" >}}

@@ -154,7 +161,7 @@ DESC mysql.expr_pushdown_blacklist;
3 rows in set (0.00 sec)
```

2. `mysql.expr_pushdown_blacklist` 表中插入禁用表达式,并且执行 `admin reload expr_pushdown_blacklist`
2. 将表达式插入到 `mysql.expr_pushdown_blacklist` 表并执行 `admin reload expr_pushdown_blacklist`

{{< copyable "sql" >}}

@@ -170,14 +177,14 @@ DESC mysql.expr_pushdown_blacklist;
{{< copyable "sql" >}}

```sql
ADMIN reload expr_pushdown_blacklist;
admin reload expr_pushdown_blacklist;
```

```sql
Query OK, 0 rows affected (0.00 sec)
```

3. 重新观察执行计划,发现表达式下推黑名单生效,`where` 条件中的 `<``>` 没有被下推到 TiKV Coprocessor
3. 再次观察执行计划,你会发现 `<``>` 运算符都没有下推到 TiKV Coprocessor。

{{< copyable "sql" >}}

@@ -196,7 +203,7 @@ DESC mysql.expr_pushdown_blacklist;
3 rows in set (0.00 sec)
```

4. 将某一表达式(`>` 大于)禁用规则从黑名单表中删除,并且执行 `admin reload expr_pushdown_blacklist`
4. 从黑名单中移除一个表达式(这里是 `>`)并执行 `admin reload expr_pushdown_blacklist`

{{< copyable "sql" >}}

@@ -211,14 +218,14 @@ DESC mysql.expr_pushdown_blacklist;
{{< copyable "sql" >}}

```sql
ADMIN reload expr_pushdown_blacklist;
admin reload expr_pushdown_blacklist;
```

```sql
Query OK, 0 rows affected (0.00 sec)
```

5. 重新观察执行计划,可以看到只有 `>` 表达式被重新下推到 TiKV Coprocessor`<` 表达式仍然被禁用下推
5. 再次观察执行计划,你会发现 `<` 没有下推,而 `>` 下推到了 TiKV Coprocessor。

{{< copyable "sql" >}}

110 changes: 55 additions & 55 deletions cached-tables.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
---
title: 缓存表
summary: 了解 TiDB 中的缓存表功能,用于很少被修改的热点小表,提升读性能
summary: 了解 TiDB 中的缓存表功能,该功能用于提高很少更新的小型热点表的读取性能
---

# 缓存表

TiDB 在 v6.0.0 版本中引入了缓存表功能。该功能适用于频繁被访问且很少被修改的热点小表,即把整张表的数据加载到 TiDB 服务器的内存中,直接从内存中获取表数据,避免从 TiKV 获取表数据,从而提升读性能
在 v6.0.0 中,TiDB 为经常访问但很少更新的小型热点表引入了缓存表功能。使用此功能时,整个表的数据会加载到 TiDB 服务器的内存中,TiDB 直接从内存获取表数据而无需访问 TiKV,从而提高读取性能

本文介绍了 TiDB 缓存表的使用场景、使用示例、与其他 TiDB 功能的兼容性限制。
本文档描述缓存表的使用场景、示例以及与其他 TiDB 功能的兼容性限制。

## 使用场景

TiDB 缓存表功能适用于以下特点的表
缓存表功能适用于具有以下特征的表

- 表的数据量不大,例如 4 MiB 以下
- 只读表,或者几乎很少修改,例如写入 QPS 低于每分钟 10 次
- 表的访问很频繁,期望有更好的读性能,例如在直接读取 TiKV 时遇到小表热点瓶颈
- 表的数据量小,例如小于 4 MiB
- 表是只读的或很少更新,例如每分钟写入 QPS(每秒查询数)少于 10 次
- 表经常被访问,并且你期望更好的读取性能,例如当从 TiKV 直接读取小表时遇到热点。

当表的数据量不大,访问又特别频繁的情况下,数据会集中在 TiKV 一个 Region 上,形成热点,从而影响性能。因此,TiDB 缓存表的典型使用场景如下:
当表的数据量小但数据经常被访问时,数据会集中在 TiKV 的一个 Region 上并形成热点 Region,这会影响性能。因此,缓存表的典型使用场景如下:

- 配置表,业务通过该表读取配置信息
- 金融场景中的存储汇率的表,该表不会实时更新,每天只更新一次
- 银行分行或者网点信息表,该表很少新增记录项
- 配置表,应用程序从中读取配置信息。
- 金融领域的汇率表。这些表每天只更新一次,而不是实时更新。
- 银行分支机构或网络信息表,很少更新。

以配置表为例,当业务重启的瞬间,全部连接一起加载配置,会造成较高的数据库读延迟。如果使用了缓存表,则可以解决这样的问题
以配置表为例。当应用程序重启时,所有连接都会加载配置信息,这会导致较高的读取延迟。在这种情况下,你可以使用缓存表功能来解决这个问题

## 使用示例
## 示例

本节通过示例介绍缓存表的使用方法
本节通过示例描述缓存表的使用方法

### 将普通表设为缓存表
### 将普通表设置为缓存表

假设已存在普通表 `users`:
假设有一个表 `users`

```sql
CREATE TABLE users (
@@ -41,25 +41,25 @@ CREATE TABLE users (
);
```

通过 `ALTER TABLE` 语句,可以将这张表设置成缓存表
要将此表设置为缓存表,使用 `ALTER TABLE` 语句:

```sql
ALTER TABLE users CACHE;
```

```
```sql
Query OK, 0 rows affected (0.01 sec)
```

### 验证是否为缓存表
### 验证缓存表

要验证一张表是否为缓存表,使用 `SHOW CREATE TABLE` 语句。如果为缓存表,返回结果中会带有 `CACHED ON` 属性:
要验证缓存表,使用 `SHOW CREATE TABLE` 语句。如果表已缓存,返回结果会包含 `CACHED ON` 属性:

```sql
SHOW CREATE TABLE users;
```

```
```sql
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
@@ -72,13 +72,13 @@ SHOW CREATE TABLE users;
1 row in set (0.00 sec)
```

从缓存表读取数据后,TiDB 会将数据加载到内存中。你可使用 [`TRACE`](/sql-statements/sql-statement-trace.md) 语句查看 TiDB 是否已将数据加载到内存中。当缓存还未加载时,语句的返回结果会出现 `regionRequest.SendReqCtx`,表示 TiDB 从 TiKV 读取了数据
从缓存表读取数据后,TiDB 会将数据加载到内存中。你可以使用 [`TRACE`](/sql-statements/sql-statement-trace.md) 语句检查数据是否已加载到内存中。当缓存未加载时,返回结果包含 `regionRequest.SendReqCtx` 属性,这表示 TiDB 从 TiKV 读取数据

```sql
TRACE SELECT * FROM users;
```

```
```sql
+------------------------------------------------+-----------------+------------+
| operation | startTS | duration |
+------------------------------------------------+-----------------+------------+
@@ -98,9 +98,9 @@ TRACE SELECT * FROM users;
12 rows in set (0.01 sec)
```

而再次执行 [`TRACE`](/sql-statements/sql-statement-trace.md),返回结果中不再有 `regionRequest.SendReqCtx`,表示 TiDB 已经不再从 TiKV 读取数据,而是直接从内存中读取:
再次执行 [`TRACE`](/sql-statements/sql-statement-trace.md) 后,返回结果不再包含 `regionRequest.SendReqCtx` 属性,这表示 TiDB 不再从 TiKV 读取数据,而是从内存中读取数据。

```
```sql
+----------------------------------------+-----------------+------------+
| operation | startTS | duration |
+----------------------------------------+-----------------+------------+
@@ -115,7 +115,7 @@ TRACE SELECT * FROM users;
7 rows in set (0.00 sec)
```

注意,读取缓存表会使用 `UnionScan` 算子,所以通过 `explain` 查看缓存表的执行计划时,可能会在结果中看到 `UnionScan`
注意,`UnionScan` 运算符用于读取缓存表,所以你可以通过 `explain` 在缓存表的执行计划中看到 `UnionScan`

```sql
+-------------------------+---------+-----------+---------------+--------------------------------+
@@ -128,23 +128,23 @@ TRACE SELECT * FROM users;
3 rows in set (0.00 sec)
```

### 往缓存表写入数据
### 向缓存表写入数据

缓存表支持写入数据。例如, `users` 表中插入一条记录
缓存表支持数据写入。例如,你可以向 `users` 表插入一条记录

```sql
INSERT INTO users(id, name) VALUES(1001, 'Davis');
```

```
```sql
Query OK, 1 row affected (0.00 sec)
```

```sql
SELECT * FROM users;
```

```
```sql
+------+-------+
| id | name |
+------+-------+
@@ -155,15 +155,15 @@ SELECT * FROM users;

> **注意:**
>
> 往缓存表写入数据时,有可能出现秒级别的写入延迟。延迟的时长由全局环境变量 [`tidb_table_cache_lease`](/system-variables.md#tidb_table_cache_lease-从-v600-版本开始引入) 控制。你可根据实际业务能否承受此限制带来的延迟,决定是否适合使用缓存表功能。例如,对于完全只读的场景,可以将 `tidb_table_cache_lease` 调大
> 向缓存表插入数据时,可能会出现秒级写入延迟。延迟由全局环境变量 [`tidb_table_cache_lease`](/system-variables.md#tidb_table_cache_lease-new-in-v600) 控制。你可以根据应用程序检查延迟是否可接受来决定是否使用缓存表功能。例如,在只读场景中,你可以增加 `tidb_table_cache_lease` 的值
>
> ```sql
> set @@global.tidb_table_cache_lease = 10;
> ```
>
> 缓存表的写入延时高是受到实现的限制。存在多个 TiDB 实例时,一个 TiDB 实例并不知道其它的 TiDB 实例是否缓存了数据,如果该实例直接修改了表数据,而其它 TiDB 实例依然读取旧的缓存数据,就会读到错误的结果。为了保证数据正确性,缓存表的实现使用了一套基于 lease 的复杂机制:读操作在缓存数据同时,还会对于缓存设置一个有效期,也就是 lease。在 lease 过期之前,无法对数据执行修改操作。因为修改操作必须等待 lease 过期,所以会出现写入延迟
> 缓存表的写入延迟较高,因为缓存表功能的实现使用了复杂的机制,需要为每个缓存设置租约。当有多个 TiDB 实例时,一个实例不知道其他实例是否已缓存数据。如果一个实例直接修改表数据,其他实例会读取旧的缓存数据。为确保正确性,缓存表实现使用租约机制确保在租约到期前数据不会被修改。这就是写入延迟较高的原因
缓存表相关的元信息存储在 `mysql.table_cache_meta` 表中。这张表记录了所有缓存表的 ID、当前的锁状态 `lock_type`,以及锁租约 `lease` 相关的信息。这张表仅供 TiDB 内部使用,不建议用户修改该表,否则可能导致不可预期的错误
缓存表的元数据存储在 `mysql.table_cache_meta` 表中。此表记录了所有缓存表的 ID、当前锁定状态(`lock_type`)和锁定租约信息(`lease`)。此表仅供 TiDB 内部使用,不建议修改它。否则,可能会发生意外错误
```sql
SHOW CREATE TABLE mysql.table_cache_meta\G
@@ -183,62 +183,62 @@ Create Table: CREATE TABLE `table_cache_meta` (

> **注意:**
>
> 对缓存表执行 DDL 语句会失败。若要对缓存表执行 DDL 语句,需要先去掉缓存属性,将缓存表设回普通表后,才能对其执行 DDL 语句
> 在缓存表上执行 DDL 语句将会失败。在对缓存表执行 DDL 语句之前,你需要先移除缓存属性,将缓存表恢复为普通表
```sql
TRUNCATE TABLE users;
```

```
```sql
ERROR 8242 (HY000): 'Truncate Table' is unsupported on cache tables.
```

```sql
mysql> ALTER TABLE users ADD INDEX k_id(id);
```

```
```sql
ERROR 8242 (HY000): 'Alter Table' is unsupported on cache tables.
```

使用 `ALTER TABLE t NOCACHE` 语句可以将缓存表恢复成普通表
要将缓存表恢复为普通表,使用 `ALTER TABLE t NOCACHE`

```sql
ALTER TABLE users NOCACHE
ALTER TABLE users NOCACHE;
```

```
```sql
Query OK, 0 rows affected (0.00 sec)
```

## 缓存表大小限制
## 缓存表的大小限制

由于 TiDB 将整张缓存表的数据加载到 TiDB 进程的内存中,并且执行修改操作后缓存会失效,需要重新加载,所以 TiDB 缓存表只适用于表比较小的场景
缓存表仅适用于小表场景,因为 TiDB 会将整个表的数据加载到内存中,并且缓存的数据在修改后会失效并需要重新加载

目前 TiDB 对于每张缓存表的大小限制为 64 MB。如果表的数据超过了 64 MB,执行 `ALTER TABLE t CACHE` 会失败
目前TiDB 中缓存表的大小限制为 64 MB。如果表数据超过 64 MB,执行 `ALTER TABLE t CACHE` 将会失败

## 与其他 TiDB 功能的兼容性限制

以下是缓存表不支持的功能
缓存表**不支持**以下功能

- 不支持对分区表执行 `ALTER TABLE t CACHE` 操作
- 不支持对临时表执行 `ALTER TABLE t CACHE` 操作
- 不支持对视图执行 `ALTER TABLE t CACHE` 操作
- 不支持 Stale Read 功能
- 不支持对缓存表直接做 DDL 操作,需要先通过 `ALTER TABLE t NOCACHE` 将缓存表改回普通表后再进行 DDL 操作
- 不支持对分区表执行 `ALTER TABLE t ADD PARTITION` 操作
- 不支持对临时表执行 `ALTER TABLE t CACHE` 操作
- 不支持对视图执行 `ALTER TABLE t CACHE` 操作
- 不支持 Stale Read
- 不支持直接对缓存表执行 DDL 操作。在执行 DDL 操作之前,你需要先使用 `ALTER TABLE t NOCACHE` 将缓存表恢复为普通表

以下是缓存表无法使用缓存的场景
缓存表**不能**在以下场景中使用

- 设置系统变量 `tidb_snapshot` 读取历史数据
- 执行修改操作期间,已有缓存会失效,直到数据被再次加载
- 设置系统变量 `tidb_snapshot` 来读取历史数据。
- 在修改期间,缓存的数据会失效,直到数据重新加载。

## TiDB 数据迁移工具兼容性
## TiDB 迁移工具的兼容性

缓存表并不是标准的 MySQL 功能,而是 TiDB 扩展。只有 TiDB 能识别 `ALTER TABLE ... CACHE` 语句。所有的 TiDB 数据迁移工具均不支持缓存表功能,包括 Backup & Restore (BR)、TiCDC、Dumpling 等组件,它们会将缓存表当作普通表处理
缓存表是 TiDB 对 MySQL 语法的扩展。只有 TiDB 能识别 `ALTER TABLE ... CACHE` 语句。TiDB 迁移工具**不支持**缓存表,包括 Backup & Restore (BR)、TiCDC 和 Dumpling。这些工具将缓存表视为普通表

这意味着,备份恢复一张缓存表时,它会变成一张普通表。如果下游集群是另一套 TiDB 集群并且你希望继续使用缓存表功能,可以对下游集群中的表执行 `ALTER TABLE ... CACHE` 手动开启缓存表功能
也就是说,当缓存表被备份和恢复时,它会变成普通表。如果下游集群是不同的 TiDB 集群,并且你想继续使用缓存表功能,你可以在下游集群上通过对下游表执行 `ALTER TABLE ... CACHE` 手动启用缓存表

## 另请参阅

* [ALTER TABLE](/sql-statements/sql-statement-alter-table.md)
* [System Variables](/system-variables.md)
* [系统变量](/system-variables.md)
222 changes: 115 additions & 107 deletions character-set-and-collation.md

Large diffs are not rendered by default.

62 changes: 36 additions & 26 deletions character-set-gbk.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
---
title: GBK
summary: 本文介绍 TiDB 对 GBK 字符集的支持情况
summary: 本文档提供了 TiDB 对 GBK 字符集支持的详细信息
---

# GBK

TiDB 从 v5.4.0 开始支持 GBK 字符集。本文档介绍 TiDB 对 GBK 字符集的支持和兼容情况
从 v5.4.0 版本开始,TiDB 支持 GBK 字符集。本文档提供了 TiDB 对 GBK 字符集的支持和兼容性信息

TiDB v6.0.0 开始,[新的排序规则框架](/character-set-and-collation.md#新框架下的排序规则支持)默认启用,即 TiDB GBK 字符集的默认排序规则为 `gbk_chinese_ci` MySQL 保持一致。
从 v6.0.0 版本开始,TiDB 默认启用[新的排序规则框架](/character-set-and-collation.md#new-framework-for-collations)TiDB GBK 字符集的默认排序规则是 `gbk_chinese_ci`这与 MySQL 保持一致。

```sql
SHOW CHARACTER SET WHERE CHARSET = 'gbk';
@@ -28,41 +28,51 @@ SHOW COLLATION WHERE CHARSET = 'gbk';
2 rows in set (0.00 sec)
```

## MySQL 的兼容性
## MySQL 兼容性

本节介绍 TiDB 中 GBK 字符集与 MySQL 的兼容情况
本节提供 MySQL 和 TiDB 之间的兼容性信息

### 排序规则兼容性
### 排序规则

MySQL 的 GBK 字符集默认排序规则是 `gbk_chinese_ci`。TiDB 的 GBK 字符集的默认排序规则取决于 TiDB 配置项 [`new_collations_enabled_on_first_bootstrap`](/tidb-configuration-file.md#new_collations_enabled_on_first_bootstrap) 的值:
<CustomContent platform="tidb">

- 默认情况下,TiDB 配置项 [`new_collations_enabled_on_first_bootstrap`](/tidb-configuration-file.md#new_collations_enabled_on_first_bootstrap)`true`,表示开启[新的排序规则框架](/character-set-and-collation.md#新框架下的排序规则支持)。GBK 字符集的默认排序规则是 `gbk_chinese_ci`
- 当 TiDB 配置项 [`new_collations_enabled_on_first_bootstrap`](/tidb-configuration-file.md#new_collations_enabled_on_first_bootstrap)`false` 时,表示关闭新的排序规则框架,GBK 字符集的默认排序规则是 `gbk_bin`
MySQL 中 GBK 字符集的默认排序规则是 `gbk_chinese_ci`。TiDB 中 GBK 字符集的默认排序规则取决于 TiDB 配置项 [`new_collations_enabled_on_first_bootstrap`](/tidb-configuration-file.md#new_collations_enabled_on_first_bootstrap) 的值:

另外,TiDB 支持的 `gbk_bin` 与 MySQL 支持的 `gbk_bin` 排序规则不一致,TiDB 是将 GBK 转换成 `utf8mb4`,然后再进行二进制排序。
- 默认情况下,TiDB 配置项 [`new_collations_enabled_on_first_bootstrap`](/tidb-configuration-file.md#new_collations_enabled_on_first_bootstrap) 设置为 `true`,这意味着启用了[新的排序规则框架](/character-set-and-collation.md#new-framework-for-collations),GBK 字符集的默认排序规则是 `gbk_chinese_ci`
- 当 TiDB 配置项 [`new_collations_enabled_on_first_bootstrap`](/tidb-configuration-file.md#new_collations_enabled_on_first_bootstrap) 设置为 `false` 时,[新的排序规则框架](/character-set-and-collation.md#new-framework-for-collations)被禁用,GBK 字符集的默认排序规则是 `gbk_bin`

</CustomContent>

<CustomContent platform="tidb-cloud">

默认情况下,TiDB Cloud 启用[新的排序规则框架](/character-set-and-collation.md#new-framework-for-collations),GBK 字符集的默认排序规则是 `gbk_chinese_ci`

</CustomContent>

此外,由于 TiDB 将 GBK 转换为 `utf8mb4` 后再使用二进制排序规则,因此 TiDB 中的 `gbk_bin` 排序规则与 MySQL 中的 `gbk_bin` 排序规则不同。

### 非法字符兼容性

* 在系统变量 [`character_set_client`](/system-variables.md#character_set_client)[`character_set_connection`](/system-variables.md#character_set_connection) 不同时设置为 `gbk` 的情况下,TiDB 处理非法字符的方式与 MySQL 一致
* `character_set_client``character_set_connection` 同时为 `gbk` 的情况下,TiDB 处理非法字符的方式与 MySQL 有所区别
* 如果系统变量 [`character_set_client`](/system-variables.md#character_set_client)[`character_set_connection`](/system-variables.md#character_set_connection) 没有同时设置为 `gbk`,TiDB 处理非法字符的方式与 MySQL 相同
* 如果 `character_set_client``character_set_connection` 都设置为 `gbk`,TiDB 处理非法字符的方式与 MySQL 不同

- MySQL 处理非法 GBK 字符集时,对读和写操作的处理方式不同
- TiDB 处理非法 GBK 字符集时,对读和写操作的处理方式相同。TiDB 在严格模式下读写非法 GBK 字符都会报错,在非严格模式下,读写非法 GBK 字符都会用 `?` 替换
- MySQL 对读写操作中的非法 GBK 字符集处理方式不同
- TiDB 对读写操作中的非法 GBK 字符集处理方式相同。在 SQL 严格模式下,TiDB 在读写非法 GBK 字符时都会报错。在非严格模式下,TiDB 在读写非法 GBK 字符时都会将非法 GBK 字符替换为 `?`

例如, `SET NAMES gbk` 时,如果分别在 MySQL 和 TiDB 上通过 `CREATE TABLE gbk_table(a VARCHAR(32) CHARACTER SET gbk)` 语句建表,然后按照下表中的 SQL 语句进行操作,就能看到具体的区别
例如,在执行 `SET NAMES gbk` 后,如果您分别在 MySQL 和 TiDB 中使用 `CREATE TABLE gbk_table(a VARCHAR(32) CHARACTER SET gbk)` 语句创建表,然后执行下表中的 SQL 语句,您可以看到详细的差异

| 数据库 | 如果设置的 SQL 模式包含 `STRICT_ALL_TABLES``STRICT_TRANS_TABLES` | 如果设置的 SQL 模式不包含 `STRICT_ALL_TABLES` `STRICT_TRANS_TABLES` |
| 数据库 | 如果配置的 SQL 模式包含 `STRICT_ALL_TABLES``STRICT_TRANS_TABLES` 中的任一个 | 如果配置的 SQL 模式既不包含 `STRICT_ALL_TABLES` 也不包含 `STRICT_TRANS_TABLES` |
|-------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| MySQL | `SELECT HEX('一a');` <br /> `e4b88061`<br /><br />`INSERT INTO gbk_table values('一a');`<br /> `Incorrect Error` | `SELECT HEX('一a');` <br /> `e4b88061`<br /><br />`INSERT INTO gbk_table VALUES('一a');`<br />`SELECT HEX(a) FROM gbk_table;`<br /> `e4b8` |
| TiDB | `SELECT HEX('一a');` <br /> `Incorrect Error`<br /><br />`INSERT INTO gbk_table VALUES('一a');`<br /> `Incorrect Error` | `SELECT HEX('一a');` <br /> `e4b83f`<br /><br />`INSERT INTO gbk_table VALUES('一a');`<br />`SELECT HEX(a) FROM gbk_table;`<br /> `e4b83f` |

说明:该表中 `SELECT HEX('一a');` `utf8mb4` 字节集下的结果为 `e4b88061`
在上表中,在 `utf8mb4` 字节集中 `SELECT HEX('a');` 的结果是 `e4b88061`

### 其它
### 其他 MySQL 兼容性

* 目前 TiDB 不支持通过 `ALTER TABLE` 语句将其它字符集类型改成 `gbk` 或者从 `gbk` 转成其它字符集类型
- 目前TiDB 不支持使用 `ALTER TABLE` 语句将其他字符集类型转换为 `gbk` 或从 `gbk` 转换为其他字符集类型

* TiDB 不支持使用 `_gbk`,比如
* TiDB 不支持使用 `_gbk`。例如

```sql
CREATE TABLE t(a CHAR(10) CHARSET BINARY);
@@ -71,16 +81,16 @@ MySQL 的 GBK 字符集默认排序规则是 `gbk_chinese_ci`。TiDB 的 GBK 字
ERROR 1115 (42000): Unsupported character introducer: 'gbk'
```

* 对于 `ENUM``SET` 类型中的二进制字符,TiDB 目前都会将其作为 `utf8mb4` 字符集处理。
- 目前,对于 `ENUM``SET` 类型的二进制字符,TiDB 将其作为 `utf8mb4` 字符集处理。

## 组件兼容性

* TiFlash 目前不支持 GBK 字符集。
- 目前,TiFlash 不支持 GBK 字符集。

* TiDB Data Migration (DM) 在 v5.4.0 之前不支持将 `charset=GBK` 的表迁移到 TiDB。
- TiDB Data Migration (DM) 不支持将 `charset=GBK` 的表迁移到 v5.4.0 之前的 TiDB 集群

* TiDB Lightning v5.4.0 之前不支持导入 `charset=GBK` 的表
- TiDB Lightning 不支持将 `charset=GBK` 的表导入到 v5.4.0 之前的 TiDB 集群

* TiCDC 在 v6.1.0 之前不支持同步 `charset=GBK` 的表。另外,任何版本的 TiCDC 都不支持同步 `charset=GBK` 的表到 6.1.0 之前的 TiDB 集群。
- v6.1.0 之前的 TiCDC 版本不支持复制 `charset=GBK` 的表。所有版本的 TiCDC 都不支持将 `charset=GBK` 的表复制到 v6.1.0 之前的 TiDB 集群。

* TiDB Backup & Restore(BR)在 v5.4.0 之前不支持恢复 `charset=GBK` 的表。另外,任何版本的 BR 都不支持恢复 `charset=GBK` 的表到 5.4.0 之前的 TiDB 集群。
- v5.4.0 之前的 Backup & Restore (BR) 版本不支持恢复 `charset=GBK` 的表。所有版本的 BR 都不支持将 `charset=GBK` 的表恢复到 v5.4.0 之前的 TiDB 集群。
206 changes: 102 additions & 104 deletions choose-index.md

Large diffs are not rendered by default.

142 changes: 70 additions & 72 deletions clustered-indexes.md

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions column-pruning.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
---
title: 列裁剪
summary: 列裁剪是优化器在优化过程中删除不需要的列的基本思想。这样可以减少 I/O 资源占用并为后续优化带来便利。TiDB 会在逻辑优化阶段进行列裁剪,减少资源浪费。该扫描过程称作“列裁剪”,对应逻辑优化规则中的 columnPruner。如果要关闭这个规则,可以参照优化规则及表达式下推的黑名单中的关闭方法
title: 列剪裁
summary: 了解 TiDB 中列剪裁的使用
---

# 列裁剪
# 列剪裁

列裁剪的基本思想在于:对于算子中实际用不上的列,优化器在优化的过程中没有必要保留它们。对这些列的删除会减少 I/O 资源占用,并为后续的优化带来便利。下面给出一个列重复的例子
列剪裁的基本思想是,对于算子中未使用的列,优化器在优化过程中不需要保留它们。移除这些列可以减少 I/O 资源的使用,并有助于后续的优化。以下是列重复的示例

假设表 t 里面有 a b c d 四列,执行如下语句
假设表 t 中有四列(a、b、c 和 d)。你可以执行以下语句

{{< copyable "sql" >}}

```sql
select a from t where b > 5
select a from t where b> 5
```

在该查询的过程中,t 表实际上只有 a, b 两列会被用到,而 c, d 的数据则显得多余。对应到该语句的查询计划,Selection 算子会用到 b 列,下面接着的 DataSource 算子会用到 a, b 两列,而剩下 c, d 两列则都可以裁剪掉,DataSource 算子在读数据时不需要将它们读进来
在这个查询中,只使用了列 a 和列 b,而列 c 和列 d 是冗余的。关于这个语句的查询计划,`Selection` 算子使用列 b,然后 `DataSource` 算子使用列 a 和列 b。由于 `DataSource` 算子不读取列 c 和列 d,所以可以对它们进行剪裁

出于上述考量,TiDB 会在逻辑优化阶段进行自上而下的扫描,裁剪不需要的列,减少资源浪费。该扫描过程称作 “列裁剪”,对应逻辑优化规则中的 `columnPruner`。如果要关闭这个规则,可以在参照[优化规则及表达式下推的黑名单](/blocklist-control-plan.md)中的关闭方法
因此,当 TiDB 在逻辑优化阶段执行自上而下的扫描时,会剪裁冗余列以减少资源浪费。这个扫描过程称为"列剪裁",对应于 `columnPruner` 规则。如果你想禁用这个规则,请参考[优化规则和表达式下推的黑名单](/blocklist-control-plan.md)
66 changes: 36 additions & 30 deletions comment-syntax.md
Original file line number Diff line number Diff line change
@@ -5,16 +5,16 @@ summary: 本文介绍 TiDB 支持的注释语法。

# 注释语法

本文档介绍 TiDB 支持的注释语法。
本文描述 TiDB 支持的注释语法。

TiDB 支持三种注释风格:

* `#` 注释一行:
- 使用 `#` 注释一行:

{{< copyable "sql" >}}

```sql
SELECT 1+1; # 注释文字
SELECT 1+1; # comments
```

```
@@ -26,12 +26,12 @@ TiDB 支持三种注释风格:
1 row in set (0.00 sec)
```

* `--` 注释一行:
- 使用 `--` 注释一行:

{{< copyable "sql" >}}

```sql
SELECT 1+1; -- 注释文字
SELECT 1+1; -- comments
```

```
@@ -42,10 +42,10 @@ TiDB 支持三种注释风格:
+------+
1 row in set (0.00 sec)
```

这种风格要求在 `--` 后至少有一个空格:

`--` 注释时,必须要在其之后留出至少一个空格,否则注释不生效:

{{< copyable "sql" >}}
{{< copyable "sql" >}}

```sql
SELECT 1+1--1;
@@ -60,12 +60,12 @@ TiDB 支持三种注释风格:
1 row in set (0.01 sec)
```

* `/* */` 注释一块,可以注释多行
- 使用 `/* */` 注释一个块或多行

{{< copyable "sql" >}}
{{< copyable "sql" >}}

```sql
SELECT 1 /* 这是行内注释文字 */ + 1;
SELECT 1 /* this is an in-line comment */ + 1;
```

```
@@ -82,16 +82,15 @@ TiDB 支持三种注释风格:
```sql
SELECT 1+
/*
/*> 这是一条
/*> 多行注释
/*> this is a
/*> multiple-line comment
/*> */
1;
```

```
+-------------------+
| 1+

1 |
+-------------------+
| 2 |
@@ -101,48 +100,55 @@ TiDB 支持三种注释风格:

## MySQL 兼容的注释语法

TiDB 也跟 MySQL 保持一致,支持一种 C 风格注释的变体
MySQL 一样,TiDB 支持 C 风格注释语法的变体

```
/*! Specific code */
/*! 特定代码 */
```
或者
```
/*!50110 Specific code */
/*!50110 特定代码 */
```
和 MySQL 一样,TiDB 会执行注释中的语句。
在这种风格中,TiDB 会执行注释中的语句。
例如:`SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHERE ...`
例如:
在 TiDB 中,这种写法等价于 `SELECT STRAIGHT_JOIN col1 FROM table1,table2 WHERE ...`
```sql
SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHERE ...
```

在 TiDB 中,你也可以使用另一个版本:

如果注释中指定了 Server 版本号,例如 `/*!50110 KEY_BLOCK_SIZE=1024 */`,在 MySQL 中表示只有 MySQL 的版本大于等于 5.1.10 才会处理这个 comment 中的内容。但是在 TiDB 中,这个 MySQL 版本号不会起作用,所有的 comment 都被会处理。
```sql
SELECT STRAIGHT_JOIN col1 FROM table1,table2 WHERE ...
```

## TiDB 可执行的注释语法
如果在注释中指定了服务器版本号,例如 `/*!50110 KEY_BLOCK_SIZE=1024 */`,在 MySQL 中这意味着只有当 MySQL 版本是或高于 5.1.10 时才处理此注释中的内容。但在 TiDB 中,MySQL 版本号不起作用,注释中的所有内容都会被处理。

TiDB 也有独立的注释语法,称为 TiDB 可执行注释语法。主要分为两种:
## TiDB 特有的注释语法

* `/*T! Specific code */`:该语法只能被 TiDB 解析执行,而在其他数据库中会被忽略。
TiDB 有自己的注释语法(即 TiDB 特有的注释语法),可以分为以下两种:

* `/*T![feature_id] Specific code */`:该语法用于保证 TiDB 不同版本之间的兼容性。只有在当前版本中实现了 `feature_id` 对应的功能特性的 TiDB,才会试图解析该注释里的 SQL 片段。例如 v3.1.1 中引入了 `AUTO_RANDOM` 特性,该版本能够将 `/*T![auto_rand] auto_random */` 解析为 `auto_random`;而 v3.0.0 中没有实现 `AUTO_RANDOM` 特性,则上述 SQL 语句片段会被忽略。**注意前几个字符 `/*T![` 中,各字符之间没有任何空格**。
* `/*T! 特定代码 */`:这种语法只能被 TiDB 解析和执行,在其他数据库中会被忽略。
* `/*T![feature_id] 特定代码 */`:这种语法用于确保不同版本的 TiDB 之间的兼容性。TiDB 只有在当前版本实现了相应的 `feature_id` 功能时才能解析这个注释中的 SQL 片段。例如,由于 `AUTO_RANDOM` 功能是在 v3.1.1 中引入的,因此这个版本的 TiDB 可以将 `/*T![auto_rand] auto_random */` 解析为 `auto_random`。因为 v3.0.0 中没有实现 `AUTO_RANDOM` 功能,所以上述 SQL 语句片段会被忽略。**不要在 `/*T![` 字符内留有任何空格**

## 优化器注释语法

还有一种注释会被当做是优化器 Hint 特殊对待
另一种注释类型被特别处理为优化器提示

{{< copyable "sql" >}}

```sql
SELECT /*+ hint */ FROM ...;
```

TiDB 支持的相关优化器 hint 详见 [Optimizer Hints](/optimizer-hints.md)
关于 TiDB 支持的优化器提示的详细信息,请参见[优化器提示](/optimizer-hints.md)

> **注意:**
>
> 在 MySQL 客户端中,TiDB 可执行注释语法会被默认当成注释被清除掉。在 MySQL 客户端 5.7.7 之前的版本中,Hint 也会被默认当成注释被清除掉。推荐在启动客户端时加上 `--comments` 选项,例如 `mysql -h 127.0.0.1 -P 4000 -uroot --comments`
> 在 MySQL 客户端中,TiDB 特有的注释语法默认被视为注释并被清除。在 MySQL 5.7.7 之前的客户端中,提示也被视为注释并默认被清除。建议在启动客户端时使用 `--comments` 选项。例如,`mysql -h 127.0.0.1 -P 4000 -uroot --comments`
更多细节,请参考 [MySQL 文档](https://dev.mysql.com/doc/refman/8.0/en/comments.html)
更多信息,请参见 [Comment Syntax](https://dev.mysql.com/doc/refman/8.0/en/comments.html)
Loading