Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@
- 属性
- [AUTO_INCREMENT](/auto-increment.md)
- [AUTO_RANDOM](/auto-random.md)
- [_tidb_rowid](/tidb-rowid.md)
- [SHARD_ROW_ID_BITS](/shard-row-id-bits.md)
- [字面值](/literal-values.md)
- [Schema 对象名](/schema-object-names.md)
Expand Down
2 changes: 1 addition & 1 deletion clustered-indexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ summary: 本文档介绍了聚簇索引的概念、使用场景、使用方法

目前 TiDB 中含有主键的表分为以下两类:

- `NONCLUSTERED`,表示该表的主键为非聚簇索引。在非聚簇索引表中,行数据的键由 TiDB 内部隐式分配的 `_tidb_rowid` 构成,而主键本质上是唯一索引,因此非聚簇索引表存储一行至少需要两个键值对,分别为
- `NONCLUSTERED`,表示该表的主键为非聚簇索引。在非聚簇索引表中,行数据的键由 TiDB 内部隐式分配的 [`_tidb_rowid`](/tidb-rowid.md) 值构成,而主键本质上是唯一索引,因此非聚簇索引表存储一行至少需要两个键值对,分别为
- `_tidb_rowid`(键)- 行数据(值)
- 主键列数据(键) - `_tidb_rowid`(值)
- `CLUSTERED`,表示该表的主键为聚簇索引。在聚簇索引表中,行数据的键由用户给定的主键列数据构成,因此聚簇索引表存储一行至少只要一个键值对,即
Expand Down
10 changes: 7 additions & 3 deletions shard-row-id-bits.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ summary: 介绍 TiDB 的 `SHARD_ROW_ID_BITS` 表属性。

# SHARD_ROW_ID_BITS

本文介绍表属性 `SHARD_ROW_ID_BITS`,它用来设置隐式 `_tidb_rowid` 分片数量的 bit 位数。
本文介绍表属性 `SHARD_ROW_ID_BITS`,它用来设置隐式 [`_tidb_rowid`](/tidb-rowid.md) 分片数量的 bit 位数。

## 基本概念

对于非[聚簇索引](/clustered-indexes.md)主键或没有主键的表,TiDB 会使用一个隐式的自增 rowid。大量执行 `INSERT` 插入语句时会把数据集中写入单个 Region,造成写入热点。
对于非聚簇索引主键或没有主键的表,TiDB 会使用内部自动生成的 [`_tidb_rowid`](/tidb-rowid.md) 作为隐式自增 rowid。大量执行 `INSERT` 插入语句时会把数据集中写入单个 Region,造成写入热点。

通过设置 `SHARD_ROW_ID_BITS`,可以把 rowid 打散写入多个不同的 Region,缓解写入热点问题。

Expand All @@ -23,9 +23,13 @@ summary: 介绍 TiDB 的 `SHARD_ROW_ID_BITS` 表属性。
|--------|--------|--------------|
| 1 bit | `S` bits | `63-S` bits |

- 自增位的值保存在 TiKV 中,由 TiDB 按顺序分配,每次分配后值会自增 1。自增位确保了 `_tidb_rowid` 列的值全局唯一。当自增位的值耗尽后(即达到最大值时),再次自动分配时会报 `Failed to read auto-increment value from storage engine` 错误。
- 自增位的值保存在 TiKV 中,由 TiDB 按顺序分配,每次分配后值会自增 1。当自增位的值耗尽后(即达到最大值时),再次自动分配时会报 `Failed to read auto-increment value from storage engine` 错误。
- 关于 `_tidb_rowid` 取值范围:最终生成值包含的最大位数 = 分片位 + 自增位,最大值为 `(2^63)-1`。

> **警告:**
>
> `_tidb_rowid` 是 TiDB 内部隐式分配的行 ID。不要假定它在所有情况下都是全局唯一的。对于未使用聚簇索引的分区表,`ALTER TABLE ... EXCHANGE PARTITION` 操作可能会导致不同分区具有相同的 `_tidb_rowid` 值。详情请参阅 [`_tidb_rowid`](/tidb-rowid.md)。

> **注意:**
>
> 分片位长度 (`S`) 的选取:
Expand Down
5 changes: 5 additions & 0 deletions sql-statements/sql-statement-show-table-next-rowid.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ summary: TiDB 数据库中 SHOW TABLE NEXT_ROW_ID 的使用概况。

`SHOW TABLE NEXT_ROW_ID` 语句用于显示用户表中某些特殊列的详情,主要包含以下几种类型:

<<<<<<< HEAD
* TiDB 创建的 `AUTO_INCREMENT` 类型列,即 `_tidb_rowid` 列
=======
* TiDB 自动管理的隐藏行 ID 列 [`_tidb_rowid`](/tidb-rowid.md)
>>>>>>> 1a4892c273 (add `_tidb_rowid` document (#21446))
* 用户创建的 `AUTO_INCREMENT` 类型列
* 用户创建的 [`AUTO_RANDOM`](/auto-random.md) 类型列
* 用户创建的 [`SEQUENCE`](/sql-statements/sql-statement-create-sequence.md) 对象信息
Expand Down Expand Up @@ -70,3 +74,4 @@ show table t next_row_id;
* [CREATE TABLE](/sql-statements/sql-statement-create-table.md)
* [AUTO_RANDOM](/auto-random.md)
* [CREATE_SEQUENCE](/sql-statements/sql-statement-create-sequence.md)
* [_tidb_rowid](/tidb-rowid.md)
157 changes: 157 additions & 0 deletions tidb-rowid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
title: _tidb_rowid
summary: 了解 `_tidb_rowid` 是什么,何时可用,以及如何安全地使用它。
---

# _tidb_rowid

`_tidb_rowid` 是 TiDB 自动生成的一个隐藏系统列,在没有使用聚簇索引的表中作为表的内部行 ID。你无法在表结构中定义或修改此列,但当表使用 `_tidb_rowid` 作为其内部行 ID 时,可以在 SQL 语句中引用它。

在当前实现中,`_tidb_rowid` 是一个由 TiDB 自动维护的 `BIGINT NOT NULL` 列。

> **警告:**
>
> - 不要假设 `_tidb_rowid` 在所有情况下都是全局唯一的。对于不使用聚簇索引的分区表,执行 `ALTER TABLE ... EXCHANGE PARTITION` 后,不同分区之间可能出现相同的 `_tidb_rowid`。
> - 如果你需要一个稳定的唯一标识符,请定义并使用显式主键,而不是依赖 `_tidb_rowid`。

## `_tidb_rowid` 何时可用

当表没有使用聚簇主键作为行的唯一标识时,TiDB 会使用 `_tidb_rowid` 来标识每一行。实际上,这意味着以下表类型会使用 `_tidb_rowid`:

- 没有主键的表
- 主键显式定义为 `NONCLUSTERED` 的表

`_tidb_rowid` 不适用于使用聚簇索引的表,即主键定义为 `CLUSTERED` 的表(无论是单列主键还是复合主键)。

以下示例显示了区别:

```sql
CREATE TABLE t1 (a INT, b VARCHAR(20));
CREATE TABLE t2 (id BIGINT PRIMARY KEY NONCLUSTERED, a INT);
CREATE TABLE t3 (id BIGINT PRIMARY KEY CLUSTERED, a INT);
```

对于 `t1` 和 `t2`,你可以查询 `_tidb_rowid`,因为这两个表没有使用聚簇索引作为行标识:

```sql
SELECT _tidb_rowid, a, b FROM t1;
SELECT _tidb_rowid, id, a FROM t2;
```

对于 `t3`,`_tidb_rowid` 不可用,因为该表使用了聚簇索引作为行标识,:

```sql
SELECT _tidb_rowid, id, a FROM t3;
```

```sql
ERROR 1054 (42S22): Unknown column '_tidb_rowid' in 'field list'
```

## 读取 `_tidb_rowid`

对于使用了 `_tidb_rowid` 的表,你可以在 `SELECT` 语句中查询 `_tidb_rowid`。这对于分页查询、故障排除和批量处理等任务非常有用。

示例:

```sql
CREATE TABLE t (a INT, b VARCHAR(20));
INSERT INTO t VALUES (1, 'x'), (2, 'y');

SELECT _tidb_rowid, a, b FROM t ORDER BY _tidb_rowid;
```

```sql
+-------------+---+---+
| _tidb_rowid | a | b |
+-------------+---+---+
| 1 | 1 | x |
| 2 | 2 | y |
+-------------+---+---+
```

要查看 TiDB 将要分配的下一个行 ID 值,请使用 `SHOW TABLE ... NEXT_ROW_ID`:

```sql
SHOW TABLE t NEXT_ROW_ID;
```

```sql
+-----------------------+------------+-------------+--------------------+-------------+
| DB_NAME | TABLE_NAME | COLUMN_NAME | NEXT_GLOBAL_ROW_ID | ID_TYPE |
+-----------------------+------------+-------------+--------------------+-------------+
| update_doc_rowid_test | t | _tidb_rowid | 30001 | _TIDB_ROWID |
+-----------------------+------------+-------------+--------------------+-------------+
```

## 写入 `_tidb_rowid`

默认情况下,TiDB 不允许通过 `INSERT`、`REPLACE` 或 `UPDATE` 语句中直接写入 `_tidb_rowid`。

```sql
INSERT INTO t(_tidb_rowid, a, b) VALUES (101, 4, 'w');
```

```sql
ERROR 1105 (HY000): insert, update and replace statements for _tidb_rowid are not supported
```

在数据导入或迁移场景中,如需保留原始行 ID,请先启用系统变量 [`tidb_opt_write_row_id`](/system-variables.md#tidb_opt_write_row_id):

```sql
SET @@tidb_opt_write_row_id = ON;
INSERT INTO t(_tidb_rowid, a, b) VALUES (100, 3, 'z');
SET @@tidb_opt_write_row_id = OFF;

SELECT _tidb_rowid, a, b FROM t WHERE _tidb_rowid = 100;
```

```sql
+-------------+---+---+
| _tidb_rowid | a | b |
+-------------+---+---+
| 100 | 3 | z |
+-------------+---+---+
```

> **警告:**
>
> `tidb_opt_write_row_id` 仅用于导入和迁移场景。不推荐用于常规应用程序写入。

## 限制

- 不能创建名为 `_tidb_rowid` 的用户列。
- 不能将现有用户列重命名为 `_tidb_rowid`。
- `_tidb_rowid` 是 TiDB 内部列,不适合作为业务主键或长期标识。
- 在分区的非聚簇表上,`_tidb_rowid` 的值不保证在分区之间是唯一的。执行 `EXCHANGE PARTITION` 后,不同分区可能包含具有相同 `_tidb_rowid` 值的行。
- `_tidb_rowid` 是否存在取决于表结构。对于使用聚簇索引的表,应使用主键作为行标识。

## 解决热点问题

对于使用 `_tidb_rowid` 的表,TiDB 默认按递增顺序分配行 ID。在写密集型工作负载下,这可能会导致写热点。

要缓解此问题(针对依赖 `_tidb_rowid` 作为行 ID 的表),请考虑使用 [`SHARD_ROW_ID_BITS`](/shard-row-id-bits.md)将行 ID 打散分布,并在需要时使用 [`PRE_SPLIT_REGIONS`](/sql-statements/sql-statement-split-region.md#pre_split_regions) 提前分裂 Region。

示例:

```sql
CREATE TABLE t (
id BIGINT PRIMARY KEY NONCLUSTERED,
c INT
) SHARD_ROW_ID_BITS = 4;
```

`SHARD_ROW_ID_BITS` 仅适用于使用 `_tidb_rowid` 的表,不适用于聚簇索引表。

## 相关语句和变量

- [`SHOW TABLE NEXT_ROW_ID`](/sql-statements/sql-statement-show-table-next-rowid.md):显示 TiDB 将要分配的下一个行 ID
- [`SHARD_ROW_ID_BITS`](/shard-row-id-bits.md):分片隐式行 ID 以减少热点
- [`Clustered Indexes`](/clustered-indexes.md):解释了何时表使用主键而不是 `_tidb_rowid`
- [`tidb_opt_write_row_id`](/system-variables.md#tidb_opt_write_row_id):控制是否允许写入 `_tidb_rowid`

## 另请参阅

- [`CREATE TABLE`](/sql-statements/sql-statement-create-table.md)
- [`AUTO_INCREMENT`](/auto-increment.md)
- [非事务 DML 语句](/non-transactional-dml.md)
Loading