1
2
1
0
专栏/.../

一文概述TiDB中的索引类型

 数据源的TiDB学习之路  发表于  2024-04-02

在关系型数据库中,索引是一种非常常用的数据结构,它通过缩小数据扫描的范围来提升查询时间和减少资源消耗。TiDB数据库中支持多种类型的索引,本文简要总结TiDB中的索引类型。

从大类上来划分,TiDB的索引可以分为主键索引二级索引。主键索引就是在主键字段上建立的索引,二级索引是在非主键字段上建立的索引,下面通过示例简要描述二者。

主键索引

TiDB中的主键索引有两种,即聚簇索引非聚簇索引

聚簇索引

聚簇索引在有些数据库中也称为索引组织表,简单理解就是说表的数据在存储的时候就是按照主键字段进行排序存储的。聚簇索引是TiDB v5.0开始支持的特性,由于表按主键排序,因此不需要额外增加索引结构就可以实现按主键字段进行快速高效的查询和过滤。

较新的TiDB版本中默认创建的主键索引即为聚簇索引,这通过查看表结构可以看到,如下图所示中的/*T![clustered_index] CLUSTERED */代表创建出来的是聚簇索引。

image.png

非聚簇索引

非聚簇索引是通过额外的索引条目来实现数据的排序,表本身的数据存储并不是按主键字段排序的。在较新的TiDB版本中,如果想定义一个主键为非聚簇索引,需要显式在建表语句中定义NONCLUSTERED关键字,如下图所示。

image.png

TiDB底层存储是KV结构,在聚簇索引中TiDB是把主键字段直接映射到Key中,但非聚簇索引中Key是由TiDB内部隐式分配的_tidb_rowid构成,主键本质是唯一索引。从下图我们也可以发现,非聚簇索引表中可以查询到_tidb_rowid这个隐含字段,而聚簇索引表中则不存在这个隐含字段。

image.png

聚簇与非聚簇索引的对比

聚簇索引的优势

既然TiDB在v5.0版本引入聚簇索引并设置为默认选项,那么聚簇索引肯定有其独特的优势,包括:

  1. 插入数据时,减少一次索引数据写入
  2. 等值查询时,减少一次索引数据读取
  3. 范围查询时,减少多次索引数据读取

简单来说,使用聚簇索引,在大部分场景下,写入和查询时都有一定的性能提升

聚簇索引的劣势

  1. 当批量插入大量取值相邻的主键时,可能产生表上较大的写热点问题
  2. 当查询只需要获取少量字段时,由于要把整行数据取出可能导致性能反而不如非聚簇
  3. 不支持在建表后添加或删除聚簇索引

聚簇与非聚簇的默认行为控制

前面提到在当前的TiDB版本中,主键默认创建为聚簇索引,这是由参数tidb_enable_clustered_index来控制的,ON表示所有主键默认创建为聚簇索引,OFF则表示默认创建为非聚簇索引。

因此,如果想针对单张表创建为非聚簇索引,我们使用上述所说的在表定义中添加NONCLUSTERED关键字。如果想全局控制这个行为,可以直接修改tidb_enable_clustered_index这个系统参数。比如说,我们设置这个变量的global范围为OFF,那后续创建的所有主键表都是非聚簇索引。

image.png

正如上面截图所示,会话1中将变量的global范围设置为OFF,在会话2中创建一个有主键的表,查看表结构发现默认创建为NONCLUSTERED属性。

二级索引

二级索引是在非主键字段上创建的索引,二级索引可以在建表语句中一起创建,也可以在事后单独创建二级索引。

建表时创建

在建表语句中,可以通过 KEY index_name (column_name,…) 或者 INDEX index_name (column_name,…) 来定义索引,两者含义相同。

image.png

单独事后创建

通过标准的CREATE INDEX语法创建,示例如下:

image.png

 

主键索引和二级索引是从选取哪个字段的维度来区分的,TiDB也支持一些特殊场景使用的索引,如唯一索引、表达式索引、多值索引等。

唯一索引

唯一索引在功能上等同于普通索引+唯一性约束,使用CREATE UNIQUE INDEX创建。如果一个字段被定义为唯一索引,那么这个字段中的每个值必须要唯一才行,对于有冲突的数据是无法被写入的。

image.png

上述示例中,当b字段添加了唯一索引之后,插入两条相同的值时第2条插入语句报唯一性错误。

表达式索引

在数据库查询过滤时,有些时候我们不是直接基于值本身来过滤,而是基于值转换后的过滤,比如说要查询所有值为’abc’的数据,但并不区别大小写,‘ABC’、’Abc’等都满足条件。查询语句大概会写成SELECT … FROM .. WHERE lower(col1)=’abc’,但如果我们在col1上建立一个普通的索引,这种情况下执行计划是无法选择走索引扫描的。

因此,我们需要创建一个表达式索引,有了表达式索引,上述语句便可以走索引扫描。

image.png

注意:在创建表达式索引时,在表达式外面需要多写一层括号,如上图所示,create index idx_lower on t_functbl((lower(b))); 如果这里少一层括号会报语法错误。

组合索引

所谓组合索引,就是说索引里面由多个字段组合而成,因为有些时候我们需要根据多个维度来筛选我们想要的数据,比如会按照姓名和年龄两个条件来找到某个人的信息。

image.png

多值索引

多值索引跟组合索引是两个概念,多值索引是一种定义在数组列上的二级索引,它主要用于索引JSON数组,以下示例来自TiDB官网。

image.png

覆盖索引

覆盖索引通常是指为了索引回表导致的额外开销,把查询所需要的所有字段都添加到索引中。对于kv存储的数据库,通常的覆盖索引会把查询条件(即“谓词”)涉及的字段放在索引的Key中,而把谓词以外的字段存放在键值对的Value中。

笔者之前使用过的一款数据库中使用覆盖索引的语法为:

CREATE INDEX index-name ON tablename(col1,col2..) WITH COLUMNS(col3,col4…)

通过上述语法把col1、col2..映射到Key,而把col3、col4..映射到Value中。

TiDB中的覆盖索引并没有上述的实现方式,而是较为简单的采用了组合索引的方式代替,即把查询所有字段都映射到Key值中,

CREATE INDEX index-name ON tablename(col1,col2,col3,col4…)

那么假如查询语句如下:

SELECT col3,col4 FROM .. WHERE col1=.. AND col2=..

由于索引中包含语句中所有需要的字段信息,因为只需要查询索引即可,而不需要再回表,这在TiDB中称为覆盖索引优化 (covering index optimization),参考 用 EXPLAIN 查看索引查询的执行计划 | PingCAP 文档中心

不可见索引

不可见索引其实不能算是一种索引类型,应该算是索引的一种属性。TiDB默认创建的索引都是可见的,有时候我们发现表上创建了过多的索引而且SQL又使用了错误的索引,这时候我们就想临时把索引设置为不可见。因为如果直接删除索引的话再创建时可能会非常耗时,我们仅仅是想让优化器看不到这个索引而已,索引的顺序还是会跟着表的数据变动而变化。在这种场景下,我们可以将索引设置为不可见索引,如下图所示。

image.png

 

1
2
1
0

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

评论
暂无评论