【QianBase V1】性能优化之建表

驿道 2022-03-01 17:53:24

通常情况下,任何数据库在涉及到性能优化时候,除了参数设置之外,剩下较为关键部分就是如何建立更高效更合理的表结构,QianBase V1也不例外。

本文主要介绍V1常见的建表方式,不一样的建表方式,对SQL执行耗时,会有不同的影响,整体总结而言,主要有下述几个关键选项:

  1. 主键或者Store By键
  2. 分区键&分区个数
  3. Division By
  4. Replicate n ways
  5. HBase Options

 注:上述5类选项,建表时都为可选项


主键&Store By键

QianBase V1中的表是按簇键(Cluster Key)顺序存储的,所以我们通常认为QianBase的表本身也是一个索引。主键是具有唯一约束的特殊簇键。我们应优先选择那些在WHERE过滤中经常用到的字段作为主键或簇键字段。主键或簇键的设计一般要遵循以下规则, 

主键或簇键可以包含一个或多个字段,字段的先后也是非常严格的。

键应尽可能短,尤其是表不使用Aligned Format的时候。 

[1] Key中应尽量避免使用CHAR/VARCHAR类型,尤其是比较长的字段。如果CHAR/VARCHAR字段是UTF8格式,尽量使用”n BYTES”而非”n CHARS”,避免为每个字符分配4个bytes。 

[2] 不要因为唯一性要求而把Key设置在太多字段上。唯一性可以通过以下一些方式实现。 

如何不适合创建Primary Key,下面提供一些方法保证Key的唯一性。 

[1] 使用Store By代替,Store By会生成一个隐藏列”SYSKEY”作为Key的最后一个字段。SYSKEY在每一行都是唯一的。 

[2] 使用一个CHAR(16)类型的字段作为Key的最后一个字段。当表有数据插入时,使用unique_id()函数生成一个唯一值。 

[3] 使用IDENTITY。对于批量插入如Bulk Load或Upsert需要把Cache Size设置大一些(默认25太小)。

注:

QianBase V1 在定义表结构时,支持主键/Store by键的字段为空(但实际数据库server端会存一个非空串),例如primary key nullable(col1,col2)

分区键&分区个数

QianBase V1允许在主键或簇键的子集上进行Salting(盐粒分布)。Salting的目的是把数据均衡分布到不同的Region服务器中,从而避免查询热点问题(只从单个Region服务器查询)。Salting会生成一个hash值作为Key的前缀。如果查询语句的WHERE条件中包含Salt中的字段,那么查询很可能只需要查询对应的Region区域。默认情况下,Salting字段即主键或簇键所有字段,不过也可以选择部分字段作为Salting字段。选择Salting键的原则一般如下:

Salting Key字段组合的UEC必须至少是表partition个数的200倍或更多。表中列的UEC可以通过先更新表的统计信息再通过SHOWSTATS查询。UEC可以通过showstats查看。

Salting Key不能要严重的数据倾斜。数据倾斜会导致数据分布不均从而导致查询性能问题。可以使用SELECT "_SALT_", count(*) from group by 1 order by 2; 来查看数据倾斜问题。

最具有代表性的OLTP查询是WHERE条件中Salting Key中的每个字段都有”=”限制,这样查询会只定位到某一个或很少的几个region上面。 

基于上述3个条件,尽量选择一个最少的Salting Key的字段组合。 

对于join较多的表,根据经常使用的join字段对表进行Salting,这可以使得只关联到指定的region。

对于高并发场景,应该用高并发查询字段进行Salting

HBase表在表数据量增长时会自动Split。如果能在建表的时候进行预分区,可以防止在业务繁忙的时候频繁Split。 

表的region个数可以通过region最大尺寸推断(默认一个region大小是10G,建议增大到100G)。可以在Salt子句中定义建表时创建多少个region。 

对于表的扫描,并发度的最大值等于partition的数量。 

设置分区数一般原则如下: 

[1].分区数要根据节点数、磁盘数及表的据量综合考量,不宜随意指定,一般是节点数的N倍(最佳实践为1=<N<=10),一般设置表的最大region数为8到10*nodes,比如总共有6个nodes,那么最大的region数为8*6=48或者60个。 

[2].对于小表(如 <100万)可不分区;对于大表(如>1000万)可适当分区,分区数一般为节点的 n倍( n<=10),生产环境下,每个分区的数据在百万或10G~100G)较合适  

[3].单个HBase RS个尽量不超过1000个region,否则HBase可能运行不稳定。 

[4].表的分区数可以自适应数据量的变化,使用SALT USING 192 PARTITIONS IN 4 REGIONS语句,初始4个分区,最大192个分区。 

注:

1. 在有主键或者Store By键前提下,才能谈分区键或者分区数

2. 分区键可以是主键或者Store By键中字段的单个或者组合

3. 如果primary key (a,b) salt using 10 partititon on (a),那数据实际是按照"_SALT_",a,b顺序排序

4. 第3点中,"_SALT_"列的值,对a字段的取值经哈希计算得出

5. 如果salt using 10 partititon但不注明on哪个字段,那会按照主键所有列来hash预分区

Division By

 division by属于一种数据属于相对比较取巧的数据排序方式,在某些分析场景的SQL下,可以节约一个索隐引

简单而言,如果主键为(A,B,C)division by (DATE_TRUNC('DAY',C)),那实际数据排序会按照DATE_TRUNC('DAY',C),A,B,C四个列来排序

并且,只有在有主键或者Store by键时,才能新建division by选项

注:

1. Division By也是在有主键或者Store By键前提下

2. Division by通常用在时延要求大于100ms的sql中

3. primary key (a,b) salt using 10 partititon on (a) division by (date_trunc('day',b)),那数据实际是按照"_SALT_","_DIVISION_",a,b顺序排序

Replicate n ways

[1].Replicate表,也可称为复制表

[2].Replicate 4 ways,意味该表会建立成4个分区,每个分区都有一样的数据,并编号0-3。建议复制数与节点数相同。

[3].建议只读表设置为replicate表,或者读占绝大部分场景

[4].作用:降低单分区小表的并发读热点

注:

对应的索引也会继承replicate属性

HBase Options

QianBase V1沿用了几种常见的HBase Option,例如:

[1] DATA_BLOCK_ENCODING -- 数据块编码方式

[2] COMPRESSION -- 压缩方式

[3] MEMSTORE_FLUSH_SIZE -- memstore刷磁盘的阈值大小

[4] PREFETCH_BLOCKS_ON_OPEN -- blockcache预期选项

[5] IN_MEMORY -- 数据是否优先放缓存

[6] DURABILITY -- WAL方式,可选的值有ASYNC_WAL, FSYNC_WAL, SKIP_WAL,SYNC_WAL(默认值),USE_DEFAULT等

 

【下面列举几种常用建表的案例】

案例一:无主键或者store by键,无HBASE_OPTION 

[1].该表没有HBASE_OPTION

[2].数据不会压缩

[3].表只有一个分区,在表大小达到分区的临界值(100G之后),HBase会将该分区自动分裂成2个分区

[4].一般不推荐该种表结构

[5] 该表在数据库端后台,会自动生成唯一的key,即syskey,作为K-V存储中的键值部分(syskey可以直接通过select syskey,* from trafodion.seabase.no_hbopt查询出来,并且数据按照syskey排序),这里syskey是伪列

案例二:仅有HBase Option

[1].对于数据量较小的表,不需要建立分区

[2].该表没有主键

[3].一般不推荐该种表结构

[4] 该表在数据库端后台,也会自动生成唯一的key,作为K-V存储中的键值部分

案例三:有主键,但不分区

[1].该表定义了主键

[2].该表未预分区(默认1个分区)

[3].适用于小表,并且对该表并发查询较低情况

[4].此时数据按照(b,a)排序,实际存储主键也是(b,a)

案例四:主键可为空

[1].该表主键列可以为空

[2].如果因业务无法确定是否字段为空,但又碍于需要建立主键并预分区,可以使用该做法

[3].该做法才易鲸捷实际场景使用并不冷门

[4].此时数据按照("_SALT_",b,a)排序,实际存储主键也是("_SALT_",b,a),这里"_SALT_"是伪列,并且可以根据(b,a)组合的值哈希算出来

[5].该表预分成了8个分区

案例五:正常主键

[1].通常情况下,如果表有主键,请设置主键

[2].分区数一般视数据量与节点数而定。物理机条件下,一般按照100万数据量1个分区。如果节点数是3,那500万数据量可以分成6个分区。

[3].极端情况下,表数据量非常大,那可以考虑1000万数据量一个分区,分区数一般不要超过节点数*10

[4].此时数据按照("_SALT_",b,a)排序,实际存储主键也是("_SALT_",b,a),这里"_SALT_"是伪列,也可以根据(b,a)组合的值哈希算出来

 案例六:Store by

[1].如果表数据量较大,需要分区,但是并不能设置主键(比如说有重复值),请使用store by方式来分区

[2].分区键a请选择UEC较大的列,UEC需要超过分区数200倍

[3].a列的UEC代表a列中不同值的个数,也就是count(distinct)的数量,表示该列数据分散情况

[4].默认store by的字段不可为空

[5].此时数据按照("_SALT_",b,a,syskey)排序,实际存储主键也是("_SALT_",b,a,syskey),这里"_SALT_"与syskey都是伪列,并且"_SALT_"可以根据b的值哈希算出来

 案例七,复杂建表结构

[1].如果同时考虑写入性能、基于ID列查询、基于DATATIME列查询三种情况,可以考虑如上表结构

[2].该表结构,不会创建隐式索引

[3].此时数据按照("_SALT_","_DIVISION_",ID,DATATIME)排序,实际存储主键也是("_SALT_","_DIVISION_",ID,DATATIME),这里"_SALT_"与"_DIVISION_"都是伪列,并且"_SALT_"可以根据b的值哈希算出来,"_DIVISION_"根据DATE_TRUNC('DAY',DATATIME)计算

...全文
338 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

17

社区成员

发帖
与我相关
我的任务
社区描述
易鲸捷数据库讨论专区
其他 企业社区
社区管理员
  • 么哒meda
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧