Skip to content

Commit

Permalink
reference: add user documentation for auto_random feature (pingcap#2209)
Browse files Browse the repository at this point in the history
  • Loading branch information
tangenta authored Feb 24, 2020
1 parent e5176c4 commit 5059c83
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 4 deletions.
2 changes: 2 additions & 0 deletions TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
- [用户自定义变量](/reference/sql/language-structure/user-defined-variables.md)
- [表达式语法](/reference/sql/language-structure/expression-syntax.md)
- [注释语法](/reference/sql/language-structure/comment-syntax.md)
+ 表属性和列属性
- [`AUTO_RANDOM`](/reference/sql/attributes/auto-random.md)
+ 数据类型
- [概述](/reference/sql/data-types/overview.md)
- [默认值](/reference/sql/data-types/default-values.md)
Expand Down
12 changes: 10 additions & 2 deletions reference/best-practices/high-concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ SPLIT TABLE table_name [INDEX index_name] BY (value_list) [, (value_list)]

![Table Region Range](/media/best-practices/table-Region-range.png)

从上图可知,根据行数据 key 的编码规则,行 ID (rowID) 是行数据中唯一可变的部分。在 TiDB 中,rowID 是一个 Int64 整形。但是用户不一定能将 Int64 整形范围均匀切分成需要的份数,然后均匀分布在不同的节点上,还需要结合实际情况。
从上图可知,根据行数据 key 的编码规则,行 ID (rowID) 是行数据中唯一可变的部分。在 TiDB 中,rowID 是一个 Int64 整型。但是用户不一定能将 Int64 整型范围均匀切分成需要的份数,然后均匀分布在不同的节点上,还需要结合实际情况。

如果行 ID 的写入是完全离散的,那么上述方式是可行的。如果行 ID 或者索引有固定的范围或者前缀(例如,只在 `[2000w, 5000w)` 的范围内离散插入数据),这种写入依然在业务上不产生热点,但是如果按上面的方式进行切分,那么有可能一开始数据仍只写入到某个 Region 上。

Expand Down Expand Up @@ -170,7 +170,9 @@ SPLIT TABLE TEST_HOTSPOT BETWEEN (0) AND (9223372036854775807) REGIONS 128;

### 更复杂的热点问题

如果表没有主键或者主键不是 Int 类型,而且用户也不想自己生成一个随机分布的主键 ID 的话,TiDB 内部有一个隐式的 `_tidb_rowid` 列作为行 ID。在不使用 `SHARD_ROW_ID_BITS` 的情况下,`_tidb_rowid` 列的值基本也为单调递增,此时也会有写热点存在(参阅 [`SHARD_ROW_ID_BITS` 的详细说明](/reference/configuration/tidb-server/tidb-specific-variables.md#shard_row_id_bits))。
**问题一:**

如果表没有主键或者主键不是整数类型,而且用户也不想自己生成一个随机分布的主键 ID 的话,TiDB 内部有一个隐式的 `_tidb_rowid` 列作为行 ID。在不使用 `SHARD_ROW_ID_BITS` 的情况下,`_tidb_rowid` 列的值基本也为单调递增,此时也会有写热点存在(参阅 [`SHARD_ROW_ID_BITS` 的详细说明](/reference/configuration/tidb-server/tidb-specific-variables.md#shard_row_id_bits))。

要避免由 `_tidb_rowid` 带来的写入热点问题,可以在建表时,使用 `SHARD_ROW_ID_BITS``PRE_SPLIT_REGIONS` 这两个建表选项(参阅 [`PRE_SPLIT_REGIONS` 的详细说明](/reference/sql/statements/split-region.md#pre_split_regions))。

Expand All @@ -193,6 +195,12 @@ create table t (a int, b int) shard_row_id_bits = 4 pre_split_regions=·3;

开始写数据进表 t 后,数据会被写入提前切分好的 8 个 Region 中,这样也避免了刚开始建表完后因为只有一个 Region 而存在的写热点问题。

**问题二:**

如果表的主键为整数类型,并且该表使用了 `AUTO_INCREMENT` 来保证主键唯一性(不需要连续或递增)的表而言,由于 TiDB 直接使用主键行值作为 `_tidb_rowid`,此时无法使用 `SHARD_ROW_ID_BITS` 来打散热点。

要解决上述热点问题,可以利用 `AUTO_RANDOM` 列属性(参阅 [`AUTO_RANDOM` 的详细说明](/reference/sql/attributes/auto-random.md)),将 `AUTO_INCREMENT` 改为 `AUTO_RANDOM`,插入数据时让 TiDB 自动为整型主键列分配一个值,消除行 ID 的连续性,从而达到打散热点的目的。

## 参数配置

TiDB 2.1 版本中在 SQL 层引入了 [latch 机制](/reference/configuration/tidb-server/configuration-file.md#txn-local-latches),用于在写入冲突比较频繁的场景中提前发现事务冲突,减少 TiDB 和 TiKV 事务提交时写写冲突导致的重试。通常,跑批场景使用的是存量数据,所以并不存在事务的写入冲突。可以把 TiDB 的 latch 功能关闭,以减少为细小对象分配内存:
Expand Down
10 changes: 10 additions & 0 deletions reference/configuration/tidb-server/configuration-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,3 +448,13 @@ TiDB 服务状态相关配置。

+ 悲观事务中每个语句最大重试次数,超出该限制将会报错。
+ 默认值:256

## experimental

experimental 部分为 TiDB 实验功能相关的配置。该部分从 v3.1.0 开始引入。

### `allow-auto-random` <span class="version-mark">从 v3.1.0 版本开始引入</span>

+ 用于控制是否允许使用 `AUTO_RANDOM`
+ 默认值:false
+ 默认情况下,不支持使用 `AUTO_RANDOM`。当该值为 true 时,不允许同时设置 alter-primary-key 为 true。
103 changes: 103 additions & 0 deletions reference/sql/attributes/auto-random.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: AUTO_RANDOM
category: reference
---

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

> **警告:**
>
> 当前 `AUTO_RANDOM` 属性为实验功能,**不建议在生产环境中使用**。在后续版本中,`AUTO_RANDOM` 的语法或语义可能会变化。
使用 `AUTO_RANDOM` 功能前,须在 TiDB 配置文件 `experimental` 部分设置 `allow-auto-random = true`。该参数详情可参见 [`allow-auto-random`](/reference/configuration/tidb-server/configuration-file.md#allow-auto-random)

## 使用场景

`AUTO_RANDOM` 用于解决大批量写数据入 TiDB 时因含有**整型自增主键列**的表而产生的热点问题。详情参阅 [TiDB 高并发写入场景最佳实践](/reference/best-practices/high-concurrency.md)

以下面语句建立的表为例:

```sql
create table t (a int primary key auto_increment, b varchar(255))
```

在以上语句所建的表上执行大量未指定主键值的 `INSERT` 语句时,示例如下:

```sql
insert into t(b) values ('a'), ('b'), ('c')
```

在该情况下,由于未指定主键列的值(`a` 列),TiDB 会使用连续自增的行值作为行 ID,可能导致单个 TiKV 节点上产生写入热点,进而影响对外提供服务的性能。要避免这种性能下降,可以在执行建表语句时为 `a` 列指定 `AUTO_RANDOM` 属性而不是 `AUTO_INCREMENT` 属性。示例如下:

{{< copyable "sql" >}}

```sql
create table t (a int primary key auto_random, b varchar(255))
```

或者

{{< copyable "sql" >}}

```sql
create table t (a int auto_random, b varchar(255), primary key (a))
```

此时再执行形如 `INSERT INTO t(b) values...``INSERT` 语句,

+ 如果该 `INSERT` 语句没有指定整型主键列(`a` 列)的值,TiDB 会为该列自动分配值。该值不保证自增,不保证连续,只保证唯一,避免了连续的行 ID 带来的热点问题。
+ 如果该 `INSERT` 语句显式指定了整型主键列的值,和 `AUTO_INCREMENT` 属性类似,TiDB 会原封不动地保存该值。

自动分配值的计算方式如下:

该行值在二进制形式下的最高 5 位(称为 shard bits)由当前事务的开始时间决定,剩下的位数按照自增的顺序分配。

如果希望使用一个不同的 shard bits 的数量,可以在 `AUTO_RANDOM` 后面加一对括号,并在括号中指定,例如:

{{< copyable "sql" >}}

```sql
create table t (a int primary key auto_random(3), b varchar(255))
```

以上建表语句中,shard bits 的数量为 `3`。shard bits 的数量的取值范围是 `[1, field_max_bits)`,其中 `field_max_bits` 为整型主键列类型占用的位长度。

含有 `AUTO_RANDOM` 属性的表在 `information_schema.tables``TIDB_ROW_ID_SHARDING_INFO` 一列的值为 `PK_AUTO_RANDOM_BITS=x`,其中 `x` 为 shard bits 的数量。

## 兼容性

TiDB 支持解析版本注释语法。示例如下:

{{< copyable "sql" >}}

```sql
create table t (a int primary key /*T!30100 auto_random */)
```

{{< copyable "sql" >}}

```sql
create table t (a int primary key auto_random)
```

以上两个语句含义相同。

`show create table` 的结果中,`AUTO_RANDOM` 属性会被注释掉。注释会附带一个版本号,例如 `/*T!30100 auto_random */`。其中 `30100` 表示 v3.1.0 引入该功能,低版本的 TiDB 能够忽略带有上述注释的 `AUTO_RANDOM` 属性。

该功能支持向前兼容,即降级兼容。v3.1.0 以前的 TiDB 会忽略表(带有上述注释)的 `AUTO_RANDOM` 属性,因此能够使用含有该属性的表。

## 使用限制

目前在 TiDB 中使用 `AUTO_RANDOM` 有以下限制:

- 该属性必须指定在整数类型的主键列上,否则会报错。例外情况见[关于 `alter-primary-key` 配置项的说明](#关于-alter-primary-key-配置项的说明)
- 不支持使用 `ALTER TABLE` 来修改 `AUTO_RANDOM` 属性,包括添加或移除该属性。
- 不支持修改含有 `AUTO_RANDOM` 属性的主键列的列类型。
- 不支持与 `AUTO_INCREMENT` 同时指定在同一列上。
- 不支持与列的默认值 `DEFAULT` 同时指定在同一列上。
- 插入数据时,不建议自行显式指定含有 `AUTO_RANDOM` 列的值。不恰当地显式赋值,可能会导致该表提前耗尽用于自动分配的数值。因为用于保证唯一性的 rebase 仅针对除 shard bits 以外的位进行。

### 关于 `alter-primary-key` 配置项的说明

-`alter-primary-key = true` 时,即使是整型主键列,也不支持使用 `AUTO_RANDOM`
- 配置文件中的 `alter-primary-key``allow-auto-random` 两个配置项的值不允许同时为 `true`
2 changes: 1 addition & 1 deletion reference/sql/language-structure/comment-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ TiDB 也跟 MySQL 保持一致,支持一种 C 风格注释的变体:

在 TiDB 中,这种写法等价于 `SELECT STRAIGHT_JOIN col1 FROM table1,table2 WHERE ...`

如果注释中指定了 Server 版本号,例如 `/*!50110 KEY_BLOCK_SIZE=1024 */`,在 MySQL 中表示只有 MySQL 的版本大于等于 5.1.10 才会处理这个 comment 中的内容。但是在 TiDB 中,这个版本号不会起作用,所有的 comment 都会处理
如果注释中指定了 Server 版本号,例如 `/*!50110 KEY_BLOCK_SIZE=1024 */`,在 MySQL 中表示只有 MySQL 的版本大于等于 5.1.10 才会处理这个 comment 中的内容。但是在 TiDB 中,这个 MySQL 版本号不会起作用,所有的 comment 都被会处理。TiDB 有自己的版本号注释语法,格式为 `/*T!30100 XXX */`

还有一种注释会被当做是优化器 Hint 特殊对待:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Query OK, 0 rows affected (0.08 sec)
- ASC (R)
- ASCII
- AUTO_INCREMENT
- AUTO_RANDOM
- AVG
- AVG_ROW_LENGTH

Expand Down
3 changes: 2 additions & 1 deletion reference/sql/statements/create-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ create_definition:
```sql
column_definition:
data_type [NOT NULL | NULL] [DEFAULT default_value]
[AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY]
[AUTO_INCREMENT | AUTO_RANDOM [(length)]]
[UNIQUE [KEY] | [PRIMARY] KEY]
[COMMENT 'string']
[reference_definition]
| data_type [GENERATED ALWAYS] AS (expression)
Expand Down

0 comments on commit 5059c83

Please sign in to comment.