问题记录
最近发现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编码
参考文献: