TiDB数据库schema设计之索引设计

本文详细介绍了TiDB数据库的索引设计原理,包括KV映射、聚簇与非聚簇索引、二级索引以及联合索引的最左匹配原则。同时,讲解了索引的创建、不可见索引的特性,并提供了索引使用的注意事项和运维技巧,如索引覆盖和表达式索引。此外,还强调了TiDB在索引方面的限制和查询优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

索引的KV映射原理

首先回顾一下表的行数据的KV映射原理:

对于聚簇表,TiDB会将表的编号加上主键作为Key,其余列作为Value。

# 假设Col1是主键列(聚簇索引)
Key: tablePrefix{tableID}_recordPrefixSep{Col1}
Value:{Col2,Col3,Col4}

对于非聚簇表,TiDB会自动为每行数据隐式生成一个RowID,将表的编号加上RowID作为Key,Value中包含所有列的数据。

Key: tablePrefix{tableID}_recordPrefixSep{_tidb_RowID}
Value:{Col1,Col2,Col3,Col4}

唯一索引&非聚簇表的主键

Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue
Value: RowID

二级索引
二级索引的Key不一定唯一。如果二级索引是非唯一索引,其Value值为null;如果二级索引是唯一索引,其Value中存储的是主键索引。

Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue_{RowID}
Value: null

下面是一个示例:

create table `EldenBoss` (
  ID int not null primary key nonclustered,
  name varchar(20),
  role varchar(20),
  age int,
  KEY idxAge(age)
);

insert into EldenBoss values (100, "Hoarah Loux", "Elden Lord", 56);
insert into EldenBoss values (200, "Mohg", "Hentai Noble", 28);
insert into EldenBoss values (300, "Malenia", "Goddess Warrior", 24);

该表中,数据的KV映射关系为:

t10_r1 --> [100, "Hoarah Loux", "Elden Lord", 56]
t10_r2 --> [200, "Mohg", "Hentai Noble", 28]
t10_r3 --> [300, "Malenia", "Goddess Warrior", 24]

主键的KV映射关系为:

t10_i1_100 --> 1
t10_i1_200 --> 2
t10_i1_300 --> 3

二级索引的KV映射关系为:

t10_i2_56_1 --> null
t10_i2_28_2 --> null
t10_i2_24_3 --> null

索引的设计

索引的创建

TiDB的索引创建语法与MySQL语法兼容。既可以在建表时一起创建,也可以在建表后单独添加。TiDB在创建索引时不会阻塞表的读写。

> create table `t1` (
  id int not null primary key auto_increment,
  c1 int not null,
  c2 int not null,
  key idx_t1_c1 (c1)
);

> alter table t1 add index idx_t1_c2(c2);
> alter table t1 add unique index uidx_t1_id(`id`);

TiDB目前还不支持在一个alter table语句中同时创建或修改多个索引。

联合索引

联合索引在查询时遵循最左匹配原则

> create table `t2` (
  id int not null primary key auto_increment,
  c1 int not null,
  c2 int not null,
  c3 int not null,
  key idx_t2 (c1,c2,c3)
);

创建一个联合索引(c1,c2,c3),相当于创建了(c1)(c1,c2)(c1,c2,c3)三个索引。

可以有效使用联合索引的场景:

> select * from t2 where c1 between 1 and 20;   -- (c1)
> select * from t2 where c1=20 and c2>5 and c2<10;   -- (c1,c2)
> select * from t2 where c1=5 and c2=10 and c3 between 1 and 50;   -- (c1,c2,c3)

可以部分使用联合索引的场景:

-- 只能使用(c1),c1不是等值查询所以无法使用(c1,c2)
> select * from t2 where c1 between 1 and 20 and c2=30;   
-- 只能使用(c1,c2),c2不是等值查询所以无法使用(c1,c2,c3)
> select * from t2 where c1=3 and c2>5 and c2<10 and c3=12;

不能使用联合索引的场景:

-- 查询条件不是以c1开始,不符合最左匹配原则
> select * from t2 where c2=30 and c3=10;
> select * from t2 where c3=100;

索引覆盖

索引覆盖是指当通过索引可以获取完整的行数据,那么可以直接通过遍历索引来取得数据,无需回表,从而减少IO。索引覆盖是提升数据库查询性能的主要手段之一。

-- c1,c2,c3列值都存储在联合索引的Key中
> select c1 from t2 where c1=1 and c2-100 and c3 between 10 and 50;
> select c1,c2,c3 from t2 where c1=100 and c2 between 1 and 200;

表达式索引

表达式索引是一种特殊的索引,能够将索引建立于表达式之上。表达式索引中的表达式需要用小括号包围起来,否则会报语法错误。

create index idx1 on t1 ((lower(col1)));

可以通过查询变量tidb_allow_function_for_expression_index知道哪些函数可以用于表达式索引:

> select @@tidb_allow_function_for_expression_index;
+--------------------------------------------+
| @@tidb_allow_function_for_expression_index |
+--------------------------------------------+
| lower,upper,md5,reverse,vitesse_hash       |
+--------------------------------------------+

当查询语句中的表达式与表达式索引一致时,优化器就可以为其选择使用表达式索引:

> select lower(col1) from t1;
> select * from t1 where lower(col1)="a";
> select * from t1 order by lower(col1);
> select min(col1) from t1 group by lower(col1); 

不可见索引

不可见索引(invisible indexes)可以在不删除索引的前提下对优化器“隐藏”索引。

  • 不可见索引不会被查询优化器使用;
  • 不可见索引仍然可以被修改或者删除;
  • 即使用SQL Hint USE INDEX强制使用索引,优化器也无法使用不可见索引;
  • 不允许将主键设为不可见;
  • 通过alter index语句来修改索引的可见性,可以把索引设置为Visible或者Invisible。
> create table t2 (c1 int, c2 int, unique(c2));
> create unique index c1 on t2(c1) VISIBLE;
> alter table t2 alter index c1 INVISIBLE;

索引使用的注意事项

与MySQL相比,TiDB中的索引使用有如下限制:

  • 不支持FULLTEXT、HASH和SPATIAL索引;
  • 不支持降序索引(类似MySQL 5.7);
  • 无法向表中添加CLUSTERED类型的PRIMARY KEY;
  • 不支持删除CLUSTERED类型的PRIMARY KEY;
  • 不支持使用类似MySQL中提供的优化器开关use_invisible_indexes=ON来将所有不可见索引重新设置为可见。

运维技巧

查看索引的Region分布:

-- show table [table_name] index [index_name] REGIONS [WhereClauseOption];
> show table t1 index idx_t1_c1 regions;
### 数据库 Schema 的概念与设计 数据库 **Schema** 是指数据库的整体逻辑结构描述,定义了数据库中的对象及其之间的关系。它是数据库的核心组成部分之一,直接影响到系统的性能、扩展性和易用性[^1]。 #### 1. 数据库 Schema 概述 数据库 Schema 描述了一个数据库的逻辑视图,包括表、字段、索引、约束等内容。在实际应用中,Schema 不仅限于简单的数据存储,还涉及复杂的业务需求建模。例如,在学生宿舍管理系统的设计过程中,虽然功能较为基础,但仍需关注基本的数据操作和安全性问题[^2]。 #### 2. 表结构设计的关键要素 在 TiDB 这样的分布式数据库中,Schema 设计需要特别注意分区策略。每张表会分配一个唯一的 Table ID,并按 Region 划分数据存储范围,默认大小为 96MB。这种设计有助于提高查询效率并支持大规模并发访问[^4]。 以下是表结构设计的一些核心原则: - **字段选择**: 字段应尽可能精简,避免冗余。 - **主键设置**: 主键用于唯一标识记录,通常推荐使用自增整数或 UUID。 - **索引优化**: 合理配置索引可以显著提升查询速度,但也可能增加写入开销。 ```sql CREATE TABLE students ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), dormitory_id INT, INDEX (dormitory_id) ); ``` #### 3. 关系建立与规范化 为了减少重复数据并增强一致性,数据库设计常采用范式化方法。常见的第三范式(3NF)能够有效消除传递依赖,从而简化更新操作。然而,在某些场景下适当反范式化也能带来性能收益。 #### 4. Schema 变更管理 随着项目发展,原有 Schema 往往无法满足新需求,此时就需要对其进行调整。频繁修改元数据不仅增加了维护难度,还会对线上服务造成干扰。为此建议采取如下措施来降低风险[^3]: - 提前规划好未来扩展方向; - 使用版本控制工具跟踪改动历史; - 测试环境充分验证后再部署至生产端。 ### 结论 综上所述,良好的数据库 Schema 设计对于构建高效稳定的应用至关重要。从初始架构搭建到最后运维阶段都需要持续投入精力加以改进和完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GottdesKrieges

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值