Kudu 是一个基于 Raft 的分布式存储系统,它致力于融合低延迟写入和高性能分析这两种场景
- Kudu 提供了 table 的概念。用户可以建立多个 table,每个 table 都有一个预先定义好的 schema。Schema 里面定义了这个 table 多个 column,每个 column 都有名字,类型,是否允许 null 等。一些 columns 组成了 primary key。
- 分区 Kudu 支持对数据按照 Range 以及 Hash 的方式进行分区。 每个大的 table 都可以通过这种方式将数据分不到不同的 Tablet 上面。
- 三种Fulsh Mode
- AUTO_FLUSH_SYNC 默认,自动flush,同步方法,调用 kuduSession.apply() 时立即写入,在写入完成后才会返回一个 OperationResponse 对象,也只有模式下才会返回该对象,其他的都是返回 null,调用kuduSession.flush() 不会有任何操作。
- AUTO_FLUSH_BACKGROUND 自动后台写入,在apply后会立即返回null,但是写入会在后再自动运行,可能会多条操作用同一个session批量写入,如果缓存条数满了,则会阻塞一会,直至缓冲区有可用的空间在返回。因为可能多条并发写入,所以写入顺序可能是无序的,(猜测当一条数据先插再删、变成先删再插可能就有问题,并没有测试过)。因为后台写入,多以error信息会存储在 session-local buffer 中,通过 kuduSession.countPendingErrors() 访问等待的error数量,kudu.getPendingErrors() 得到error的操作进行重试。调用kuduSession.fulsh()时会阻塞,直至缓冲区为空。
- MANUAL_FLUSH 手动flush,在此状态下调用 apply() 会立即返回null,只有用户调用 kuduSession.fulsh() 的时候才会发送写入操作。如果超过了缓冲区配置的大小,在 apply() 的时候会返回一个error。
kuduSession.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH);
- kudu 原生对impala 做了很好的兼容,用impala可以很方便的使用sql对kudu进行操作,我们在项目中是使用了 impala 和原生 kuduClient 相结合的方式进行。
- impala 和 kuduClient 的选择
- 就查询来说,在我的使用过程中 impala 的查询速度要快于 kuduClient 的 scan。建议使用impala
- insert 的速度都很快 upsert/update/delete 如果用主键的话也都很快,但impala的并发性能比较差,所以这种操作尽量用 kuduClient 的原生 api 进行操作
- 原生 api update、delete、upsert 只能根据主键操作,如果需要其他条件则需要查询一下,拿到主键再进行操作,所以不如impala写sql方便,看具体情况吧,只要 impala 吼得住并发,或做好资源隔离。
- 原生 api 在操作上有些不方便,我们自己封装了一个简单的 spring starter ,还在完善,当然也可以把核心代码拿出来自己封装。链接:https://gitee.com/git-of-Jason/kudu-spring-boot-starter
下面是使用impala和原生api操作kudu中遇到的一点问题
- impala用sql操作 set version = version + 1 ,version 必须是 bigint,因为 version(int)+1 结果是bigint
- json中的 ” 会被转义为 \” 而当作为sql存入的时候 \” 又会被反译 为 ” 所以,再取出来就不是规范的json格式了。。不过这个问题是所有拼接 sql 提交查询的共性问题
//所以把其中的 \ 替换为 \\ , " 替换为 \"
str = str.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"");
// ps : replaceAll 的参数是正则,所以 \\\\ = 正则 \\ = 真正的 \
- kudu 支持最大300列,每个字段不超过64K(包括String类型和二进制类型)。一般每列不建议超过1k,每行不建议超过100K。
impala 大小写不敏感,kudu 大小写敏感,Impala建表时的大小写除了表名其他的所有字段都会变成小写进入kudu
impala 会保存 kudu 的 schema,所以使用impala查询全程大小写混搭无所谓的,但用 kudu 原生api,要注意,表名区分大小写,列名全程小写。
impala 建一个数据库 wx ,建一个表 test,在kudu中没有 数据库的概念,impala会把它声明为, impala::db.tableName -> impala::wx.test
- 使用原生kudu api upsert 时,除了传入主键的几个字段外,not null的字段也必须传入,否则更新不了,如果用 impala 写sql 就会报错的,但用原生 api 正常返回 OperationResponse 里面有错误信息,如果不主动检查是发现不了更新失败的。
- 用原生 api 进行删除操作,只能set 主键作为条件,其他的字段都不能设置,会失败,在 OperationResponse 可以看到错误信息。但如果用 impala 的话,where 条件就无所谓了,更正常 sql 一样, impala 会处理的
使用过程中对默认参数的调整
- impala 文件操作符数量(ulimit) 32,768 -> 50000 观察中
- kudu
Kudu Tablet Server Block Cache Capacity
block_cache_capacity_mb 512M -> 1G
Kudu Tablet Server Hard Memory Limit
memory_limit_hard_bytes 默认 4G -> 30 G 这个参数尽量大,机器 80% 左右
pom.xml
额,正常不用这么复杂,只是我们用的 cloudera 公司的版本所以需要引,cloudera 公司的私有库
<properties>
<cdh.hive.version>1.1.0-cdh5.10.0</cdh.hive.version>
<cdh.hadoop.version>2.6.0-cdh5.10.0</cdh.hadoop.version>
</properties>
<!-- CDH 仓库 -->
<repositories>
<repository>
<id>cdh.repo</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
<name>Cloudera Repositories</name>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>cdh.snapshots.repo</id>
<url>https://repository.cloudera.com/artifactory/libs-snapshot-local</url>
<name>Cloudera Snapshots Repository</name>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>${cdh.hive.version}</version>
<!--hive 依赖的 jar 包冲突,排除掉-->
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.hive</groupId>
<artifactId>hive-shims</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${cdh.hadoop.version}</version>
</dependency>
</dependencies>