Kudu+Impala很适合数据分析, 但直接使用Insert values语句往Kudu表插入数据, 效率实在不好, 测试下来insert的速度仅为80笔/秒. 原因也是显然的, Kudu本身写入效率很高, 但是Impala并没有做这方面优化, 观察下来每次Impala语句执行的overhead都太大了, 导致频繁小批次写入效率非常差, Kudu官方推荐使用Java API或Python API完成数据写入工作. 下面是使用Java API的测试用例, 也可以看出Kudu API的大致用法.

=========================
准备测试Table
=========================

-- kudu table
CREATE TABLE kudu_testdb.tmp_test_perf
(
id string ENCODING PLAIN_ENCODING COMPRESSION SNAPPY,
int_value int ,
bigint_value bigint ,
timestamp_value timestamp ,
boolean_value int,
PRIMARY KEY (id)
)
PARTITION BY HASH (id) PARTITIONS 6
STORED AS KUDU
TBLPROPERTIES (
'kudu.table_name' = 'testdb.tmp_test_perf',
'kudu.master_addresses' = '10.0.0.100:7051,10.0.0.101:7051,10.0.0.101:7051',
'kudu.num_tablet_replicas' = '1'
)
;

=========================
编写测试java程序
=========================
Kudu API 编码注意事项:

1. 尽管建表Impala DDL中,kudu表字段名大小写不敏感, 但在kudu层面, 字段名称已经转成为小写形式, 在Kudu API中, 字段名称必须是小写字母.
2. 建表Impala DDL表名称大小写会被完整地保留下来, 并没有被转成小写, 而且在Kudu API使用中, 表名是大小写敏感的, 必须和建表DDL完全一致.
3. Kudu API给字段赋值函数是不接受传入null, 所以如果在为字段赋值之前, 最好先判断一下取值是否为null. 例如下面两行代码会报错.

Long longTmp=null;
row.addLong("bigint_value",longTmp);

 
package kudu_perf_test;

import java.sql.Timestamp;
import java.util.UUID;
import org.apache.kudu.client.*; public class Test {
private final static int OPERATION_BATCH = 500; //同时支持三个模式的测试用例
public static void insertTestGeneric(KuduSession session, KuduTable table, SessionConfiguration.FlushMode mode,
int recordCount) throws Exception {
// SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND
// SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC
// SessionConfiguration.FlushMode.MANUAL_FLUSH
session.setFlushMode(mode);
if (SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC != mode) {
session.setMutationBufferSpace(OPERATION_BATCH);
}
int uncommit = 0; for (int i = 0; i < recordCount; i++) {
Insert insert = table.newInsert();
PartialRow row = insert.getRow();
UUID uuid = UUID.randomUUID();
row.addString("id", uuid.toString());
row.addInt("int_value", 100);
row.addLong("bigint_value", 10000L); Long gtmMillis;
/* System.currentTimeMillis() 是从1970-01-01开始算的毫秒数(GMT), kudu API是采用纳秒数, 所以需要*1000
另外, 考虑到我们是东8区时间, 所以转成Long型需要再加8个小时, 否则存到Kudu的时间是GTM, 比东8区晚8个小时
*/ //方法1: 获取当前时间对应的GTM时区unix毫秒数
gtmMillis=System.currentTimeMillis(); //方法2: 将timestamp转成对应的GTM时区unix毫秒数
Timestamp localTimestamp = new Timestamp(System.currentTimeMillis());
gtmMillis=localTimestamp.getTime(); //将GTM的毫秒数转成东8区的毫秒数量
Long shanghaiTimezoneMillis=gtmMillis+8*3600*1000;
row.addLong("timestamp_value", shanghaiTimezoneMillis*1000); session.apply(insert); // 对于手工提交, 需要buffer在未满的时候flush,这里采用了buffer一半时即提交
if (SessionConfiguration.FlushMode.MANUAL_FLUSH == mode) {
uncommit = uncommit + 1;
if (uncommit > OPERATION_BATCH / 2) {
session.flush();
uncommit = 0;
}
}
} // 对于手工提交, 保证完成最后的提交
if (SessionConfiguration.FlushMode.MANUAL_FLUSH == mode && uncommit > 0) {
session.flush();
} // 对于后台自动提交, 必须保证完成最后的提交, 并保证有错误时能抛出异常
if (SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND == mode) {
session.flush();
RowErrorsAndOverflowStatus error = session.getPendingErrors();
if (error.isOverflowed() || error.getRowErrors().length > 0) {
if (error.isOverflowed()) {
throw new Exception("Kudu overflow exception occurred.");
}
StringBuilder errorMessage = new StringBuilder();
if (error.getRowErrors().length > 0) {
for (RowError errorObj : error.getRowErrors()) {
errorMessage.append(errorObj.toString());
errorMessage.append(";");
}
}
throw new Exception(errorMessage.toString());
}
} } //仅支持手动flush的测试用例
public static void insertTestManual(KuduSession session, KuduTable table, int recordCount) throws Exception {
// SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND
// SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC
// SessionConfiguration.FlushMode.MANUAL_FLUSH
SessionConfiguration.FlushMode mode = SessionConfiguration.FlushMode.MANUAL_FLUSH;
session.setFlushMode(mode);
session.setMutationBufferSpace(OPERATION_BATCH); int uncommit = 0;
for (int i = 0; i < recordCount; i++) {
Insert insert = table.newInsert();
PartialRow row = insert.getRow();
UUID uuid = UUID.randomUUID();
row.addString("id", uuid.toString());
row.addInt("int_value", 100);
row.addLong("bigint_value", 10000L); Long gtmMillis;
/* System.currentTimeMillis() 是从1970-01-01开始算的毫秒数(GMT), kudu API是采用纳秒数, 所以需要*1000
另外, 考虑到我们是东8区时间, 所以转成Long型需要再加8个小时, 否则存到Kudu的时间是GTM, 比东8区晚8个小时
*/ //方法1: 获取当前时间对应的GTM时区unix毫秒数
gtmMillis=System.currentTimeMillis(); //方法2: 将timestamp转成对应的GTM时区unix毫秒数
Timestamp localTimestamp = new Timestamp(System.currentTimeMillis());
gtmMillis=localTimestamp.getTime(); //将GTM的毫秒数转成东8区的毫秒数量
Long shanghaiTimezoneMillis=gtmMillis+8*3600*1000;
row.addLong("timestamp_value", shanghaiTimezoneMillis*1000); session.apply(insert); // 对于手工提交, 需要buffer在未满的时候flush,这里采用了buffer一半时即提交
uncommit = uncommit + 1;
if (uncommit > OPERATION_BATCH / 2) {
session.flush();
uncommit = 0;
}
} // 对于手工提交, 保证完成最后的提交
if (uncommit > 0) {
session.flush();
}
} //仅支持自动flush的测试用例
public static void insertTestInAutoSync(KuduSession session, KuduTable table, int recordCount) throws Exception {
// SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND
// SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC
// SessionConfiguration.FlushMode.MANUAL_FLUSH
SessionConfiguration.FlushMode mode = SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC;
session.setFlushMode(mode); for (int i = 0; i < recordCount; i++) {
Insert insert = table.newInsert();
PartialRow row = insert.getRow();
UUID uuid = UUID.randomUUID();
row.addString("id", uuid.toString());
row.addInt("int_value", 100);
row.addLong("bigint_value", 10000L); Long gtmMillis;
/* System.currentTimeMillis() 是从1970-01-01开始算的毫秒数(GMT), kudu API是采用纳秒数, 所以需要*1000
另外, 考虑到我们是东8区时间, 所以转成Long型需要再加8个小时, 否则存到Kudu的时间是GTM, 比东8区晚8个小时
*/ //方法1: 获取当前时间对应的GTM时区unix毫秒数
gtmMillis=System.currentTimeMillis(); //方法2: 将timestamp转成对应的GTM时区unix毫秒数
Timestamp localTimestamp = new Timestamp(System.currentTimeMillis());
gtmMillis=localTimestamp.getTime(); //将GTM的毫秒数转成东8区的毫秒数量
Long shanghaiTimezoneMillis=gtmMillis+8*3600*1000;
row.addLong("timestamp_value", shanghaiTimezoneMillis*1000); //对于AUTO_FLUSH_SYNC模式, apply()将立即完成kudu写入
session.apply(insert);
}
} public static void test() throws KuduException {
KuduClient client = new KuduClient.KuduClientBuilder("10.0.0.100:7051,10.0.0.101:7051,10.0.0.101:7051")
.build();
KuduSession session = client.newSession();
KuduTable table = client.openTable("testdb.tmp_test_perf"); SessionConfiguration.FlushMode mode;
Timestamp d1 = null;
Timestamp d2 = null;
long millis;
long seconds;
int recordCount = 0; try {
mode = SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND;
d1 = new Timestamp(System.currentTimeMillis());
insertTestGeneric(session, table, mode, recordCount);
d2 = new Timestamp(System.currentTimeMillis());
millis = d2.getTime() - d1.getTime();
seconds = millis / 1000 % 60;
System.out.println(mode.name() + "耗时秒数:" + seconds); mode = SessionConfiguration.FlushMode.AUTO_FLUSH_SYNC;
d1 = new Timestamp(System.currentTimeMillis());
insertTestInAutoSync(session, table, recordCount);
d2 = new Timestamp(System.currentTimeMillis());
millis = d2.getTime() - d1.getTime();
seconds = millis / 1000 % 60;
System.out.println(mode.name() + "耗时秒数:" + seconds); mode = SessionConfiguration.FlushMode.MANUAL_FLUSH;
d1 = new Timestamp(System.currentTimeMillis());
insertTestManual(session, table, recordCount);
d2 = new Timestamp(System.currentTimeMillis());
millis = d2.getTime() - d1.getTime();
seconds = millis / 1000 % 60;
System.out.println(mode.name() + "耗时秒数:" + seconds); } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (!session.isClosed()) {
session.close();
}
} } public static void main(String[] args) {
try {
test();
} catch (KuduException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Done"); }
}

=========================
性能测试结果
=========================
MANUAL_FLUSH 模式:8000 row/second
AUTO_FLUSH_BACKGROUND 模式:8000 row/second
AUTO_FLUSH_SYNC 模式:1000 row/second
Impala SQL Insert 语句:80 row/second

=========================
Kudu API 使用总结
=========================
1. 尽量采用 MANUAL_FLUSH, 性能最好, 如果有写入kudu错误, flush()函数就会抛出异常, 逻辑非常清晰.
2. 在性能要求不高的情况下, AUTO_FLUSH_SYNC 也是一个好的选择.
3. 仅仅在demo场景下使用 AUTO_FLUSH_BACKGROUND, 不考虑异常处理时候代码可以很简单, 性能也很好. 在生产环境下, 不推荐的 原因是: 插入数据可能会是乱序, 一旦考虑捕获异常代码就很拖沓.

kudu系列: Java API使用和效率测试的更多相关文章

  1. HBase 系列(六)——HBase Java API 的基本使用

    一.简述 截至到目前 (2019.04),HBase 有两个主要的版本,分别是 1.x 和 2.x ,两个版本的 Java API 有所不同,1.x 中某些方法在 2.x 中被标识为 @depreca ...

  2. ElasticSearch实战系列三: ElasticSearch的JAVA API使用教程

    前言 在上一篇中介绍了ElasticSearch实战系列二: ElasticSearch的DSL语句使用教程---图文详解,本篇文章就来讲解下 ElasticSearch 6.x官方Java API的 ...

  3. kafka2.9.2的伪分布式集群安装和demo(java api)测试

    目录: 一.什么是kafka? 二.kafka的官方网站在哪里? 三.在哪里下载?需要哪些组件的支持? 四.如何安装? 五.FAQ 六.扩展阅读   一.什么是kafka? kafka是LinkedI ...

  4. ubuntu12.04+kafka2.9.2+zookeeper3.4.5的伪分布式集群安装和demo(java api)测试

    博文作者:迦壹 博客地址:http://idoall.org/home.php?mod=space&uid=1&do=blog&id=547 转载声明:可以转载, 但必须以超链 ...

  5. 5 weekend01、02、03、04、05、06、07的分布式集群的HA测试 + hdfs--动态增加节点和副本数量管理 + HA的java api访问要点

    weekend01.02.03.04.05.06.07的分布式集群的HA测试 1)  weekend01.02的hdfs的HA测试 2)  weekend03.04的yarn的HA测试 1)  wee ...

  6. [测试]java IO写入文件效率——几种方法比较

    各类写入方法 /** *1 按字节写入 FileOutputStream * * @param count 写入循环次数 * @param str 写入字符串 */ public void outpu ...

  7. Hadoop 系列(三)Java API

    Hadoop 系列(三)Java API <dependency> <groupId>org.apache.hadoop</groupId> <artifac ...

  8. Hadoop 系列(七)—— HDFS Java API

    一. 简介 想要使用 HDFS API,需要导入依赖 hadoop-client.如果是 CDH 版本的 Hadoop,还需要额外指明其仓库地址: <?xml version="1.0 ...

  9. SuperMap iServer 扩展/JAVA API 系列博客整理

    转载:http://blog.csdn.net/supermapsupport/article/details/70158940 SuperMap iServer为广大用户提供了整套 SDK,应用开发 ...

随机推荐

  1. ubuntu下查看磁盘读写情况

    iostat -d -k 1 10 每秒刷新一次,共10次. 未完待续..

  2. redis执行lua文件

    1.编写lua文件 还lua表示循环插入1-100的键为1-100值为1-100的键值对到redis中 for i=0,100 do redis.call("set",i,i) e ...

  3. gradle配置统一管理

    配置全局参数 根目录build.gradle文件: 主要关注ext部分 // Top-level build file where you can add configuration options ...

  4. A1131. Subway Map (30)

    In the big cities, the subway systems always look so complex to the visitors. To give you some sense ...

  5. Web Deploy 服务器安装设置与使用

    一.服务器的安装设置 1.在windows server上确保IIS安装了[管理服务]这个功能.方法是在[服务器管理器]=>[管理]=>[添加角色和功能]=>[下一步]=>[基 ...

  6. Java 读数据库字段时发现的一个现象

    早上发现有一个网名叫“帅!是不需要理由”的一个人,在后台只能看到“帅!是不需要理”,“由”字就是不显示出来. 经过分析发现,在Access数据库中,name这个字段的长度是15,因为我知道Access ...

  7. Linux:去除每一行行首的空格

    如下命令: sed 's/^ *//' file1.txt > file2.txt

  8. Android Eclipse 安装教程 hosts替换

    http://www.cnblogs.com/Potato-lover/p/5582542.html 第一步,也是最为关键的一步——修改hosts文件 为什么说是最关键的一步呢?因为接下来的操作,我们 ...

  9. my97DatePicker选择年、季度、月、周、日(转)

    My97DatePicker是一款非常灵活好用的日期控件.使用非常简单. 下面总结下使用该日历控件选择年.季度.月.周.日的方法. 1.选择年 <input id="d1212&quo ...

  10. phpstudy vhosts.conf 文件配置 记录下!

    <VirtualHost _default_:80>DocumentRoot "D:\phpStudy\WWW" <Directory "D:\phpS ...