0
0
0
0
专栏/.../

PCTP考试学习笔记之二:TiDB 数据库 schema 设计

 OnTheRoad  发表于  2022-09-02

本系列学习笔记根据官方课程《TiDB 高级系统管理 [TiDB v5]》整理,感谢官方精心制作的视频课程。相关课程介绍,详见官方课程链接:https://learn.pingcap.com/learner/course/120005

2. 第二章 TiDB 数据库 schema 设计

2.1. TiDB 数据库表结构设计

2.1.1. 数据库对象

TiDB 与 MySQL、Oracle 数据库的数据库对象比较如表2.1所示。


MySQLTiDBOracle
Table支持支持支持
Partition支持部分支持支持
View支持部分支持支持
Index支持支持支持
Sequence不支持支持支持
User Function支持不支持支持
Procedure支持不支持支持
Trigger支持不支持支持

2.1.1.1. 分区表

TiDB5.0 当前支持的分区类型包括 Range 分区、List 分区、List COLUMNS 分区、Hash 分区。其中,Range、List、List COLUMNS 分区适用于需要按范围或指定值大量删除数据的业务场景(直接删除分区);Hash 分区则可用于大量写入场景下的数据打散。

分区表的每个唯一键或主键,必须包含分区表达式中用到的所有列。示例如下:

# 有效的分区表
CREATE TABLE tab03
(
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY( col1,col2,col3 )
)
PARTITION BY HASH( col3 )
PARTITIONS 4;

# 无效的分区表
CREATE TABLE tab03
(
    col1 INT  NOT NULL,
    col2 DATE NOT NULL,
    col3 INT  NOT NULL,
    col4 INT  NOT NULL,
    UNIQUE KEY( col1,col2 )            # 唯一键未包含分区表达式中的 col3 列
)
PARTITION BY HASH( col3 )
PARTITIONS 4;

2.1.2. 聚簇索引

聚簇表(Clustered Table)在 TiDB 官方文档中称为 “聚簇索引表(index-organized tables)”,在Oracle 中称为“索引组织表(IOT,Index-Organized Tables)”。聚簇表具有如下特点:

  • 聚簇表中行数据的物理存储顺序与主键的存储顺序一致;

  • 通过主键访问行数据时,可以直接获取行数据,即只需 1 次 I/O;

  • TiDB 数据库中的聚簇表,其主键列也是 Region 中 KEY 的一部分;

  • 创建示例:“CREATE TABLE tab01(a BIGINT PRIMARY KEY CLUSTERED , b VARCHAR(255));”

非聚簇表(Non-clustered Table)具有如下的特点:

  • 表中行数据的物理存储顺序与主键(主键本质上是唯一索引)的存储顺序无关;

  • 通过主键访问非聚簇表的行数据时,无法直接获取行数据,需要 2 次 I/O(第一次扫主键索引,获取数据行的 RowID;第二次通过 RowID 回表获取行数据);

  • 创建示例:“CREATE TABLE tab02(a BIGINT PRIMARY KEY NONCLUSTERED ,b VARCHAR(255));”。

查询主键是否为聚簇索引的方式:

  • show create table <TableName>;

  • show index from <TableName>;

  • select TIDB_PK_TYPE from information_schema.tables where table_name = <TableName>;

mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> CREATE TABLE tab01 ( a BIGINT PRIMARY KEY CLUSTERED , b VARCHAR(255) );
Query OK, 0 rows affected (0.60 sec)

mysql> show create
    -> table
    -> tab01 ;
+-------+-----------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                          |
+-------+-----------------------------------------------------------------------------------------------------------------------------------+
| tab01 | CREATE TABLE `tab01` (
  `a` bigint(20) NOT NULL,
  `b` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+-------+-----------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

mysql> show index from tab01 ;
+------+-----------+---------+-------------+------------+----------+------------+---------+-------+-----------+--------+-----------+----------+
| Table| Non_unique| Key_name| Seq_in_index| Column_name| Collation| Cardinality| Sub_part| Packed| Index_type| Visible| Expression| Clustered|
+------+-----------+---------+-------------+------------+----------+-------------+--------+-------+-----------+--------+-----------+----------+
| tab01|        0  | PRIMARY |          1  | a          | A        |         0  |    NULL | NULL  | BTREE     | YES    | NULL      | YES      |
+------+-----------+---------+-------------+------------+----------+------------+---------+-------+-----------+--------+-----------+----------+
1 row in set (0.01 sec)

mysql> select TIDB_PK_TYPE from information_schema.tables where table_name = 'tab01';
+--------------+
| TIDB_PK_TYPE |
+--------------+
| CLUSTERED    |
+--------------+
1 row in set (0.04 sec)

【注意】

目前,TiDB 不支持在建表之后添加或删除聚簇索引,也不支持聚簇索引和非聚簇索引的互相转换。因为,非聚簇索引就是二级索引,所以,可以在建表之后,为其添加或删除非聚簇索引(二级索引)。此时,可指定 NONCLUSTERED 关键字或省略关键字。

2.1.2.1. 非聚簇表的热点写入问题

在非聚簇表中,TiDB 会为每行数据隐式增加一个 RowID 列(_tidb_rowid),默认 RowID 为单调递增。大量执行 INSERT 插入语句时会把数据集中写入单个 Region,造成写入热点。要避免因 RowID 带来的写入热点问题,可以在建表时,设置 SHARD_ROW_ID_BITS 和 PRE_SPLIT_REGIONS 这两个建表选项:

  • SHARD_ROW_ID_BITS=N 用于将 TiDB 为数据行隐式增加的 RowID 随机打散成 2^N 个分片(范围区间),以防止一个连续的范围区间,导致大量 INSERT 都查入到一个 Region 中。如 SHARD_ROW_ID_BITS=4,表示将 RowID 打散成 2^4=16 个分片。默认值为 0,表示 2^0=1 个分片。

  • PRE_SPLIT_REGIONS=N 用于在建完表后预先进行 Split Region。即建表后直接预先创建 2^N 个 Region。如 PRE_SPLIT_REGIONS=3 表示预先创建 2^3=8 个 Region。

【注意】

PRE_SPLIT_REGIONS 的值必须小于或等于 SHARD_ROW_ID_BITS

全局变量 tidb_scatter_region 会影响 PRE_SPLIT_REGIONS 的行为。该变量用于控制建表完成后是否等待预切分和打散 Region 完成后再返回结果。如果建表后有大批量写入,需要设置该变量值为 1,表示等待所有 Region 都切分和打散完成后再返回结果给客户端。否则未打散完成就进行写入,会对写入性能影响有较大的影响。

应用示例:

CREATE TABLE tab02( a int , b int ) SHARD_ROW_ID_BITS = 4 PRE_SPLIT_REGIONS=3;
  • SHARD_ROW_ID_BITS = 4 表示 _tidb_rowid 的值会随机打散成 2^4=16 个范围区间。

  • PRE_SPLIT_REGIONS = 3 表示建完表后提前切分出 2^3=8 个 Region。

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

2.1.3. 数据类型

TiDB 支持除空间类型 (SPATIAL) 之外的所有 MySQL 数据类型,包括数值型类型、字符串类型、时间和日期类型、JSON 类型,详细内容可参考如下链接:

在数据类型描述中的 DEFAULT value 段描述了一个列的默认值。针对数据类型的默认值,有如下几点需要注意:

  1. 数值型类型、大部分字符串类型列的默认值,必须是常量;

  2. 时间、日期类型的列,可以使用 NOWCURRENT_TIMESTAMPLOCALTIMELOCALTIMES-TAMP 等函数作为 DATETIME 或者 TIMESTAMP 的默认值。。

  3. BLOBTEXT 以及 JSON 不可以设置默认值。

  4. 若在列的定义中未指定 DEFAULT 的设置,则 TiDB 按如下规则决定:

    • 若类型可以使用 NULL 作为值,则 TiDB 为该列添加隐式的默认值设置 DEFAULT NULL

    • 若类型不可使用 NULL 作为值,则 TIDB 不会为其添加隐式的默认值设置。

  5. 对于设置了 NOT NULL,但未显示设置 DEFAULT 的列,当 INSERTREPLACE 未指定该列的值时,TiDB 根据当时的 SQL_MODE 进行不同的行为:

    • 若设置是 strict sql_mode,则在事务中的语句会导致事务失败并回滚;非事务中的语句直接报错;

    • 若设置不是 strict sql_mode,则 TiDB 会为该列赋值为列数据类型的隐式默认值。

  6. 数据类型的隐式默认值规则:

    • 对于数值类型,其隐式默认值为 0。当有 AUTO_INCREMENT 参数时,默认值会自动递增;

    • 对于除了时间戳外的日期时间类型,其隐式默认值是该类型的“零值”。时间戳类型的隐式默认值是当前的时间。

    • 对于除枚举以外的字符串类型,其隐式默认值是空字符串。对于枚举类型,隐式默认值是枚举中的第一个值。

2.1.3.1. 自增列(AUTO_INCREMENT)

  • 基本概念

AUTO_INCREMENT 是用于以自增方式,自动填充缺省列值的列属性。当 INSERT 语句没有指定 AUTO_INCREMENT 列的具体值时,系统会自动地为该列分配一个自增值。此外,AUTO_INCREMENT 还支持显式指定列值的插入语句,此时 TiDB 会保存显式指定的值:

mysql> CREATE TABLE t( id int PRIMARY KEY AUTO_INCREMENT, c int );

mysql> INSERT INTO t(c) VALUES(1);
mysql> INSERT INTO t(c) VALUES(2),(3);
mysql> INSERT INTO t(id,c) VALUES(4,4);

mysql> SELECT * FROM t ;
+----+------+
| id | c    |
+----+------+
|  1 |    1 |
|  2 |    2 |
|  3 |    3 |
|  4 |    4 |
+----+------+
4 rows in set (0.00 sec)

出于性能原因,自增编号是系统批量分配给每台 TiDB 服务器的值(默认 3 万个值),因此自增编号能保证唯一性,但分配给 INSERT 语句的值仅在单台 TiDB 服务器上具有单调性。即 AUTO_INCREMENT 无法保证自增列的连续性。

【注意】

使用 AUTO_INCREMENT 可能会给生产环境带热点问题,因此推荐使用 AUTO_RANDOM 代替。详情请参考《tidb-热点问题处理》。另外,使用 TiSpark 的小伙伴需要特别注意,截止到 TiDB v6.1 为止,TiSPark 暂时不支持写入含有AUTO_RANDOM 列的表。

  • 实现原理

对于每一个自增列,都使用一个全局可见的 KV 键值对用于记录当前已分配的 MaxID。由于分布式环境下的节点通信存在一定开销,为了避免写请求放大的问题,每个 TiDB 节点在分配 ID时,都申请一段 ID(默认 3 万个)作为缓存,用完之后再去取下一段。因此,自增编号可以保证全局唯一性,但分配给 INSERT 语句的值仅在单台 TiDB 服务器上具有单调性,无法保证全局单调性和连续性。例如,假设集群中有两个 TiDB 实例 A 和 B,则实例 A 可能会缓存 [1,30000] 的自增 ID,而实例 B 则可能缓存 [30001,60000] 的自增 ID。各自实例缓存的 ID 将随着执行将来的插入语句被作为缺省值,顺序地填充到 AUTO_INCREMENT 列中。AUTO_INCREMENT 缓存不会持久化,重启会导致缓存值失效。所以,当 TiDB Server 重启后,会重新申请一段新的 AUTO_INCREMENT 缓存值。频繁的重启可能会导致 AUTO_INCREMENT缓存值被快速消耗,也会导致表中自增列的值出现跳跃。另外,当手动为自增列指定值,也可能导致 TiDB 重新选取一段新的 AUTO_INCREMENT 缓存值。

  • AUTO_ID_CACHE

TiDB 自增 ID 的缓存大小在早期版本中是对用户透明的。从 v3.1.2、v3.0.14 和 v4.0.rc-2 版本开始,TiDB 引入了 AUTO_ID_CACHE 表选项来允许用户自主设置自增 ID 分配缓存的大小。

CREATE TABLE t( a int AUTO_INCREMENT key ) AUTO_ID_CACHE 100;
  • AUTO_INCREMENT 使用限制

    1. 定义的列必须为主键或者索引的首列。

    2. 只能定义在类型为整数、FLOAT 或 DOUBLE 的列上。

    3. 不支持与列的默认值 DEFAULT 同时指定在同一列上。

    4. 不支持使用 ALTER TABLE 来添加 AUTO_INCREMENT 属性。

    5. 支持使用 ALTER TABLE 来移除 AUTO_INCREMENT 属性。但从 TiDB 2.1.18 和 3.0.4 版本开始,TiDB 通过 session 变量 @@tidb_allow_remove_auto_inc 控制是否允许通过 ALTER TABLE MODIFY 或 ALTER TABLE CHANGE 来移除列的 AUTO_INCREMENT 属性,默认是不允许移除。

2.1.3.2. 随机自增列(AUTO_RANDOM)

  • 基本概念

AUTO_RANDOM 是用于以随机自增方式,自动填充缺省列值(BIGINT 类型)的列属性。当INSERT 语句没有指定 AUTO_RANDOM 列的具体值时,系统会自动地为该列分配一个随机值。此外,AUTO_RANDOM 还支持显式指定列值的插入语句,此时 TiDB 会保存显式指定的值。单个事务中的多条 INSERT 语句所产生的 AUTO_RANDOM 值是连续自增的,而多个事务中的单条 INSERT 语句所产生的 AUTO_RANDOM 值是随机的。因此,AUTO_RANDOM 可用于解决大批量写数据入 TiDB 时因含有整型自增主键列(AUTO_INCREMENT)的表而产生的热点问题。详情参阅 《TiDB 高并发写入场景最佳实践》

【注意】

若要显式设置 AUTO_RANDOM 列的值为 0,需要在系统变量 @@sql_mode 中设置 NO_AUTO_VALUE_ON_ZERO,否则 TiDB 会为该列分配随机值。从 v4.0.3 开始,要使用显式插入的功能,需将系统变量 @@allow_auto_random_explicit_insert 的值设置为 1(默认值为 0,不支持显示插入)。

  • 实现原理AUTO_RANDOM 值在二进制形式下,最高第 64 位为符号位,63-59 共五位(称为 shard bits)为随机位,由当前事务的 start_ts 决定,取值范围 1~32;剩下的位数按照自增的顺序分配。若要使用一个不同位数(bits)的 shard bits ,可以在 AUTO_RANDOM 后面加一对括号,并在括号中指定 shard bits 的位数,示例如下:

CREATE TABLE t( a bigint PRIMARY KEY AUTO_RANDOM(3) , b varchar(255) );

使用 SHOW WARNINGS 可以查看当前表可支持的最大隐式分配的次数。为保证可隐式分配的次数最大,从 v4.0.3 开始,AUTO_RANDOM 列类型只能为 BIGINT

查看某表 AUTO_RANDOM 属性的 shard bits 数量,可以在系统表 information_ schema.tables 中 TIDB_ROW_ID_SHARDING_INFO 一列看到模式为 PK_AUTO_RANDOM_BITS=x 的值,其中 x 为 shard bits 的数量。

可以使用 SELECT last_insert_id() 获取上一次 TiDB 隐式分配的 ID。

INSERT INTO t(b) VALUES("b");
SELECT * FROM t;
SELECT last_insert_id();
  • AUTO_RANDOM 使用限制

    • 该属性必须指定在 BIGINT 类型的主键列上,否则会报错。此外,当主键属性为 NONCLUSTERED 时,即使是整型主键列,也不支持使用 AUTO_RANDOM

    • 不支持使用 ALTER TABLE 来修改 AUTO_RANDOM 属性,包括添加或移除该属性。

    • 支持将 AUTO_INCREMENT 属性改为 AUTO_RANDOM 属性。但在 AUTO_INCREMENT 的列数据最大值已接近 BIGINT 类型最大值的情况下,修改可能会失败。

    • 不支持修改含有 AUTO_RANDOM 属性的主键列的列类型。

    • 不支持与 AUTO_INCREMENT 同时指定在同一列上。

    • 不支持与列的默认值 DEFAULT 同时指定在同一列上。

    • AUTO_RANDOM 列的数据很难迁移到 AUTO_INCREMENT 列上,因为 AUTO_RANDOM 列自动分配的值通常都很大。

    • 插入数据时,不建议自行显式指定含有 AUTO_RANDOM 列的值。不恰当地显式赋值,可能会导致该表提前耗尽用于自动分配的数值。

    • TiSPark 不支持写入含有 AUTO_RANDOM 列属性的表。详见 asktug:《TiSpark 不支持 auto_random 做主键的表批量写入》

2.1.4. Schema 设计最佳实践

![TableDesign](vx_images/8942015248976.png =600x)

2.2. TiDB 数据库索引设计

2.2.1. 索引的 KV 映射原理

2.2.1.1. 非聚簇表主键(或唯一索引)的 KV 映射

非聚簇表主键(或唯一索引)的 KEY 值唯一,TiDB 在为其做 KV 映射时,将 TableID、IndexID、索引列值作为索引 KEY,Value 为数据行的 RowID。映射方式如下:

Key:tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue
Value:RowID

2.2.1.2. 二级索引的 KV 映射

二级索引的索引键 KEY 可能有重复值,TiDB 在为二级索引做 KV 映射时,会为每条重复的行数据生成一个索引 KEY,Value 为 Null。映射方式如下:

Key:tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID}
Value:NULL

比如,为Users表的 “UserName” 列创建二级索引 “idxUsr”。假设存在 3 个同名用户 “Tom”,则该二级索引会生成 3 个索引 KEY:

Key:tablePrefixUsers_indexPrefixSepidxUsr_Tom_xxxx1,Value:NULL;
Key:tablePrefixUsers_indexPrefixSepidxUsr_Tom_xxxx2,Value:NULL;
Key:tablePrefixUsers_indexPrefixSepidxUsr_Tom_xxxx3,Value:NULL;

其中,xxxx1、xxxx2、xxxx3 为 “UserName=Tom” 的 3 条重复数据的 RowID。

【注意】

只有当二级索引是非唯一索引,其 KV 中的 Value 值才为 “NULL”。如果二级索引为唯一索引,如 unique 索引,则其 KV 中的 Value 存储的为主键索引。

如图2.1所示,为主键索引与二级索引的 KV 映射示例。

![索引的KV映射](vx_images/381532515244112.png =600x)

2.2.2. 索引

2.2.2.1. 索引的创建

TiDB 中创建索引的语法与 MySQL 的语法保持兼容。可以在建表时一并创建,也可以通过 “ALTERTABLE <TableName> ADD ... INDEX” 或 “CREATE INDEX <IndexName> ON ...” 的语法为存量表创建索引。创建索引时,不会阻塞表数据的读写。

CREATE TABLE books (
    id    bigint(20) AUTO_RANDOM NOT NULL,
    title varchar(100) NOT NULL,
    type  enum('Magazine', 'Novel', 'Life', 'Arts','Kids', 'Sports') NOT NULL,
    published_at datetime NOT NULL,
    stock int(11) DEFAULT '0',
    price decimal(15,2) DEFAULT '0.0',
    PRIMARY KEY (id) CLUSTERED        /* 创建聚簇索引 */
) ;

CREATE TABLE tab04 (
    id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    c1 INT NOT NULL,
    c2 INT NOT NULL,
    c3 INT NOT NULL,
    KEY idx_t1_c1(c1)                 /* 创建非唯一的二级索引idx_t1_c1
);

/* 创建普通索引 */
ALTER TABLE tab04 ADD INDEX idx_t04_c2(c2);

/* 创建唯一索引 */
ALTER TABLE tab04 ADD UNIQUE INDEX uidx_t04_c3(c3);

2.2.2.2. 组合索引

可以为多个列创建组合索引,组合索引在使用时,需要满足最左匹配原则。

/* 为列 title, published_at 新建一个组合索引 */
CREATE INDEX title_published_at_idx ON books (title, published_at);

/* 可以利用上索引查询 */
SELECT * FROM books WHERE title = 'database';
SELECT * FROM books WHERE title = 'database' and published_at = '2018-08-18 21:42:08';

/* 未指定组合索引最左列,无法利用上索引 */
SELECT * FROM books WHERE published_at = '2018-08-18 21:42:08';

创建一个联合索引 (c1,c2,c3),实际上相当于同时创建了 3 个索引:(c1)、(c1,c2)、(c1,c2,c3)。

2.2.2.3. 表达式索引

表达式索引是一种特殊的索引,能将索引建立于应用了表达式的列上。

CREATE INDEX idx1 ON tab04 ((lower(col1)));

【注意】

表达式索引中的表达式,需要用括号()括起来,否则会报语法错误。如用()将表达式“lower(col1)’‘括起来,即"(lower(col1))’’。

可通过查询变量 tidb_allow_function_for_expression_index 来得知哪些函数可用于创建表达式索引。

2.2.2.4. 索引覆盖

索引覆盖指通过索引即可完整获取所需数据,而无需二次回表,减少了I/O操作。所以,实际应用中,索引覆盖时主要的提升性能的优化手段之一。为了最大化利用索引覆盖,因避免使用"SELECT * FROM <TableName>’'的语法来读取数据。

2.2.2.5. 索引不可见

可通过 “ALTER INDEX ... VISIBLE/INVISIBLE” 来修改索引对优化器的可见(VISIBLE)与不可见(INVISIBLE)。不可见的索引,不会被优化器所使用。不允许将主键索引设置为不可见(INVISIBLE)。

CREATE TABLE tab05 (c1 INT, c2 INT, UNIQUE(c2));
CREATE UNIQUE INDEX uidx_c1 ON tab05(c1) VISIBLE;
ALTER INDEX uidx_c1 INVISIBLE;

2.2.2.6. 索引的注意事项

  • 不支持 FULLTEXTHASHSPATIAL(地理信息)索引;

  • 不支持反转索引;

  • 无法向表中添加 CLUSTERED 类型的 PRIMARY KEY;也无法从表中删除 CLUSTERED 类型的 PRIMARY KEY

  • 不支持类似 MySQL 中提供的优化器开关 use_invisible_indexes=on(将所有的不可见索引重新设置为可见)。

2.2.2.7. 查看索引的 Region 分布

SHOW TABLE <TableName> INDEX <IndexName> REGIONS <Where Clause Optional>

2.3. TiDB 数据库系统表使用

2.3.1. TiDB 系统表存储位置

TiDB 中系统表主要存储在 mysqlinformation_schemametrics_schemaperformance_schema 数据库中:

  • mysql 数据库,存储 TiDB 的系统表,如 mysql.user 用户表等;

  • INFORMATION_SCHEMA 数据库3,提供了一种查看系统元数据的 ANSI 标准方法:

    • 与 MySQL 兼容的表,如 TABLESPROCESSLISTCOLUMNS 等;

    • TiDB 自定义的系统表,如 CLUSTER_CONFIGCLUSTER_HARDWARETIFLASH_REPLICA 等。

  • METRICS_SCHEMA 数据库4,存储的是基于 Prometheus 中 TiDB 监控指标的一组视图;

  • PERFORMANCE_SCHEMA 数据库,目前为与 MySQL 兼容保留了部分视图(大部分内部使用)。

2.3.2. mysql 数据库

  • mysql.user 数据库用户表,包含全局权限以及其他一些非权限的列;

  • mysql.db 数据库级别的权限;

  • mysql.tables_priv 表级的权限;

  • mysql.columns_priv 列级的权限(5.3 以下的版本不支持)

  • mysql.GLOBAL_VARIABLES

    • 通过系统表方式查看 TiDB 全局变量,与 SHOW GLOBAL VARIABLES 查看的内容一致;

    • 支持对系统表中 VARIABLE_VALUE 的修改;

  • mysql.tidb

    • 以 KV 形式存储集群状态的表;

    • 通过修改 mysql.tidb 中相关 VARIABLE_VALUE 可以调整相关系统行为

2.3.3. INFORMATION_SCHEMA 数据库

2.3.3.1. INFORMATION_SCHEMA.CLUSTER_INFO

NFORMATION_SCHEMA.CLUSTER_INFO 表,提供了当前集群拓扑的详细信息。

USE information_schema;
desc cluster_info;

+----------------+-------------+------+------+---------+-------+
| Field          | Type        | Null | Key  | Default | Extra |
+----------------+-------------+------+------+---------+-------+
| TYPE           | varchar(64) | YES  |      | NULL    |       |
| INSTANCE       | varchar(64) | YES  |      | NULL    |       |
| STATUS_ADDRESS | varchar(64) | YES  |      | NULL    |       |
| VERSION        | varchar(64) | YES  |      | NULL    |       |
| GIT_HASH       | varchar(64) | YES  |      | NULL    |       |
| START_TIME     | varchar(32) | YES  |      | NULL    |       |
| UPTIME         | varchar(32) | YES  |      | NULL    |       |
+----------------+-------------+------+------+---------+-------+
7 rows in set (0.00 sec)

SELECT * FROM cluster_info;
+------+-----------------+-----------------+-------------+----------+-----------------+----------------+
| TYPE | INSTANCE        | STATUS_ADDRESS  | VERSION     | GIT_HASH | START_TIME      | UPTIME         |
+------+-----------------+-----------------+-------------+----------+-----------------+----------------+
| tidb | 0.0.0.0:4000    | 0.0.0.0:10080   | 4.0.0-beta  | 0df3...f7| 2020-07-05T09:25| 26h39m4.3...3s |
| pd   | 127.0.0.1:2379  | 127.0.0.1:2379  | 4.1.0-alpha | 1ad5...62| 2020-07-05T09:25| 26h39m10.3...3s|
| tikv | 127.0.0.1:20165 | 127.0.0.1:20180 | 4.1.0-alpha | b45e...df| 2020-07-05T09:25| 26h39m7.3...3s |
+------+-----------------+-----------------+-------------+----------+-----------------+----------------+
3 rows in set (0.00 sec)
  • 字段解释:

    • TYPE:节点类型,目前节点的可取值为 tidb,pd 和 tikv。

    • INSTANCE:实例地址,为 IP:PORT 格式的字符串。

    • STATUS_ADDRESS:HTTP API 的服务地址。部分 tikv-ctl、pd-ctl 或 tidb-ctl 命令会使用到 HTTP API 和该地址。用户也可以通过该地址获取一些额外的集群信息,详情可参考 HTTP API 文档。

    • VERSION:对应节点的语义版本号。TiDB 版本为了兼容 MySQL 的版本号,以 ${mysql-version}-${tidb-version} 的格式展示版本号。

    • GIT_HASH:编译节点版本时的 Git Commit Hash,用于识别两个节点是否是绝对一致的版本。

    • START_TIME:对应节点的启动时间。

    • UPTIME:对应节点已经运行的时间。

2.3.3.2. INFORMATION_SCHEMA.CLUSTER_CONFIG

TiDB v4.0 引入了 CLUSTER_CONFIG 表,用于获取集群当前所有组件实例的配置。

USE information_schema;
DESC cluster_config;

+----------+--------------+------+------+---------+-------+
| Field    | Type         | Null | Key  | Default | Extra |
+----------+--------------+------+------+---------+-------+
| TYPE     | varchar(64)  | YES  |      | NULL    |       |
| INSTANCE | varchar(64)  | YES  |      | NULL    |       |
| KEY      | varchar(256) | YES  |      | NULL    |       |
| VALUE    | varchar(128) | YES  |      | NULL    |       |
+----------+--------------+------+------+---------+-------+

/* 查询 TiKV 实例的 coprocessor 相关配置: */
SELECT * FROM cluster_config WHERE type='tikv' AND key LIKE 'coprocessor%';

+------+-----------------+-----------------------------------+---------+
| TYPE | INSTANCE        | KEY                               | VALUE   |
+------+-----------------+-----------------------------------+---------+
| tikv | 127.0.0.1:20165 | coprocessor.batch-split-limit     | 10      |
| tikv | 127.0.0.1:20165 | coprocessor.region-max-keys       | 1440000 |
| tikv | 127.0.0.1:20165 | coprocessor.region-max-size       | 144MiB  |
| tikv | 127.0.0.1:20165 | coprocessor.region-split-keys     | 960000  |
| tikv | 127.0.0.1:20165 | coprocessor.region-split-size     | 96MiB   |
| tikv | 127.0.0.1:20165 | coprocessor.split-region-on-table | false   |
+------+-----------------+-----------------------------------+---------+
6 rows in set (0.00 sec)
  • 字段解释:

    • TYPE:节点的类型,可取值为 tidb,pd 和 tikv。

    • INSTANCE:节点的服务地址。

    • KEY:配置项名。

    • VALUE:配置项值。

2.3.3.3. INFORMATION_SCHEMA.DDL_JOBS

DDL_JOBS 表为 ADMIN SHOW DDL JOBS 命令提供了一个 INFORMATION_SCHEMA 接口。它提供了 TiDB 集群中 DDL 操作的当前状态和简短历史记录。

USE information_schema;
DESC cluster_config;

+--------------+-------------+------+------+---------+-------+
| Field        | Type        | Null | Key  | Default | Extra |
+--------------+-------------+------+------+---------+-------+
| JOB_ID       | bigint(21)  | YES  |      | NULL    |       |
| DB_NAME      | varchar(64) | YES  |      | NULL    |       |
| TABLE_NAME   | varchar(64) | YES  |      | NULL    |       |
| JOB_TYPE     | varchar(64) | YES  |      | NULL    |       |
| SCHEMA_STATE | varchar(64) | YES  |      | NULL    |       |
| SCHEMA_ID    | bigint(21)  | YES  |      | NULL    |       |
| TABLE_ID     | bigint(21)  | YES  |      | NULL    |       |
| ROW_COUNT    | bigint(21)  | YES  |      | NULL    |       |
| START_TIME   | datetime    | YES  |      | NULL    |       |
| END_TIME     | datetime    | YES  |      | NULL    |       |
| STATE        | varchar(64) | YES  |      | NULL    |       |
| QUERY        | varchar(64) | YES  |      | NULL    |       |
+--------------+-------------+------+------+---------+-------+
12 rows in set (0.00 sec)

SELECT * FROM ddl_jobs LIMIT 2\G

*************************** 1. row ***************************
      JOB_ID: 44
     DB_NAME: mysql
  TABLE_NAME: opt_rule_blacklist
    JOB_TYPE: create table
SCHEMA_STATE: public
   SCHEMA_ID: 3
    TABLE_ID: 43
   ROW_COUNT: 0
  START_TIME: 2020-07-06 15:24:27
    END_TIME: 2020-07-06 15:24:27
       STATE: synced
       QUERY: CREATE TABLE IF NOT EXISTS mysql.opt_rule_blacklist (
        name char(100) NOT NULL
    );
*************************** 2. row ***************************
      JOB_ID: 42
     DB_NAME: mysql
  TABLE_NAME: expr_pushdown_blacklist
    JOB_TYPE: create table
SCHEMA_STATE: public
   SCHEMA_ID: 3
    TABLE_ID: 41
   ROW_COUNT: 0
  START_TIME: 2020-07-06 15:24:27
    END_TIME: 2020-07-06 15:24:27
       STATE: synced
       QUERY: CREATE TABLE IF NOT EXISTS mysql.expr_pushdown_blacklist (
        name char(100) NOT NULL,
        store_type char(100) NOT NULL DEFAULT 'tikv,tiflash,tidb',
        reason varchar(200)
    );
2 rows in set (0.01 sec)

2.3.4. 常用运维查询

2.3.4.1. 查询某个用户的 Top N 慢 SQL

SELECT query_time, query FROM information_schema.slow_query 
WHERE is_internal = false AND user = "user1" ORDER BY query_time DESC LIMIT 2;

+----------------+-----------------------------------------------------------------+
|   query_time   |                    query                                        |
+----------------+-----------------------------------------------------------------+
|  12.77583857   | select * from t_slim, t_wide where t_slim.c0=t_wide.c0;         |
|  0.734982725   | select t0.c0, t1.c1 from t_slim t0, t_wide t1 where t0.c0=t1.c0;|
+----------------+-----------------------------------------------------------------+

2.3.4.2. 根据SQL指纹,查询某类慢SQL

digest字段即为SQL指纹,根据SQL指纹可以查询到同一类SQL。

SELECT query_time, query, digest FROM information_schema.slow_query 
WHERE is_internal = false AND time between '2021-09-21' AND '2021-09-02' ORDER BY query_time desc limit 1;

+----------------+-----------------------------+--------------------------------------+
|   query_time   |             query           |                 digest               |
+----------------+-----------------------------+--------------------------------------+
|  0.734982725   | select * from t1 where a=1; | 4751cb6008fda383e22dacb...bafb46a6fa |
+----------------+--------------------------------------------------------------------+

SELECT query, query_time FROM information_schema.slow_query
WHERE digest = "4751cb6008fda383e22dacb...bafb46a6fa"
+----------------+-----------------------------+
|             query           |   query_time   |
+-----------------------------+----------------+
| select * from t1 where a=1; |  0.734982725   |
| select * from t1 where a=2; |  0.414564635   |
+----------------------------------------------+

2.3.4.3. 统计读写热点表

SELECT db_name, table_name, index_name, type, sum(flow_bytes), count(1), 
       group_concat(h.region_id), count(distinct p.store_id), group_concat(p.store_id)
  FROM tidb_hot_regions h JOIN tikv_region_peers p
    ON h.region_id = p.region_id
   AND p.is_leader=1
 GROUP BY db_name,table_name,index_name,type;

2.3.4.4. 统计读写热点store

SELECT p.store_id, sum(flow_bytes), count(1) 
  FROM tidb_hot_regions h JOIN tikv_region_peers p 
    ON h.region_id = p.region_id AND p.is_leader=1 
 GROUP BY p.store_id 
 ORDER BY 2 desc;

2.3.4.5. DATA_LOCK_WAITS

information_schema.DATA_LOCK_WAITS 表展示了集群中所有 TiKV 节点上当前正在发生的悲观锁等锁的情况。

DESC data_lock_waits;
+------------------------+---------------------+------+------+---------+-------+
| Field                  | Type                | Null | Key  | Default | Extra |
+------------------------+---------------------+------+------+---------+-------+
| KEY                    | text                | NO   |      | NULL    |       |
| KEY_INFO               | text                | YES  |      | NULL    |       |
| TRX_ID                 | bigint(21) unsigned | NO   |      | NULL    |       |
| CURRENT_HOLDING_TRX_ID | bigint(21) unsigned | NO   |      | NULL    |       |
| SQL_DIGEST             | varchar(64)         | YES  |      | NULL    |       |
| SQL_DIGEST_TEXT        | text                | YES  |      | NULL    |       |
+------------------------+---------------------+------+------+---------+-------+
  • DATA_LOCK_WAITS 表中各列的字段含义如下:

    • KEY:正在发生等锁的 key,以十六进制编码的形式显示。

    • KEY_INFO:对 KEY 进行解读得出的一些详细信息,见 KEY_INFO

    • TRX_ID:正在等锁的事务 ID,即 start_ts

    • CURRENT_HOLDING_TRX_ID:当前持有锁的事务 ID,即 start_ts

    • SQL_DIGEST:当前正在等锁的事务中被阻塞的 SQL 语句的 Digest

    • SQL_DIGEST_TEXT:当前正在等锁的事务中被阻塞的 SQL 语句的归一化形式,即去除了参数和格式的 SQL 语句。与 SQL_DIGEST 对应。

【注意】:

  • 仅拥有 PROCESS 权限的用户可以查询该表。

  • 目前该表仅能记录悲观锁等锁的情况

  • DATA_LOCK_WAITS 表中的信息是在查询时,从所有 TiKV 节点实时获取的。目前,即使加上了 WHERE 查询条件,也无法避免对所有 TiKV 节点都进行信息收集。

  • 来自不同 TiKV 节点的信息不能保证是同一时间点的快照。

  • 如果集群规模很大、负载很高,查询该表有造成性能抖动的潜在风险,因此请根据实际情况使用。

2.3.4.6. DEADLOCKS

DEADLOCKS 表提供当前 TiDB 节点上最近发生的若干次死锁错误的信息。默认容纳最近 10 次死锁错误的信息。

USE information_schema;
DESC deadlocks;

+-------------------------+---------------------+------+------+---------+-------+
| Field                   | Type                | Null | Key  | Default | Extra |
+-------------------------+---------------------+------+------+---------+-------+
| DEADLOCK_ID             | bigint(21)          | NO   |      | NULL    |       |
| OCCUR_TIME              | timestamp(6)        | YES  |      | NULL    |       |
| RETRYABLE               | tinyint(1)          | NO   |      | NULL    |       |
| TRY_LOCK_TRX_ID         | bigint(21) unsigned | NO   |      | NULL    |       |
| CURRENT_SQL_DIGEST      | varchar(64)         | YES  |      | NULL    |       |
| CURRENT_SQL_DIGEST_TEXT | text                | YES  |      | NULL    |       |
| KEY                     | text                | YES  |      | NULL    |       |
| KEY_INFO                | text                | YES  |      | NULL    |       |
| TRX_HOLDING_LOCK        | bigint(21) unsigned | NO   |      | NULL    |       |
+-------------------------+---------------------+------+------+---------+-------+
  • DEADLOCKS 表中各列的字段含义如下:

    • DEADLOCK_ID:死锁事件的 ID。当表内存在多次死锁错误的信息时,需要使用该列来区分属于不同死锁错误的行。

    • OCCUR_TIME:发生该次死锁错误的时间。

    • RETRYABLE:该次死锁错误是否可重试。关于可重试的死锁错误的说明,参见可重试的死锁错误小节。

    • TRY_LOCK_TRX_ID:试图上锁的事务 ID,即事务的 start_ts

    • CURRENT_SQL_DIGEST:试图上锁的事务中当前正在执行的 SQL 语句的 Digest

    • CURRENT_SQL_DIGEST_TEXT:试图上锁的事务中当前正在执行的 SQL 语句的归一化形式。

    • KEY:该事务试图上锁、但是被阻塞的 key,以十六进制编码的形式显示。

    • KEY_INFO:对 KEY 进行解读得出的一些详细信息,详见 KEY_INFO

    • TRX_HOLDING_LOCK:该 key 上当前持锁并导致阻塞的事务 ID,即事务的 start_ts

2.3.4.7. TIDB_TRX

TIDB_TRX 表提供了当前 TiDB 节点上正在执行的事务的信息。

USE information_schema;
DESC tidb_trx;

+-------------------------+--------------------------------------+------+------+---------+-------+
| Field                   | Type                                 | Null | Key  | Default | Extra |
+-------------------------+--------------------------------------+------+------+---------+-------+
| ID                      | bigint(21) unsigned                  | NO   | PRI  | NULL    |       |
| START_TIME              | timestamp(6)                         | YES  |      | NULL    |       |
| CURRENT_SQL_DIGEST      | varchar(64)                          | YES  |      | NULL    |       |
| CURRENT_SQL_DIGEST_TEXT | text                                 | YES  |      | NULL    |       |
| STATE                   | enum('Idle','Running','LockWaiting',
                                     'Committing','RollingBack') | YES  |      | NULL    |       |
| WAITING_START_TIME      | timestamp(6)                         | YES  |      | NULL    |       |
| MEM_BUFFER_KEYS         | bigint(64)                           | YES  |      | NULL    |       |
| MEM_BUFFER_BYTES        | bigint(64)                           | YES  |      | NULL    |       |
| SESSION_ID              | bigint(21) unsigned                  | YES  |      | NULL    |       |
| USER                    | varchar(16)                          | YES  |      | NULL    |       |
| DB                      | varchar(64)                          | YES  |      | NULL    |       |
| ALL_SQL_DIGESTS         | text                                 | YES  |      | NULL    |       |
+-------------------------+--------------------------------------+------+------+---------+-------+
  • 字段含义:

    • ID:事务 ID,即事务的开始时间戳 start_ts

    • START_TIME:事务的开始时间,即事务的 start_ts 所对应的物理时间。

    • CURRENT_SQL_DIGEST:该事务当前正在执行的 SQL 语句的 Digest。

    • CURRENT_SQL_DIGEST_TEXT:该事务当前正在执行的 SQL 语句的归一化形式,即去除了参数和格式的 SQL 语句。与 CURRENT_SQL_DIGEST 对应。

    • STATE:该事务当前所处的状态。其可能的值包括:

      • Idle:事务处于闲置状态,即正在等待用户输入查询。

      • Running:事务正在正常执行一个查询。

      • LockWaiting:事务处于正在等待悲观锁上锁完成的状态。需要注意的是,事务刚开始进行悲观锁上锁操作时即进入该状态,无论是否有被其它事务阻塞。

      • Committing:事务正在提交过程中。

      • RollingBack:事务正在回滚过程中。

    • WAITING_START_TIME:当 STATE 值为 LockWaiting 时,该列显示等待的开始时间。

    • MEM_BUFFER_KEYS:当前事务写入内存缓冲区的 key 的个数。

    • MEM_BUFFER_BYTES:当前事务写入内存缓冲区的 key 和 value 的总字节数。

    • SESSION_ID:该事务所属的 session 的 ID。

    • USER:执行该事务的用户名。

    • DB:执行该事务的 session 当前的默认数据库名。

    • ALL_SQL_DIGESTS:该事务已经执行过的语句的 Digest 的列表,表示为一个 JSON 格式的字符串数组。每个事务最多记录前 50 条语句。通过 TIDB_DECODE_SQL_DIGESTS 函数可以将该列的信息变换为对应的归一化 SQL 语句的列表。

 创建主题取消

0
0
0
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论