1.通过impala来操作kudu,impala的元数据放在hive的metastore服务里,impala-shell 命令行开启shell操作。
2.在impala里创建kudu表,需要impala的外部表与kudu的表做映射:
kudu表基于其partition方法被拆分成多个分区,每个分区就是一个tablet,一张kudu表所属的所有tablets均匀分布并存储在tablet servers的磁盘上。因此在创建kudu表的时候需要声明该表的partition方法,同时要指定primary key作为partition的依据。Kudu中的分区方法主要有两种:partition by hash和partition by range。
-- 按照id 切分,partition by hash
create external table tab4
(
id int,
name string,
delete_state string,
time_stamp string,
primary key (id)
)
partition by hash (id) partitions 2
stored as kudu
tblproperties ('kudu.master_addresses'='bigdata03:7051,bigdata05:7051,bigdata07:7051', 'kudu.table_name'='tab4');
--tblproperties 在将impala里的外部表与kudu里的表进行映射
-- 按照班级分类 partition by range
create table testinkudu2(
classes int,
namestring,
ageint,
primarykey(classes,name))
partition by range(classes)(
partition value = 1,
partition value = 2,
partition value =3,
partitionvalue =4)
stored as kudu;
create external table xxx (id int ,......,primary key (id)) partition by hash (id) partitions 2 stored as kudu;
create external table xxx (id int ,......,primary key (class)) partition by range(partition value = 1,partition value = 2) stored as kudu;
3.基本语法
-- insert (3种)
1. insert into testinkudu values(‘a’, 12);
2. insert into testinkudu values(‘a’, 12),(‘b’, 13),(‘c’, 14);
3. insert into testinkudu select * from other_table;
-- update
kudu表的update操作不能更改主键的值,其他与标准sql语法相同。
-- upsert
对于upsert into testinkudu values(‘a’, 12);如果指定的values中的主键在testinkudu中已经存在,则执行update语义,反之,执行insert语义。(侦察主键是否已存在)
-- delete
与标准sql语法相同。
4.Kudu之Scala版本API
建表:
-- 建表
// 创建kudu连接
val kuduClient = new KuduClient.KuduClientBuilder("172.20.85.29:7051").build()
// 设置表名
val tableName = "kudu_test"
// 创建列
val colums = List[ColumnSchema]((new ColumnSchema.ColumnSchemaBuilder("name", Type.STRING).key(true).nullable(false).build()),
(new ColumnSchema.ColumnSchemaBuilder("age", Type.INT64).nullable(true).build()),
(new ColumnSchema.ColumnSchemaBuilder("city", Type.STRING).nullable(true).build()))
val schema: Schema = new Schema(colums.asJava)
// 设置hash分区
val cto: CreateTableOptions = new CreateTableOptions()
cto.setRangePartitionColumns(List("name").asJava).setNumReplicas(3)
// 执行建表语句
kuduClient.createTable(tableName, schema, cto)
// 关闭kudu连接
kuduClient.close()
kuduClient API insert/update/upsert/delete
// 创建kudu连接
val kuduClient = new KuduClient.KuduClientBuilder("172.20.85.29:7051").build()
// 设置表名
val tableName = "kudu_test"
// 获得表的连接
val kuduTable = kuduClient.openTable(tableName)
// 开启一个会话
val session = kuduClient.newSession()
// session.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH) # (别开,实时中易掉数据)
---------------------------------------getColumns----------------------------------------
//获取字段信息
val columns = kuduTable.getSchema.getColumns.toArray()
---------------------------------------insert----------------------------------------
// 创建插入对象并设置插入数据
val insert = kuduTable.newInsert()
val row = insert.getRow()
row.addString(0, "Ronnie")
row.addInt(1, 21)
row.addString(2, "beijing")
//执行插入语句
session.apply(insert)
---------------------------------------update----------------------------------------
// 创建updata对象
val update = kuduTable.newUpdate()
val rowUpdata = update.getRow()
rowUpdata.addString("name", "nnnn")
rowUpdata.addInt("age", 22)
rowUpdata.addString("city", "ddddd")
// 同步数据并关闭会话
session.apply(update)
---------------------------------------upsert----------------------------------------
// 创建upsert对象
val upsert = kuduTable.newUpsert()
val rowUpsert = upsert.getRow()
rowUpsert.addString("name", "nnnn")
rowUpsert.addInt("age", 19)
rowUpsert.addString("city", "mmmm")
// 执行upsert操作
session.apply(upsert)
---------------------------------------批量upsert----------------------------------------
for (indexCol <- 0 until columns.length) {
if (columns(indexCol).toString.contains("int32")) {
// 创建插入对象并设置插入数据
row.addInt(indexCol, logResult(indexCol).toInt)
}
else if (columns(indexCol).toString.contains("string")) {
row.addString(indexCol, logResult(indexCol))
}
else if (columns(indexCol).toString.contains("double")) {
row.addDouble(indexCol, logResult(indexCol).toDouble)
}
else if (columns(indexCol).toString.contains("bool")) {
row.addBoolean(indexCol, logResult(indexCol).toBoolean)
}
// 执行upsert操作
session.apply(upsert)
---------------------------------------select----------------------------------------
// 设置查询条件
val schema = kuduTable.getSchema()
val name = KuduPredicate.newComparisonPredicate(schema.getColumn("name"), KuduPredicate.ComparisonOp.EQUAL, "Ronnie")
val age = KuduPredicate.newComparisonPredicate(schema.getColumn("age"), KuduPredicate.ComparisonOp.LESS, 22)
val city = KuduPredicate.newComparisonPredicate(schema.getColumn("city"), KuduPredicate.ComparisonOp.EQUAL, "beijing")
// 执行查询操作
val builder = kuduClient.newScannerBuilder(kuduTable)
.setProjectedColumnNames(List("name", "age", "city").asJava)
.addPredicate(name)
.addPredicate(age)
.addPredicate(city).build()
while (builder.hasMoreRows()) {
val results = builder.nextRows()
while (results.hasNext()) {
val result = results.next()
System.out.println(result.getLong("name") + "_" + result.getInt("age") + "_" + result.getString("city"))
}
// 关闭kudu连接
kuduClient.close()
---------------------------------------delete----------------------------------------
// 创建删除对象并指定要删除的行
val delete = kuduTable.newDelete()
delete.getRow().addString("name", "Ronnie")
// 执行删除操作
session.apply(delete)
// 同步数据,关闭会话
session.flush()
session.close()
5. kudu插入数据的三种刷新策略
- AUTO_FLUSH_SYNC (默认)
- AUTO_FLUSH_BACKGROUND
- MANUAL_FLUSH 速度最快,但是实时的情况下容易发生数据丢失。(调用 KuduSession.apply() 方法后,会返回的非常快,但是写操作不会发送,直到用户使用flush()函数,如果缓冲区超过了配置的空间限制,KuduSession.apply()函数会返回一个错误。所以缓冲区里的数据会因为断电或其他业务,在还没有flush的情况下丢失)
6.kudu 与 impala 与 hdfs 的纠葛
impala中创建kudu表的话,可以在impala中看到kudu表,但是使用 kudu java api创建kudu表的话,impala看不到kudu表,只能kudu web ui看到,因此也只能使用 kudu java api删除该表。
因此此时需要进入impala-shell创建impala外部映射表映射Kudu表,创建impala外部映射表之后,就同时会在hdfs的/user/hive/warehouse路径下创建同名文件夹。
impala中创建kudu表和impala外部映射表的话,两者都会在hdfs的/user/hive/warehouse路径下创建同名文件夹,并且两者都能在impala-shell中查看到表。
如果使用 kudu java api创建kudu表的话,并不会在hdfs的/user/hive/warehouse路径下创建同名文件夹,并且impala-shell中查看不到该kudu表。
7.impala命令刷新元数据
impala-shell 命令进入交互界面 执行 invalidate metadata; 命令刷新元数据
Hue的wen页面中,在impala执行sql的窗口 执行 invalidate metadata; 命令刷新元数据
8.kudu导入导出
导出 kudu表数据 为csv文件
impala-shell中 导出kudu表数据为csv文件,或者说是通过impala的外部映射表把kudu表数据导出为csv文件
impala-shell -q "select * from rawdata.event_wos_p1 LIMIT 100" -B --output_delimiter="," -o /home/gzp/event_wos_p1.csv
把csv文件 导入到 kudu表中
1.首先impala中要创建带分隔符的临时表,即impala中创建的表带有row format delimited fields terminated by ',';
create table event_wos_p1_temp(day int,...)row format delimited fields terminated by ',';
2.使用 kudu java/scala api 创建kudu表 或 impala中 创建kudu表
3.impala中 创建映射kudu表 的impala外部 映射表
4.INSERT INTO impala外部映射表/kudu表 SELECT * FROM 带分隔符的临时表;
INSERT INTO event_wos_p1 SELECT * FROM event_wos_p1_temp;
9.kudu的应用场景
- kudu是什么?
简单来说:kudu是一个与hbase类似的列式存储分布式数据库。
官方给kudu的定位是:在更新更及时的基础上实现更快的数据分析
- 为什么需要kudu?
hdfs与hbase数据存储的缺点
目前数据存储有了HDFS与hbase,为什么还要额外的弄一个kudu呢?
HDFS:使用列式存储格式Apache Parquet,Apache ORC,适合离线分析,不支持单条纪录级别的update操作,随机读写性能差
HBASE:可以进行高效随机读写,却并不适用于基于SQL的数据分析方向,大批量数据获取时的性能较差。
正因为HDFS与HBASE有上面这些缺点,KUDU较好的解决了HDFS与HBASE的这些缺点,它不及HDFS批处理快,也不及HBase随机读写能力强,但是反过来它比HBase批处理快(适用于OLAP的分析场景),而且比HDFS随机读写能力强(适用于实时写入或者更新的场景),这就是它能解决的问题。