AUTO_RANDOM ⽤于解决⼤批量写数据⼊ TiDB 时因含有整型⾃增主键列的表⽽产⽣的热点问题。简而言之:就是数据处理不要只在一个region上。 通过随机的产生主键,让数据分布在不同的region上。auto_random: 是一个字段属性,用于自动随机填充默认列值以解决热点问题
mysql> select TIDB_ROW_ID_SHARDING_INFO, TIDB_PK_TYPE
-> from information_schema.tables
-> where table_schema='test'
-> and table_name='auto_random_t1';
+---------------------------+--------------+
| TIDB_ROW_ID_SHARDING_INFO | TIDB_PK_TYPE |
+---------------------------+--------------+
| PK_AUTO_RANDOM_BITS=5 | CLUSTERED |
+---------------------------+--------------+
1 row in set (0.17 sec)
create table t (a int, b int) PRE_SPLIT_REGIONS=3;
开始写数据进表 t 后,数据会被写入提前切分好的 8 (2^3次方)个 Region 中,这样也避免了刚开始建表完后因为只有一个 Region 而存在的写热点问题。
那如何将数据插入到这8个region中呢,这个时候就可以通过id(主键)来做这个事情,此时就可以用 AUTO_RANDOM (如果用auto_increment就不能打散了)
@@allow_auto_random_explicit_insert 的值设置为 1 (默认值为 0 )。
CREATE TABLE t (a bigint PRIMARY KEY AUTO_INCREMENT, b varchar(255))
在以上语句所建的表上执⾏⼤量未指定主键值的 INSERT 语句,示例如下:
INSERT INTO t(b) VALUES ('a'), ('b'), ('c')
如以上语句,由于未指定主键列的值( a 列),TiDB 会使⽤连续⾃增的⾏值作为⾏ ID,可能导致单个 TiKV 节点上产⽣写⼊热点,进⽽影响对外提供服务的性能。要避免这种写⼊热点,可以在执⾏建表语句时为 a 列指定 AUTO_RANDOM 属性⽽不是 AUTO_INCREMENT 属性。
建表
CREATE TABLE t (a bigint PRIMARY KEY AUTO_RANDOM, b varchar(255))
或者
CREATE TABLE t (a bigint AUTO_RANDOM, b varchar(255), PRIMARY KEY (a))
此时再执⾏形如 INSERT INTO t(b) values… 的 INSERT 语句。
注意
⾃动分配值的计算⽅式如下:
该⾏值在⼆进制形式下,除去符号位的最⾼五位(称为 shard bits)由当前事务的开始时间决定,剩下的位数按照⾃增的顺序分配。
分片数量:
若要使⽤⼀个不同的 shard bits 的数量,可以在 AUTO_RANDOM 后⾯加⼀对括号,并在括号中指定想要的 shard bits 数量。示例如下:
CREATE TABLE t (a bigint PRIMARY KEY AUTO_RANDOM(3), b varchar(255))
以上建表语句中,shard bits 的数量为 3 。shard bits 的数量的取值范围是 [1,16) 。
创建完表后,使⽤ SHOW WARNINGS 可以查看当前表可⽀持的最⼤隐式分配的次数:
SHOW WARNINGS
+-------+------+---------------------------------------------------
-------+
| Level | Code | Message
|
+-------+------+---------------------------------------------------
-------+
| Note | 1105 | Available implicit allocation times:
1152921504606846976 |
+-------+------+---------------------------------------------------
-------+
AUTO RANDOM 列隐式分配的值会影响 last_insert_id() 。
可以使⽤ SELECT last_insert_id() 获取上⼀次 TiDB 隐式分配的 ID,例如:
INSERT INTO t (b) VALUES ("b")
SELECT * FROM t;
SELECT last_insert_id()
可能得到的结果如下:
+------------+---+
| a | b |
+------------+---+
| 1073741825 | b |
+------------+---+
+------------------+
| last_insert_id() |
+------------------+
| 1073741825 |
+------------------+
TiDB ⽀持解析版本注释语法。示例如下:
CREATE TABLE t (a bigint PRIMARY KEY /*T![auto_rand] auto_random */)
CREATE TABLE t (a bigint PRIMARY KEY AUTO_RANDOM)
以上两个语句含义相同。
在 SHOW CREATE TABLE 的结果中, AUTO_RANDOM 属性会被注释掉。注释会附
带⼀个特性标识符,例如 /*T![auto_rand] auto_random */
。其中auto_rand 表示 AUTO_RANDOM 的特性标识符,只有实现了该标识符对应特性的TiDB 版本才能够正常解析 SQL 语句⽚段。
该功能⽀持向前兼容,即降级兼容。没有实现对应特性的 TiDB 版本则会忽略表(带有上述注释)的 AUTO_RANDOM 属性,因此能够使⽤含有该属性的表。
# 新建表
DROP TABLE IF EXISTS test.auto_random_t1;
CREATE TABLE test.auto_random_t1 (
id bigint PRIMARY KEY AUTO_RANDOM(3),
name char(255));
## 插入数据
/* Populate Seed */
INSERT INTO test.auto_random_t1 (name) VALUES ('A');
INSERT INTO test.auto_random_t1 (name) VALUES ('B');
INSERT INTO test.auto_random_t1 (name) VALUES ('C');
INSERT INTO test.auto_random_t1 (name) VALUES ('D');
INSERT INTO test.auto_random_t1 (name) VALUES ('E');
INSERT INTO test.auto_random_t1 (name) VALUES ('F');
INSERT INTO test.auto_random_t1 (name) VALUES ('G');
INSERT INTO test.auto_random_t1 (name) VALUES ('H');
INSERT INTO test.auto_random_t1 (name) VALUES ('I');
INSERT INTO test.auto_random_t1 (name) VALUES ('J');
INSERT INTO test.auto_random_t1 (name) VALUES ('K');
INSERT INTO test.auto_random_t1 (name) VALUES ('L');
INSERT INTO test.auto_random_t1 (name) VALUES ('M');
INSERT INTO test.auto_random_t1 (name) VALUES ('N');
INSERT INTO test.auto_random_t1 (name) VALUES ('O');
INSERT INTO test.auto_random_t1 (name) VALUES ('P');
INSERT INTO test.auto_random_t1 (name) VALUES ('Q');
INSERT INTO test.auto_random_t1 (name) VALUES ('R');
INSERT INTO test.auto_random_t1 (name) VALUES ('S');
INSERT INTO test.auto_random_t1 (name) VALUES ('T');
INSERT INTO test.auto_random_t1 (name) VALUES ('U');
INSERT INTO test.auto_random_t1 (name) VALUES ('V');
INSERT INTO test.auto_random_t1 (name) VALUES ('W');
INSERT INTO test.auto_random_t1 (name) VALUES ('X');
INSERT INTO test.auto_random_t1 (name) VALUES ('Y');
INSERT INTO test.auto_random_t1 (name) VALUES ('Z');
INSERT INTO test.auto_random_t1 (name) VALUES ('a');
INSERT INTO test.auto_random_t1 (name) VALUES ('b');
INSERT INTO test.auto_random_t1 (name) VALUES ('c');
INSERT INTO test.auto_random_t1 (name) VALUES ('d');
INSERT INTO test.auto_random_t1 (name) VALUES ('e');
INSERT INTO test.auto_random_t1 (name) VALUES ('f');
INSERT INTO test.auto_random_t1 (name) VALUES ('g');
INSERT INTO test.auto_random_t1 (name) VALUES ('h');
INSERT INTO test.auto_random_t1 (name) VALUES ('i');
INSERT INTO test.auto_random_t1 (name) VALUES ('j');
INSERT INTO test.auto_random_t1 (name) VALUES ('k');
INSERT INTO test.auto_random_t1 (name) VALUES ('l');
INSERT INTO test.auto_random_t1 (name) VALUES ('m');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
INSERT INTO test.auto_random_t1 (name) VALUES ('x1');
/* Check last_insert_id() */
SELECT LAST_INSERT_ID();
/* Greetings to CBO */
ANALYZE TABLE test.auto_random_t1;
/* select 'test.auto_random_t1' as Title; */
/* desc test.auto_random_t1; */
select TIDB_ROW_ID_SHARDING_INFO, TIDB_PK_TYPE
from information_schema.tables
where table_schema='test'
and table_name='auto_random_t1';
mysql> select TIDB_ROW_ID_SHARDING_INFO, TIDB_PK_TYPE
-> from information_schema.tables
-> where table_schema='test'
-> and table_name='auto_random_t1';
+---------------------------+--------------+
| TIDB_ROW_ID_SHARDING_INFO | TIDB_PK_TYPE |
+---------------------------+--------------+
| PK_AUTO_RANDOM_BITS=3 | CLUSTERED |
+---------------------------+--------------+
1 row in set (0.17 sec)
/* check value */
# 虽然是随机的,但每个region当中的id还是有一定规律。
SELECT substr(cast(id as CHAR),1,2) as id_prefix, count(*) as
approx_rows_in_shard
FROM test.auto_random_t1
GROUP BY id_prefix
HAVING approx_rows_in_shard > 1
ORDER BY id_prefix;
/*SHOW TABLE test.auto_random_t1 REGIONS\G*/
# 公有16个分片,每个分片的汇总个数
mysql> SELECT substr(cast(id as CHAR),1,2) as id_prefix, count(*) as
-> approx_rows_in_shard
-> FROM test.auto_random_t1
-> GROUP BY id_prefix
-> HAVING approx_rows_in_shard > 1
-> ORDER BY id_prefix;
+-----------+----------------------+
| id_prefix | approx_rows_in_shard |
+-----------+----------------------+
| 10 | 2 |
| 11 | 29 |
| 15 | 3 |
| 16 | 4 |
| 19 | 2 |
| 23 | 31 |
| 34 | 32 |
| 46 | 18 |
| 57 | 26 |
| 69 | 14 |
| 80 | 22 |
+-----------+----------------------+
11 rows in set (0.28 sec)