Skip to content

Index Selection

liguoqiang edited this page Jun 4, 2020 · 9 revisions

索引选择的规则

主键的选择

BaikalDB内部是有分片的,而且分片是按照主键范围划分。主键是聚簇索引。(注:聚簇索引的顺序就是数据的物理存储顺序,而对非聚簇索引的索引顺序与数据物理排列顺序无关。)

除了主键外,BaikalDB可选local index或global index。

local index:索引数据与主表数据存储在一起,并且跟随主表的分片来分布。

global index:索引数据与主表数据不存储在一起,索引数据有自己的分片规则。 一般来说,在表数据量大的时候(亿行以上),查询的时候每次都会带上主键前缀的,可以选择local index。查询的时候不带主键前缀,可以选择global index。

举个例子:

  • 一个表的单主键是id,id范围是0-100,那么可能的分片是[0,20),[20,80),[80,∞)
  • 一个表的联合主键是userid(0-100),planid(0-10000),可能的分片是[0-0,30-1000),[30-1000,80-3000),[80-3000,80-7000),[80-7000,∞)。其中80号用户是大客户,可能分散在多个分片中可并行计算。

主键的选择影响了数据的分片和物理分布,合理的主键选择可以使得相关数据物理上也聚集在一起,能够大幅提升性能,主键的选择是最重要的

好的案例(学校->学院->班级):

  • school表主键可设置为schoolid。
  • college表主键可设置为schoolid,collegeid。
  • class表主键可设置为schoolid,collegeid,classid

上面的主键选择,可以让同一个相同的学校数据全部数据在物理上也聚集在一起。并且对于大的学校来说,会自动进行分裂成多个分片,可以并行计算。

那么对于一条sql,BaikalDB是如何选择正确的分片的呢?

对于local index,where条件中只有命中了主键前缀,才能选择到合适的分片。否则就是广播查找! 对于global index,where条件中如果命中了global前缀,才能选择到合适的索引分片。然后通过解析的主键查询主表。

[0-0,30-1000),[30-1000,80-3000),[80-3000,80-7000),[80-7000,∞),以这些分片举例,where schoolid=15,系统能选到0分片。where schoolid=80,系统会选到1,2,3分片。where schoolid=80 and collegeid=5000,系统会选到2分片。

注意:索引的选择与分片的选择无关,不是说选择到了分片就一定会用主键索引的。上述分片,如果还有个unique索引unitid,where schoolid=15 and collegeid=700;分片可以选择到0,索引可以选择到unitid。

索引的选择

目前BaikalDB没有实现代价模型,因此所有的索引选择都是基于规则的:

  1. 如果有FULLTEXT则最优先匹配
  2. 如果order by可以用到索引,则优先使用该索引
  3. 全部命中比部分命中优先
  4. 优先级:主键 > 唯一 > 普通

如果发现BaikalDB查询很慢,可以尝试用hint:use index(index_name)来强制指定索引。示例:select * from b use index(primary) where id > 5 and x = 3;

联合索引的建立顺序很重要,遵循最左前缀匹配原则:只有前缀相等的情况,才会使用下一级索引(范围匹配也不行)。

举例:

  1. 索引(a,b),比如where b = 2;则不符合最左前缀原则,无法使用该索引。

  2. 索引(a,b,c,d),比如where a = 1 and b = 2 and c > 3 and d = 4;则d用不到索引。如果索引是(a,b,d,c),则可以用到全部索引列。

索引类型

类型 中文 描述
I_PRIMARY 主键索引 主键聚簇,可联合,不允许NULL
I_UNIQ 唯一索引 可多列,不可重复,不允许NULL(local index,在不同分片上可能会重复)
I_KEY 索引 可多列,可重复,不允许NULL
I_FULLTEXT 全文索引 只能单列,可选wordrank/wordseg/单字切词,只支持gbk,允许NULL
I_RECOMMEND 推荐索引 图片推荐专用索引,外部用户可忽略

与MySQL不同,BaikalDB天然顺序写,无需指定无意义的顺序id作为主键。一般情况使用联合主键会获得更好的性能。

自增列

BaikalDB一个表只允许指定一个自增列,并且取消了索引限制,可以指定在任意的整数类型列上。

insert时如果指定了自增列的值,并且这个值大于系统分配的最大值,则系统最大值会更新为此值。

由于是分布式系统,使用自增id分配需要网络开销,建议批量insert降低开销。

不建议使用自增列,支持自增列只是为了和MySQL兼容。

不建议使用唯一的自增列作为主键,会导致写压力在最后一个节点,会影响分布式系统性能。

如果非要使用自增列,建议联合索引后缀列使用自增列,例如主键指定为schoolid,collegeid,collegeid可设置为自增列。

举例说明

CREATE TABLE TEST.table (
  id bigint(20) NOT NULL 
  clktype tinyint(1) NOT NULL DEFAULT '0' ,
  time bigint(20) NOT NULL ,
  clk int(10) NOT NULL DEFAULT '0' ,
  cv int(10) NOT NULL DEFAULT '0' ,
  addcv int(10) NOT NULL DEFAULT '0' ,
  ctime datetime NOT NULL ,
  mtime datetime NOT NULL ,
  PRIMARY KEY (id, clktype, time),
  key index_id_time (id, time),
  KEY GLOBAL index_time (time)
) ENGINE=Rocksdb DEFAULT CHARSET=gbk AVG_ROW_LENGTH=100 COMMENT='{"resource_tag":"cluster1", "namespace":"NS"}';

如上表所示

对于where id=3 and clktype = 5 and time >= 11; where id=3 and clktype = 5; where id=3;这些条件均可以用到主键索引。并且还会根据主键前缀来选择分片。

对于where id=3 and time<222;可以用到index_id_time 索引。并且可以根据id字段来选择合适的分片。

对于where time = 333;则可以用到index_time 全局索引,会先根据索引查找到主键,再通过主键查找到数据。