在数据库中,用户可以通过建立索引的方式来提升查询性能,当数据表中的数据量越来越大时,则可以通过分区的方式将数据表拆分成若干分片,利用负载均衡将数据分片打散到整个集群中来提高集群整体服务能力。本文主要介绍局部索引、全局索引和唯一索引。
索引类型
OceanBase 数据库主要涉及如下索引类型:
局部分区索引:在创建索引时指定关键字
LOCAL的索引为局部索引,局部索引无须指定分区规则,它的分区属性和主表的属性一致,也会跟随主表的分区操作而发生变更。全局分区索引:在创建索引时指定关键字
GLOBAL的索引为全局索引,全局索引可以按照分区规则进行分表。唯一索引:索引键值具有唯一性,用关键字
UNIQUE表示。前缀索引:当分区索引表的分区键是索引列的左前缀时,该索引称为前缀索引。这不同于 MySQL 长字符串的前缀索引。例如,索引表
idx建立在c1和c2列上,如果该索引表的分区键为c1,那么该索引称为前缀索引;如果该索引表的分区键为c2或者其他列,则称为非前缀索引。非前缀索引:相对于分区前缀索引而言,如果分区索引不是前缀索引,那么称为非前缀索引。
局部索引
局部索引根据分区键划分为局部前缀索引和局部非前缀索引。
局部前缀索引
如果分区键是索引的左前缀,并且索引包含二级分区键,那么这个索引为局部前缀索引。局部前缀索引可以是唯一索引或者非唯一索引。
指定索引键的查询利用局部前缀索引可以唯一定位到一个索引分区,非常适合于结果集比较小,但是需要进行分区裁剪的场合。
例如,表 A 上有个局部索引 idx(c1,c2,c3),主表根据 c1 进行分区,根据局部索引的特性,索引表和主表的分区方式一样,因此 idx 也以 c1 为分区键,根据定义知道这是一个局部前缀索引,当处理指定索引键的查询时,可以通过分区键 c1 的值定位到唯一一个索引分区,大大减少了索引分区的访问。
局部非前缀索引
如果索引表不是局部前缀索引,那就是局部非前缀索引。可能的情况是,索引表的分区键不是索引的左前缀,或者索引没有包含二级分区键。
如果分区键不是索引的子集,那么局部非前缀索引不能是唯一索引。 指定索引键的查询利用局部非前缀索引,不能定位到索引分区,而是需要访问所有索引分区,因此比较适合访问数据量比较大,注重并发的场景。
例如,表 A 上有个局部索引 idx(c1,c2,c3),主表根据 c4 列进行分区,根据定义获知这是一个局部非前缀索引,当用户查询指定索引键时,不能通过分区键定位到索引分区,因此需要访问所有的索引分区才能获得结果,并发执行在这种场景下可以发挥重要的作用。
全局索引
全局索引拥有自己独立的分区定义,不需要跟主表一样。同时,全局索引表的分区也可以进行分裂和合并。 一般情况下,如果主表和全局索引的分区方式完全一样的话,除去具有唯一性的非前缀索引,其他索引建议定义成局部索引,全局索引在分区管理和维护上代价要远远大于局部索引,并且从查询代价、分区裁剪上来说,具有相同分区方式的全局索引和局部索引的效果是一样的。
全局前缀索引
如果全局分区索引的分区键是索引键的左前缀,那么这个索引称为全局前缀索引。
全局前缀索引可以是唯一索引或者非唯一索引。
全局前缀索引只在用 Range 分区时有意义,对于 Hash 分区索引无意义。原因在于,如果是用户选择 Hash 分区索引,那么用户查询模式一定是指定索引键的点查询,索引键如果覆盖分区键的话,那么是否为前缀索引并无意义,都能够通过用户指定的索引键值算出索引分区;如果用户没有指定全部分区键值,Hash 分区索引则需要访问所有的分区数据,而 Range 分区可以进行一定程度的分区裁剪。
全局非前缀索引
OceanBase 数据库不支持全局非前缀索引,全局非前缀索引对于查询优化并没有太多意义。
例如,表 A 上有一个全局索引 idx(c1,c2),idx 通过 c2 进行分区,那么 idx 为一个全局非前缀索引。这种情况下,只有当用户指定全部索引键值的时候才能进行分区裁剪,其他情况均需要扫描所有的索引分区,因此用户没有理由不直接用 c1 键做分区,使用 c1 还能通过前缀过滤进行分区裁剪。
唯一索引
唯一索引可以定义为全局索引或者是局部索引。
唯一索引定义为局部索引的时候,需要满足一定的条件,即索引键要覆盖索引分区键。 例如,表 A 上有个唯一索引 idx(c1,c2,c3),索引表 idx 按照 (c1,c2) 进行分区,这样能保证相同的 (c1,c2) 一定能进入同一分区,只需要在单个分区内维护唯一性。如果索引表 idx 按照 (c2,c4) 进行分区,索引没有覆盖分区键,就不能通过局部索引来保证分区之间索引键的唯一性,这样的局部唯一索引不能被创建。
索引创建策略
在创建索引的时候,需要将用户查询模式、索引管理、性能、可用性等方面的需求综合起来考虑,选择一个最合适自身业务的索引方式。
如果用户需要唯一索引,并且索引键覆盖所有分区键,则可以定义成局部索引,否则需要用全局索引。
如果主表的分区键是索引的子集,则可以采用局部索引。
如果主表分区属性和索引的分区属性相同,建议采用局部索引。
如果用户比较关注索引分区管理的代价,主表分区总是不断进行分区删减时,尽量避免创建全局索引。这是由于主表分区删减操作会导致全局索引变更太大,难以恢复,而且可能会导致索引不可用。
如果用户查询总是指定所有索引分区键值,那么只需要在其他列上建索引,而无须包含分区键,减少维护代价和存储代价;对于不指定分区键的查询,前缀索引更合适做分区裁剪、数据量比较小的场景,非前缀索引一般更适合做分区并行、数据量比较大的场景。
分区选择策略
在进行索引分区的时候,如果单个索引表的数据量太大则需要进行分区,以便查询更好的进行分区并行以及负载均衡。
如果用户查询多为指定索引键的单点查询,从访问分区的数目和访问并发角度来说,Hash 分区和 Range 分区的查询代价都相差不大。但是如果数据具有热点区的话,用 Hash 分区能更好的避免热点问题。
如果用户的查询多为指定索引键的范围查询,则从访问的分区数目来说,Range 分区要优于 Hash 分区;但从并发的角度考虑,Hash 分区可以更好的利用并发查询的优势,在查询结果集很大的情况下,Hash 分区的并发查询应该能有更大的性能优势。
查询时索引选择策略
如果用户更注重分区裁剪的话,选择前缀索引更好,在用查询过滤条件里面指定分区前缀的值能够最大程度的进行分区裁剪,减少索引分区数据的读取。
如果用户注重吞吐量,访问的数据量比较大时,选择非前缀索引更能提高性能,可以通过分区并行的方式来解决分区键上的范围查询;局部非前缀索引可以并发访问所有的分区,但是局部前缀索引会进行分区裁剪,由少量分区处理大量的数据集合,响应时间也许会比并发查询要差一些。




