全局二级索引(GSI)

本文介绍了全局二级索引的原理、特性和类型。

概述

全局二级索引(Global Secondary Index,简称GSI)是PolarDB-X中的一项重要特性,相比于本地二级索引,全局二级索引中的数据按照指定的分区方式分布在各个存储节点上。通过全局二级索引,用户能够按需增加分区维度、提供全局唯一约束等。

原理和特性

在分布式数据库的分区表中,数据被按照建表时指定的分区键进行路由和存储,因此包含分区键的查询可以快速定位到一个具体分区,而其它查询则需要全分区扫描。对于分布式数据库而言,全分区扫描除了会增加慢查询数量降低系统吞吐,还可能导致系统丧失线性扩展能力,因此需要尽量避免全分区扫描。

举例来说,当查询语句包含分区键时,单个逻辑查询只会被路由到一个具体分区,假设有N个存储节点,平均来看1个逻辑查询给单个存储节点施加的查询负载仅为1/N;当查询语句不含分区键时会引起全分区扫描,即单个查询会被路由到所有分区,平均来看1个逻辑查询给单个存储节点施加的查询负载为1,单个存储节点的性能上限就是整个分布式数据库的性能上限,系统失去了线性扩展能力。

PolarDB-X提供了全局二级索引以解决上述问题。在PolarDB-X中,GSI可以视为一个特殊的分区表,它冗余了主表上的部分列的数据。与普通分区表类似,GSI按照用户指定的分区规则水平拆分为若干个分区,分布在各个存储节点上。当一个查询不含主表分区键但包含GSI的分区键时,PolarDB-X通过先检索GSI的单个分区,然后回表的方式避免全分片扫描。

image

PolarDB-X使用分布式事务维护主表和GSI之间数据的强一致。

此外,GSI还支持以下特性:

  • 支持在线变更,创建、删除GSI无需锁表。

  • 用户可自定义覆盖列,减少回表操作开销。

  • 支持invisible index。

类型

全局二级索引(Global Secondary Index 简称GSI)

全局二级索引可以提供和主表不同的分区方式,当查询SQL的条件中未包含主表的分区键但包含了GSI的分区键时,仍可以避免全分区扫描。

比如对于下面的用户表user_tbl, 如果既希望按照user_id查询,又希望按照用户名name查询,就可以建立全局二级索引 g_i_name,在按照用户名name查询的时候避免全分区扫描。

CREATE TABLE user(
 user_id bigint,
 name varchar(10),
 addr varchar(30),
 GLOBAL INDEX `g_i_name` (name) PARTITION BY HASH(name),
 PRIMARY KEY(user_id)
) PARTITION BY KEY(user_id);

全局唯一索引(Unique Global Secondary Index 简称UGSI)

全局唯一索引是特殊的GSI,它不仅有普通GSI的性质,还能实现全局唯一约束。

比如对于下面的用户表user2,如果要求用户手机号全局唯一,那么可以建立一个phone字段为索引键的UGSI。

CREATE TABLE user2(
 user_id bigint,
 phone varchar(20),
 addr varchar(30),
 UNIQUE GLOBAL INDEX `g_i_phone`(phone)  PARTITION BY HASH(phone), 
 PRIMARY KEY(user_id)
) PARTITION BY KEY(user_id);

全局聚簇索引 (Clustered Global Secondary Index 简称Clustered GSI)

全局聚簇索引是特殊的GSI,它默认冗余了主表的全部列(该索引所占磁盘空间等于主表所占磁盘空间)。如果既希望避免全分区扫描,又希望避免回表开销,可以使用全局聚簇索引。

比如对于订单表order_tbl,希望支持按照user_id或order_id来查询,且希望避免用user_id查询订单时回表,就可以创建一个以user_id为索引键的全局聚簇索引cg_i_user。以user_id为条件查询订单信息时,PolarDB-X会将查询路由到cg_i_user上的一个特定分区,又因为cg_i_user上有主表的所有数据,因此无需回表。

CREATE TABLE order_tbl(
 order_id bigint,
 user_id bigint,
 addr varchar(30),
 info text,
 create_time datetime,
 CLUSTERED INDEX `cg_i_user`(user_id) PARTITION BY HASH(user_id), 
 PRIMARY KEY(order_id)
) PARTITION BY KEY(order_id);

性能

全局索引对读写性能的影响,与具体业务场景有比较大关系,本质上是牺牲一部分写入性能换取读性能的大幅提升,下面以Sysbench场景为例,展示该场景下GSI对读写吞吐的影响。

读取性能数据

Table

Threads

Sysbench SeIect_random_ranges 场景

Sysbench SeIect_random_points 场景

QPS

Avg Latency

95% Latency

QPS

Avg Latency

95% Latency

分区表

128

2769.17

46.21

99.33

5226.99

24.48

42.61

256

3415.64

144.97

144.97

5476.76

46.73

82.96

512

3272.46

156.31

257.95

5290.67

96.72

179.94

1024

2453.16

416.12

539.71

5165.31

198.07

404.61

分区表+GSI

128

9662.11

13.24

25.28

22584.89

5.66

9.73

256

10431.73

24.52

51.02

25558.26

10.01

17.95

512

15634.51

32.72

73.13

27116.56

18.86

39.65

1024

229448.76

44.53

108.68

32509.87

31.43

73.13

增加一个全局索引:

  • Select_random_ranges场景QPS:3415.64 -> 22948.76, range查询QPS提升571%。

  • Select_random_points场景QPS:5476 -> 32509.87, 点查QPS提升493%。

结论:通过全局索引可以提升Sysbench在索引列k上的查询性能。

写入性能数据

Table

Threads

Sysbench SeIect_random_ranges 场景

Sysbench SeIect_random_points 场景

QPS

Avg Latency

95% Latency

QPS

Avg Latency

95% Latency

分区表

128

86548.12

8.87

10.27

113655.28

22.52

26.2

256

115774.71

13.26

19.29

149677.52

34.19

44.17

512

143928.94

20.51

34.95

14555.16

70.28

112.67

1024

153501.7

39.53

70.55

132150.69

131.58

287.38

分区表+GSI

128

52069.22

14.25

18.28

90074.59

28.41

33.72

256

66250.79

23.17

32.53

114420.32

44.73

57.87

512

75700.74

39.1

59.99

111093.61

92.09

142.39

1024

76557.94

80.14

134.9

101828.32

182.51

350.33

增加一个全局索引:

  • WriteOnly场景QPS:153501.7 -> 76557.94,写入QPS下降50%。

  • ReadWrite场景QPS:149677.52 -> 114420.32,读写混合QPS下降23%。

结论:增加一个全局索引,Sysbench写入性能有下降。

相关文档

如何使用全局二级索引

聚簇索引