-
Notifications
You must be signed in to change notification settings - Fork 171
Index Selection
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没有实现代价模型,因此所有的索引选择都是基于规则的:
- 如果有FULLTEXT则最优先匹配
- 如果order by可以用到索引,则优先使用该索引
- 全部命中比部分命中优先
- 优先级:主键 > 唯一 > 普通
如果发现BaikalDB查询很慢,可以尝试用hint:use index(index_name)来强制指定索引。示例:select * from b use index(primary) where id > 5 and x = 3;
联合索引的建立顺序很重要,遵循最左前缀匹配原则:只有前缀相等的情况,才会使用下一级索引(范围匹配也不行)。
举例:
-
索引(a,b),比如where b = 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 全局索引,会先根据索引查找到主键,再通过主键查找到数据。