kudu使用及常见问题小结

    xiaoxiao2022-12-08  50

    问题记录

    最近发现kudu日志爆满,info信息全部输出保存在了日志文件里,需要配置日志输出级别:首先登录了kudu监控页面:http://xxxxx:8051 

    发现Command-line Flags里的

    --minloglevel=0

    修改/etc/kudu/conf/master.gflagfile 和 /etc/kudu/conf/tserver.gflagfile 这两个文件,加上--minloglevel=2,重启kudu,

    service kudu-master restart

    service kudu-tserver restart

    问题解决。

     

    kudu 使用案例:

    主要讲解使用impala进行kudu的操作:

    1.假设已经通过Java client等其他方式在kudu中创建了某个表,要想对该表进行操作,需要在impala中创建外部表,将其映射到impala当中,例如:

    CREATE EXTERNAL TABLE my_mapping_table

    STOREDAS KUDU

    TBLPROPERTIES(

    'kudu.table_name' = 'my_kudu_table');

     

    这种情况极少出现,这边先不展开。

     

    2.创建impala外表并从文件中导入数据(注意此表不是kudu表,是用来存放hdfs数据的)

    hdfs dfs -mkdir /sfmta

     

    zcat sfmtaAVLRawData01012013.csv.gz | tr -d '\r' | hadoop fs -put - /sfmta/data.csv

    hadoop fs -ls /sfmta

     

    CREATE EXTERNAL TABLE sfmta_raw (

      revision int,

      report_time string,

      vehicle_tag int,

      longitude float,

      latitude float,

      speed float,

      heading float

    )

    ROW FORMAT DELIMITED

    FIELDS TERMINATED BY ','

    LOCATION '/sfmta/'

    TBLPROPERTIES ('skip.header.line.count'='1');  ---跳过文件首行

     

    CREATE TABLE sfmta

    PRIMARY KEY (report_time, vehicle_tag)       ---创建表的时候需要指定主键(必须包含主键)

    PARTITION BY HASH(report_time) PARTITIONS 8   

    STORED AS KUDU

    AS SELECT

      UNIX_TIMESTAMP(report_time,  'MM/dd/yyyy HH:mm:ss') AS report_time,   ---string转换成时间戳

      vehicle_tag,

      longitude,

      latitude,

      speed,

      heading

    FROM sfmta_raw;

    3.kudu使用impala直接创建表

    CREATE TABLE my_first_table

    (

      id BIGINT,

      name STRING,

      PRIMARY KEY(id)

    )

    PARTITION BY HASH PARTITIONS 16

    STORED AS KUDU;

    (不指定hash分区键,默认主键作为hash分区键)

     

    CREATE TABLE new_table

    PRIMARY KEY (ts, name)

    PARTITION BY HASH(name) PARTITIONS 8

    STORED AS KUDU

    AS SELECT ts, name, value FROM old_table;

     

    4.指定 Tablet Partitioning ( Tablet 分区 )

    表分为每个由一个或多个 tablet servers 提供的 tablets 。理想情况下,tablets 应该相对平等地拆分表的数据。 Kudu 目前没有自动(或手动)拆分预先存在的 tablets  的机制。在实现此功能之前,必须在创建表时指定分区。在设计表格架构时,考虑使用主键,可以将表拆分成以类似速度增长的分区。使用 Impala 创建表时,可以使用 PARTITION BY 子句指定分区:

    CREATE TABLE cust_behavior (

      _id BIGINT PRIMARY KEY,

      salary STRING,

      edu_level INT,

      usergender STRING,

      `group` STRING,

      city STRING,

      postcode STRING,

      last_purchase_price FLOAT,

      last_purchase_date BIGINT,

      category STRING,

      sku STRING,

      rating INT,

      fulfilled_date BIGINT

    )

    PARTITION BY RANGE (_id)

    (

        PARTITION VALUES < 1439560049342,

        PARTITION 1439560049342 <= VALUES < 1439566253755,

        PARTITION 1439566253755 <= VALUES < 1439572458168,

        PARTITION 1439572458168 <= VALUES < 1439578662581,

        PARTITION 1439578662581 <= VALUES < 1439584866994,

        PARTITION 1439584866994 <= VALUES < 1439591071407,

        PARTITION 1439591071407 <= VALUES

    )

    STORED AS KUDU;

    如果有多个主键列,则可以使用元组语法指定分区边界:('va',1),('ab',2)。该表达式必须是有效的 JSON 。

     

    5.将数据插入 Kudu 表

    Impala 允许您使用标准 SQL 语句将数据插入 Kudu 。

    此示例插入单个行。

    INSERT INTO my_first_table VALUES (99, "sarah");

    此示例使用单个语句插入三行。

    INSERT INTO my_first_table VALUES (1, "john"), (2, "jane"), (3, "jim");

     

    Hive入kudu:

    insert into table mykudu.my_first_table select * from impala_database.my_first_table

     

     Kudu入hive

    insert into table impala_database.my_first_table select * from mykudu.my_first_table

     

    6.更新行

    UPDATE my_first_table SET name="bob" where id = 3;

    7.批量更新

    可以使用批量插入中相同的方法 批量更新 。

    UPDATE my_first_table SET name="bob" where age > 10;

     

    8.删除行

    DELETE FROM my_first_table WHERE id < 3;

    9.批量删除

    您可以使用 “插入批量” 中概述的相同方法 批量删除 。

    DELETE FROM my_first_table WHERE id < 3;

     

    10.删除表

    如果表是使用 Impala 中的内部表创建的,则使用 CREATE TABLE ,标准 DROP TABLE 语法会删除底层的 Kudu 表及其所有数据。如果表被创建为一个外部表,使用 CREATE EXTERNAL TABLE,Impala 和 Kudu 之间的映射被删除,但 Kudu表保持原样,并包含其所有数据。

    DROP TABLE my_first_table;

     

    11.主键设计

    每一个Kudu的表都有且仅有一个主键,主键可以包含多个列,同时要求每一列的值都不能为空(non-nullable),另外bool和浮点数也不能作为主键。创建表的时候主键必须是前几个字段,不能将非主键的字段放在主键字段前,否则会报错。

     

     

     

     12.切片设计(partitioning)

    作为一个分布式的数据存储引擎,切片是最基本也是最重要的设计之一。对于每个数据库表(table)而言,Kudu会把一个table按照切片规则分成多个partition,一个partition存储在tablet服务之中。每个tablet都有一个一主多从的tablet服务,每条数据属于且仅属于一个tablet,数据和tablet的从属关系规则,由切片规则决定。

     

    Range切片

    Range的切片方式把数据按照范围进行分类,每个partition会分配一个固定的范围,每个数据只会属于一个切片,不同partition的范围不能有重叠。切片在表的创建阶段配置,后续不可修改,但是可以删除和新增,如果数据找不到所属的切片,会插入失败。

    range的切片方式通常与时间有关系,值得注意的是,老的切片可以删掉,同时可以增加新的切片,意味着与时间强相关的数据可以按照这种方式来切片,老的数据可以通过删除切片的方式删除。同时Kudu对此类操作的支持非常高效,完全不用担心删除或者新增切片会影响数据读写。上文有讲到,单片数据过大会影响Kudu的性能,配置为时间相关的Range切片方式,可以很好地控制每片数据的总大小。比如有日志型数据,每秒平均有100条数据写入,配置每天一个切片,则单片数据量规模约为864w。如果配置为hash的切片方式,则单片数据会随着时间推移越来越多大。

    切片的设计对Scan性能的影响至关重要,比如对于时间序列类型的数据而言,往往查询的是近期的数据,如果按时间进行切片,则Scan操作可以跳过大部分数据,如果单纯按照默认的hash方式切片,Scan操作则需要扫描全表。

     

    ALTER TABLE sales_by_year ADD RANGE PARTITION VALUE = 2017;

     

    ALTER TABLE sales_by_year DROP RANGE PARTITION VALUE = 2012;

     

    Hash切片

    Hash切片把每行数据hash之后分配到对应的tablet。hash切片在设计上相对简单,通常情况下只需要配置计算hash值的列,比如前文所列举的例子,如果你需要查询一个店铺的所有商品,则把shopid作为hash列是比较恰当的选择。相同shopid的hash值是相同的,相同hash值的数据肯定会被分配到同一个partition之中。

    需要注意的是hash的切片方式是不可修改的,所以随着数据量的增长,hash的切片方式会造成单片的数据量过大,甚至超过单个tablet服务所能承受的数据量。

    hash的切片方式对于随机读写友好,对于写操作而言,hash的切片方式会均匀的把写入压力分担到多个切片之中。对于随机读而言,按照主键进行hash之后Kudu可以提前预知读操作所对应的切片,避免每个切片都查一次。

     

    优化一个数据库表的切片规则需要考虑随机读、随机写、扫描三种操作,需根据业务场景的不同侧重来最终决定。

     

    随机写压力场景

    对于写压力比较大的业务场景,最重要的一点是把写压力均匀分担到不同的tablet之中,这种场景下切片设计通常采用hash partitioning,hash切片拥有良好的随机性。

    相比Hbase而言,Kudu的架构可以轻松应对随机写的场景。

     

    随机读压力场景

    对于随机读压力比较大的业务场景并不是很建议使用Kudu,通常情况下Hbase是一个更好的选择,不过Kudu也拥有不错的随机读性能。Kudu官方的性能测试,在读压力分布符合齐夫定律时,Hbase有读性能优势,随机分布下,Kudu和Hbase的的随机读性能相当。不过通常情况下业务场景的读分布符合齐夫定律,也就是我们常说的28原则,80%的读集中在20%的数据上。

    如果用Kudu的业务场景确实随机读压力较大,则通常采用hash partitioning。

     

    小范围Scan场景

    对于拥有大量小范围Scan的业务场景,比如扫描一个店铺的所有商品,比如找到一个用户看过的所有商品,诸如此类的业务场景最好将同一个Scan所需要的数据放置在同一个tablet里面。比如按店铺id做hash,可以把同一个店铺的所有信息放置在同一个tablet里。按userid做hash,可以把一个用户的所有信息放置在同一个tablet里面。

     

    大范围Scan

    如果业务场景的Scan所需要扫描的数据量比较大,又想这类Scan跑的快,则需要把这类Scan所需要的数据分布到多个tablets里面,充分利用多机分布式计算能力。假设我们有一个表存储了最近12个月的数据,一个设计方案是按照月来切片,一共12个tablet,但如果大部分BI查询对应的Scan只需要最近1个月的数据,则这种设计便不合理,因为Scan的压力全部集中到了一个tablet之中。

    这种情况下一个更好的设计方案是按月切片再按hash切片,具体方案后续再详细分析。

     

    多级切片

    Kudu支持多层的切片方式,hash和range的切片方式可以结合起来。比如按照月把数据分成多片,每个月的数据再按照hash进行二级切片。

    合理的使用多级切片,可以充分利用不同切片方式的优势。

     

    切片调优

    合理的切片可以让Kudu的Scan操作跳过部分切片,比如上文举例说明的时间序列类型存储。合理的切片还需要避免写入热点,防止大量的写入分配到同一个tablet服务之中。

     

    高级分区

    hash分区数量*range分区数量不能超过60个(1.7.0版本之后没限制了)

     

    可以组合 HASH 和 RANGE 分区来创建更复杂的分区模式。您可以指定零个或多个 HASH 定义,后跟零个或一个 RANGE 定义。每个定义可以包含一个或多个列。

    PARTITION BY HASH and RANGE

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

    以下示例仍然创建了16个 tablets ,首先将 id 列分为 4 个存储区,然后根据 sku 字符串的值应用范围划分将每个存储区分为四个数据块。至少四片(最多可达16张)。当您查询相邻范围的 sku 值时,您很有可能只需从四分之一的 tablets 中读取即可完成查询。

    注意

    默认情况下,使用 PARTITION BY HASH 时,整个主键是散列的。要只对主键进行散列,可以使用像 PARTITION BY HASH(id, 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;

    Multiple PARTITION BY HASH Definitions

    再次扩展上述示例,假设查询模式将是不可预测的,但希望确保写入分布在大量 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;

    该示例创建16个分区。也可以使用 HASH(id,sku) PARTITIONS 16。但是,对于 sku 值的扫描几乎总是会影响所有16个分区,而不是可能限制为 4 。

     

    存在问题:

    (1) 重复数据不报错(upsert into)

    经过测试发现,insert into时若有主键重复,插入的数据不会更新原有数据,并且不会报错,但是会有warning。用upsert into时,主键重复数据会更新原有数据,并且语句执行正常,没有warning。并且两个语句的执行效率类似。

     

    (2) 不存在事务

    (3)导入数据有问题

    暂时如此,未来要管控

    (4) 分区要研究

    答:kudu建表必须指定分区,且分区数至少为2个。Kudu建表有两种方式的分区方法,分为hash方式建表和range方式建表。

    使用hash方式:

    一般情况下,对于全量表,可使用hash方式建表,且由于全量表数据量较小,hash分区数指定为2即可。

    例:

    drop table mykudu.cust_behavior;

    CREATE TABLE mykudu.cust_behavior (

      id BIGINT,

      sku STRING,

      salary STRING,

      edu_level INT,

      PRIMARY KEY (id, sku)

    )

    PARTITION BY HASH PARTITIONS 2

    STORED AS KUDU;

    使用range方式:

    对于大数据量的增量表,如估值表,凭证表,大数据平台暂定存储三个月时间的数据,因此一般使用range方式存储数据,range方式可动态的进行分区的创建和删除,可按月来进行数据的存储。后续需要按月进行批量表的动态分区创建及删除。

    例:

    CREATE TABLE mykudu.sales_by_month (

          month_time INT,

    sale_id INT,

    amount INT,

      PRIMARY KEY (sale_id, month_time)

    )

    PARTITION BY RANGE (month_time) (

      PARTITION VALUE = 201811,

      PARTITION VALUE = 201812,

      PARTITION VALUE = 201901

    )

    STORED AS KUDU;

     

    ALTER TABLE mykudu.sales_by_month ADD RANGE PARTITION VALUE = 201902;

    ALTER TABLE mykudu.sales_by_month drop RANGE PARTITION VALUE = 201811;

     

    (5)部分函数不复用

    (6)不存在索引

    (7)必须有主键

    Kudu使用过程中的多种限制(主键):

    a.表创建之后,主键不能修改,必须删除重建表指定新的主键;

    b.主键列必须在非主键列之前;

    c.主键列的值不能使用update函数进行修改;如果要修改主键的值,只能删除该行重新插入;

    d.Double,float和boolean类型的列不能作为主键,此外,主键必须为not null。

    e.不支持自动生成主键(如自增列);

    f.组合主键所有列在编码后,大小不能大于16k

     

    (8)数据存储默认编码格式(utf-8)

     下游同步数据需要注意

    (9)主键列存在NULL值,导致插入错误,但执行成功,只返回warning。

     

    (10)主要参考Impala函数

    (11)字符串是以UTF-8编码

     

    参考文献:

    (1)Kudu+Impala介绍 | 微店数据科学团队博客

    (2)Apache Kudu - Fast Analytics on Fast Data

    最新回复(0)