|
| 1 | +--- |
| 2 | +title: _tidb_rowid |
| 3 | +summary: Learn what `_tidb_rowid` is, when it is available, and how to use it safely. |
| 4 | +--- |
| 5 | + |
| 6 | +# `_tidb_rowid` |
| 7 | + |
| 8 | +`_tidb_rowid` is a hidden system column automatically generated by TiDB. For tables that do not use a clustered index, it serves as the internal row ID of the table. You cannot declare or modify this column in the table schema, but you can reference it in SQL when the table uses `_tidb_rowid` as its internal row ID. |
| 9 | + |
| 10 | +In the current implementation, `_tidb_rowid` is an extra `BIGINT NOT NULL` column automatically managed by TiDB. |
| 11 | + |
| 12 | +> **Warning:** |
| 13 | +> |
| 14 | +> - Do not assume that `_tidb_rowid` is globally unique in all cases. For partitioned tables that do not use clustered indexes, executing `ALTER TABLE ... EXCHANGE PARTITION` might result in duplicate `_tidb_rowid` values across different partitions. |
| 15 | +> - If you need a stable unique identifier, define and use an explicit primary key instead of relying on `_tidb_rowid`. |
| 16 | +
|
| 17 | +## When `_tidb_rowid` is available |
| 18 | + |
| 19 | +TiDB uses `_tidb_rowid` to identify each row when a table does not use a clustered primary key as the unique row identifier. In practice, this means that the following types of tables use `_tidb_rowid`: |
| 20 | + |
| 21 | +- Tables without primary keys |
| 22 | +- Tables with primary keys that are explicitly defined as `NONCLUSTERED` |
| 23 | + |
| 24 | +`_tidb_rowid` is not available for tables that use a clustered index (that is, tables whose primary key is defined as `CLUSTERED`, regardless of whether it is a single-column or composite primary key). |
| 25 | + |
| 26 | +The following example shows the difference: |
| 27 | + |
| 28 | +```sql |
| 29 | +CREATE TABLE t1 (a INT, b VARCHAR(20)); |
| 30 | +CREATE TABLE t2 (id BIGINT PRIMARY KEY NONCLUSTERED, a INT); |
| 31 | +CREATE TABLE t3 (id BIGINT PRIMARY KEY CLUSTERED, a INT); |
| 32 | +``` |
| 33 | + |
| 34 | +For `t1` and `t2`, you can query `_tidb_rowid` because these tables do not use a clustered index as the row identifier: |
| 35 | + |
| 36 | +```sql |
| 37 | +SELECT _tidb_rowid, a, b FROM t1; |
| 38 | +SELECT _tidb_rowid, id, a FROM t2; |
| 39 | +``` |
| 40 | + |
| 41 | +For `t3`, `_tidb_rowid` is unavailable because the clustered primary key is already the row identifier: |
| 42 | + |
| 43 | +```sql |
| 44 | +SELECT _tidb_rowid, id, a FROM t3; |
| 45 | +``` |
| 46 | + |
| 47 | +```sql |
| 48 | +ERROR 1054 (42S22): Unknown column '_tidb_rowid' in 'field list' |
| 49 | +``` |
| 50 | + |
| 51 | +## Read `_tidb_rowid` |
| 52 | + |
| 53 | +For tables that use `_tidb_rowid`, you can query `_tidb_rowid` in `SELECT` statements. This is useful for tasks such as pagination, troubleshooting, and batch processing. |
| 54 | + |
| 55 | +Example: |
| 56 | + |
| 57 | +```sql |
| 58 | +CREATE TABLE t (a INT, b VARCHAR(20)); |
| 59 | +INSERT INTO t VALUES (1, 'x'), (2, 'y'); |
| 60 | + |
| 61 | +SELECT _tidb_rowid, a, b FROM t ORDER BY _tidb_rowid; |
| 62 | +``` |
| 63 | + |
| 64 | +```sql |
| 65 | ++-------------+---+---+ |
| 66 | +| _tidb_rowid | a | b | |
| 67 | ++-------------+---+---+ |
| 68 | +| 1 | 1 | x | |
| 69 | +| 2 | 2 | y | |
| 70 | ++-------------+---+---+ |
| 71 | +``` |
| 72 | + |
| 73 | +To view the next value that TiDB will allocate for the row ID, use `SHOW TABLE ... NEXT_ROW_ID`: |
| 74 | + |
| 75 | +```sql |
| 76 | +SHOW TABLE t NEXT_ROW_ID; |
| 77 | +``` |
| 78 | + |
| 79 | +```sql |
| 80 | ++-----------------------+------------+-------------+--------------------+-------------+ |
| 81 | +| DB_NAME | TABLE_NAME | COLUMN_NAME | NEXT_GLOBAL_ROW_ID | ID_TYPE | |
| 82 | ++-----------------------+------------+-------------+--------------------+-------------+ |
| 83 | +| update_doc_rowid_test | t | _tidb_rowid | 30001 | _TIDB_ROWID | |
| 84 | ++-----------------------+------------+-------------+--------------------+-------------+ |
| 85 | +``` |
| 86 | + |
| 87 | +## Write `_tidb_rowid` |
| 88 | + |
| 89 | +By default, TiDB does not allow `INSERT`, `REPLACE`, or `UPDATE` statements to write `_tidb_rowid` directly. |
| 90 | + |
| 91 | +```sql |
| 92 | +INSERT INTO t(_tidb_rowid, a, b) VALUES (101, 4, 'w'); |
| 93 | +``` |
| 94 | + |
| 95 | +```sql |
| 96 | +ERROR 1105 (HY000): insert, update and replace statements for _tidb_rowid are not supported |
| 97 | +``` |
| 98 | + |
| 99 | +If you need to preserve the original row IDs during data import or migration, enable the [`tidb_opt_write_row_id`](/system-variables.md#tidb_opt_write_row_id) system variable first: |
| 100 | + |
| 101 | +```sql |
| 102 | +SET @@tidb_opt_write_row_id = ON; |
| 103 | +INSERT INTO t(_tidb_rowid, a, b) VALUES (100, 3, 'z'); |
| 104 | +SET @@tidb_opt_write_row_id = OFF; |
| 105 | + |
| 106 | +SELECT _tidb_rowid, a, b FROM t WHERE _tidb_rowid = 100; |
| 107 | +``` |
| 108 | + |
| 109 | +```sql |
| 110 | ++-------------+---+---+ |
| 111 | +| _tidb_rowid | a | b | |
| 112 | ++-------------+---+---+ |
| 113 | +| 100 | 3 | z | |
| 114 | ++-------------+---+---+ |
| 115 | +``` |
| 116 | + |
| 117 | +> **Warning:** |
| 118 | +> |
| 119 | +> `tidb_opt_write_row_id` is intended for import and migration scenarios. It is not recommended for regular application writes. |
| 120 | +
|
| 121 | +## Restrictions |
| 122 | + |
| 123 | +- You cannot create a user column named `_tidb_rowid`. |
| 124 | +- You cannot rename an existing user column to `_tidb_rowid`. |
| 125 | +- `_tidb_rowid` is an internal column in TiDB. Do not treat it as a business primary key or a long-term identifier. |
| 126 | +- On partitioned non-clustered tables, `_tidb_rowid` values are not guaranteed to be unique across partitions. After you execute `EXCHANGE PARTITION`, different partitions can contain rows with the same `_tidb_rowid` value. |
| 127 | +- Whether `_tidb_rowid` exists depends on the table schema. For tables with clustered indexes, use the primary key as the row identifier. |
| 128 | + |
| 129 | +## Address hotspot issues |
| 130 | + |
| 131 | +For tables that use `_tidb_rowid`, TiDB allocates row IDs in increasing order by default. In write-intensive workloads, this can create write hotspots. |
| 132 | + |
| 133 | +To mitigate this issue (for tables that rely on `_tidb_rowid` as the row ID), consider using [`SHARD_ROW_ID_BITS`](/shard-row-id-bits.md) to distribute row IDs more evenly, and use [`PRE_SPLIT_REGIONS`](/sql-statements/sql-statement-split-region.md#pre_split_regions) to pre-split Regions when necessary. |
| 134 | + |
| 135 | +Example: |
| 136 | + |
| 137 | +```sql |
| 138 | +CREATE TABLE t ( |
| 139 | + id BIGINT PRIMARY KEY NONCLUSTERED, |
| 140 | + c INT |
| 141 | +) SHARD_ROW_ID_BITS = 4; |
| 142 | +``` |
| 143 | + |
| 144 | +`SHARD_ROW_ID_BITS` applies only to tables that use `_tidb_rowid` and does not apply to tables with clustered indexes. |
| 145 | + |
| 146 | +## Related statements and variables |
| 147 | + |
| 148 | +- [`SHOW TABLE NEXT_ROW_ID`](/sql-statements/sql-statement-show-table-next-rowid.md): shows the next row ID that TiDB will allocate |
| 149 | +- [`SHARD_ROW_ID_BITS`](/shard-row-id-bits.md): shards implicit row IDs to reduce hotspots |
| 150 | +- [`Clustered Indexes`](/clustered-indexes.md): explains when a table uses the primary key instead of `_tidb_rowid` |
| 151 | +- [`tidb_opt_write_row_id`](/system-variables.md#tidb_opt_write_row_id): controls whether writes to `_tidb_rowid` are allowed |
| 152 | + |
| 153 | +## See also |
| 154 | + |
| 155 | +- [`CREATE TABLE`](/sql-statements/sql-statement-create-table.md) |
| 156 | +- [`AUTO_INCREMENT`](/auto-increment.md) |
| 157 | +- [Non-transactional DML](/non-transactional-dml.md) |
0 commit comments