Impala SQL on Kudu优化(二)

3 篇文章 0 订阅
2 篇文章 0 订阅

1、前言

        上一篇 Impala SQL on Kudu优化谈到了如何从执行计划的角度来优化查询SQL,感兴趣的同学可以去回看关注下(Impala SQL on Kudu优化(一)_一个数据小开发的博客-CSDN博客),本篇优化将主要从建表层面来谈谈优化。

2、概念&理念

        官网上对分区表也有一些表述,大家可以看看。Apache Kudu - Using Apache Kudu with Apache Impala

        根据主键列上的分区schema,表被划分为tablets。每个tablets至少有一个tablet服务器。理想情况下,一个表应该被划分为分布在多个tablets服务器上的tablet,以最大化并行操作。您使用的分区schema的细节完全取决于您存储的数据类型以及如何访问它。有关Kudu中schema设计的完整讨论,请参阅Schema设计。
        Kudu目前没有在创建表后拆分或合并tablets的机制。在创建表时,必须为它提前提供分区模式。在设计表时,考虑使用主键,这将允许您将表数据平均划分到不同的的tables中并保持数据匀速增长。
        您可以使用Impala的partition BY关键字对表进行分区,该关键字支持RANGE或HASH分布。分区方案可以包含零个或多个HASH定义,后面跟着一个可选的RANGE定义。RANGE定义可以引用一个或多个主键列。基本分区和高级分区的示例如下所示。

2.1 分区类型描述

2.1.1 RANGE分区(PARTITION BY RANGE)

        可以为一个或多个主键列指定范围分区。Kudu中的范围分区允许基于所选分区键的特定值或值的范围来拆分表。这允许您平衡读和写的并行度和效率。

        假设您有一个包含state、name和purchase_count列的表。下面的示例创建50个tablets,每个美国州一个。

CREATE TABLE customers (
  state STRING,
  name STRING,
  purchase_count int,
  PRIMARY KEY (state, name)
)
PARTITION BY RANGE (state)
(
  PARTITION VALUE = 'al',
  PARTITION VALUE = 'ak',
  PARTITION VALUE = 'ar',
  -- ... etc ...
  PARTITION VALUE = 'wv',
  PARTITION VALUE = 'wy'
)
STORED AS KUDU;

注意事项:

如果在一个值单调递增的列上按范围划分,则最后一个tablet将比其他tablet增长得大得多。此外,所有插入的数据都将一次写入单个tablet,这限制了数据摄取的可伸缩性。在这种情况下,可以考虑使用HASH来代替RANGE。

 2.1.2 HASH分区(PARTITION BY HASH)

        不是通过显式的范围分布,或者结合范围分布,你可以通过哈希分布到特定数量的“桶”中。您可以指定要分区的主键列,以及要使用的存储桶的数量。行是通过散列指定的键来分布的。假设被散列的值本身没有明显的倾斜,这将有助于将数据均匀地分布到各个桶。

        您可以指定多个定义,也可以指定使用复合主键的定义。但是,一个列不能在多个散列定义中提到。考虑两列,a和b: *  √  HASH(a), HASH(b) *   √   HASH(a,b) *  X  HASH(a), HASH(a,b)

        如果主键值均匀地分布在它们的域中,并且没有明显的数据倾斜(如时间戳或串行id),那么哈希分区是一种合理的方法。

        下面的示例通过哈希id和sku列来创建16个tablets。这将在所有16个tablets上进行书写。在本例中,对一系列sku值的查询可能需要读取全部16个tablets,因此这可能不是这个表的最佳模式。有关扩展示例,请参阅高级分区。

CREATE TABLE cust_behavior (
  id BIGINT,
  sku STRING,
  salary STRING,
  edu_level INT,
  usergender STRING,
  `group` STRING,
  city STRING,
  postcode STRING,
  last_purchase_price FLOAT,
  last_purchase_date BIGINT,
  category STRING,
  rating INT,
  fulfilled_date BIGINT,
  PRIMARY KEY (id, sku)
)
PARTITION BY HASH PARTITIONS 16
STORED AS KUDU;

注意事项:

不指定列的PARTITION BY HASH是通过对所有主键列进行散列来创建所需数量的bucket的快捷方式。

2.1.3 高级分区(Advanced Partitioning)

        您可以组合HASH和RANGE分区来创建更复杂的分区模式。您可以指定零个或多个HASH定义,然后是零个或一个RANGE定义。每个定义可以包含一个或多个列。虽然枚举每个可能的分布模式超出了本文的范围,但有几个示例说明了其中的一些可能性。

a)Hash和Range一起用的分区

        考虑上面的简单哈希示例,如果您经常查询一系列sku值,您可以通过结合哈希分区和范围分区来优化示例。

        下面的示例仍然创建了16个tablet,首先将id列散列为4个bucket,然后根据sku字符串的值应用范围分区将每个bucket分割为4个tablet。写操作至少分布在4个平板电脑上(可能多达16个)。当查询连续的sku值范围时,很有可能只需要从四分之一的平板读取即可完成查询。

CREATE TABLE cust_behavior (
  id BIGINT,
  sku STRING,
  salary STRING,
  edu_level INT,
  usergender STRING,
  `group` STRING,
  city STRING,
  postcode STRING,
  last_purchase_price FLOAT,
  last_purchase_date BIGINT,
  category STRING,
  rating INT,
  fulfilled_date BIGINT,
  PRIMARY KEY (id, sku)
)
PARTITION BY HASH (id) PARTITIONS 4,
RANGE (sku)
(
  PARTITION VALUES < 'g',
  PARTITION 'g' <= VALUES < 'o',
  PARTITION 'o' <= VALUES < 'u',
  PARTITION 'u' <= VALUES
)
STORED AS KUDU;

b)两个不同字段列的Hash分区

        再次展开上面的示例,假设查询模式是不可预测的,但您希望确保写分布在大量的tablets 上。您可以通过对两个主键列进行散列来实现整个主键的最大分布。

CREATE TABLE cust_behavior (
  id BIGINT,
  sku STRING,
  salary STRING,
  edu_level INT,
  usergender STRING,
  `group` STRING,
  city STRING,
  postcode STRING,
  last_purchase_price FLOAT,
  last_purchase_date BIGINT,
  category STRING,
  rating INT,
  fulfilled_date BIGINT,
  PRIMARY KEY (id, sku)
)
PARTITION BY HASH (id) PARTITIONS 4,
             HASH (sku) PARTITIONS 4
STORED AS KUDU;

总结:

言外之意,也就是说,如果是那种分布比较均匀的数据,譬如说按照全国省份统计人口数,就可以使用range分区来存储。反之,如果是存储身份证号码这种明细数据,用hash分区来存储比较合适。

对于大型表,比如事实表,目标是在集群中有多少核心就有多少tablets 。

对于小型表,例如维度表,确保每个tablets 至少有1gb大小。

通常,在当前的实现中,要注意tablets 的数量限制了读取的并行性。如果tablets 的数量远远超过核心数量,那么效率和性能可能会递减。

 写在最后,如果需要对Kudu表Range分区增加值,可以执行如下Impala语法

ALTER TABLE table_name ADD RANGE PARTITION col = col_vaule;

3、实战演练&验证

        我们在日常生产中有很多系统会产生日志数据,日志数据到底应该怎么存,才会最大化的提升Kudu的性能?

        首先分析下,系统日志,量一定很大,如果建不好表,那肯定是会带来灾难性的性能,日志是一条条的,都是事件数据,而每一个事件都必然会存在一个事件产生的时间,所以这里就很容易想到使用高级分区模式,Range和Hash两种模式一起使用来建表,然后通过 tablet-servers 这个查询页面,查询下一共有多少个tablet,建表的时候就采用多少个tablet个数

CREATE TABLE system_log(
  log_id BIGINT,
  log_time timestamp,
  log_content STRING,
  PRIMARY KEY (log_id , log_time )
)
PARTITION BY HASH (log_id) PARTITIONS 4,  --这里的4取值的时候小于等于tablet servers 个数
RANGE (log_time )
(
  PARTITION VALUES < '2021-09-20',
  PARTITION '2021-09-20' <= VALUES < '2021-09-21',
  PARTITION '2021-09-22' <= VALUES < '2021-09-23',
  PARTITION '2021-09-24' <= VALUES
)
STORED AS KUDU;

经过测试发现,平常查看数据的时候,速度提升了百倍以上

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值