版权声明:本套技术专栏是做者(秦凯新)平时工做的总结和升华,经过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何技术交流,可随时联系。算法
网上的Hbase调优资料良莠不齐,实在是不忍卒读,有些都是拼凑且版本过期的东西,我这里决定综合全部优质资源进行整合,写一份最全,最有深度,不过期的技术博客。辛苦成文,各自珍惜,谢谢!版权声明:禁止转载,欢迎学习,侵权必究!数据库
数据块编码主要是针对 Key/Value 中的 Key 进行编码,减小 Key 存储所占用的空间,由于不少 Key 的前缀都是重复的。举例以下:缓存
若是使用前缀编码做为数据块编码方式,那么它只会存储第一个 Key 的完整字符串,后面的 key 只存储跟第一个 key 的差别字符,从新编码过的数据以下所示。以下例所示:服务器
最上面的key值为:
myrow001:mycf:col1
针对于key而言,后续的能够之存储差别值:
myrow001:mycf:col2就变为2
myrow001:mycf:col3就变为3
myrow002:mycf:col3就变为 2:mycf:col1 (变为最上面)
myrow003:mycf:col3就变为 3
复制代码
差别编码方式默认是不启用的。为何?由于太慢了,每条数据都要这样计算一下,获取数据的速度很慢。除非你要追求极致的压缩比,可是不考虑读取性能的时候可使用它,好比你想要把这部分数据看成归档数据的时候,能够考虑使用差别编码。网络
差别编码(Diff)比前缀编码更进一步,差别编码甚至把如下字段也一块儿进行了差别化的编码。app
键长度(KeyLen);
值长度(ValueLen);
时间戳(Timestamp),也便是Version;
类型(Type),也便是键类型。
复制代码
采用了差别编码后的 KeyValue 结构为:性能
1 byte:标志位;
1-5 bytes:Key 长度(KeyLen);
1-5 bytes:Value 长度(ValLen);
1-5 bytes:前缀长度(Prefix Len);
... bytes:剩余的部分;
... bytes:真正的 Key 或者只是有差别的 key 后缀部分;
1-8 bytes:时间戳(timestamp)或者时间戳的差别部分;
1 byte:Key 类型(type);
... bytes:值(value)。
复制代码
版权声明:本套技术专栏是做者(秦凯新)平时工做的总结和升华,经过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何技术交流,可随时联系。学习
快速差别编码(Fast Diff)借鉴了 Diff 编码的思路,也考虑到了差别编码速度慢的致命缺陷。快速差别编码的 KeyValue 结构跟差别编码如出一辙,只有 Flag 的存储规则不同,而且优化了 Timestamp 的计算。Fast Diff 的实现比 Diff 更快,也是比较推荐的算法优化
不过这个“快速”只是相对原本的差别算法而言的,因为仍是有不少计算过程存在,因此快速差别算法的速度依然属于比较慢的。ui
前缀树编码(Prefix Tree)是前缀算法的变体,它是 0.96 版本以后才加入的特性。前缀树编码最大的做用就是提升了随机读的能力,可是其复杂的算法相对地下降了写入的速度,消耗了更多的 CPU 资源,使用时须要在资源的消耗和随机读的性能之间进行取舍。
压缩器的做用是能够把 HBase 的数据按压缩的格式存储,这样能够更节省磁盘空间。固然这彻底是可选的,不过推荐你们仍是安装 Snappy 压缩器,这是 HBase 官方目前排名比较高的压缩器。
hbase> alter 'mytable',{NAME =>'mycf',COMPRESSION=>'snappy'}
Snappy 是 Google 开发的压缩器,有如下特色:
快速:压缩速度达到 250MB/s;
稳定:已经用于 Google 多个产品长达数年;
健壮:Snappy 的解压器能够保证在数据被损坏的时候也不会太糟;
免费开源。
复制代码
推荐将 Configuration 作为单例;Connection 随建随用,用完及时关闭。
public static void main(String[] args) throws URISyntaxException, IOException {
//获取配置文件
Configuration config = HBaseConfiguration.create();
config.addResource(new Path(ClassLoader.getSystemResource("hbase-site.xml").toURI()));
config.addResource(new Path(ClassLoader.getSystemResource("core-site.xml").toURI()));
//建立链接
try (Connection connection = ConnectionFactory.createConnection(config); Admin admin = connection.getAdmin()) {
//定义表名
TableName tableName = TableName.valueOf("tb1");
//定义列族
ColumnFamilyDescriptor myCf = ColumnFamilyDescriptorBuilder.of("cf1");
//定义表
TableDescriptor table = TableDescriptorBuilder.newBuilder(tableName).setColumnFamily(myCf).build();
//执行建立表动做
admin.createTable(table);
} catch (Exception ex) {
ex.printStackTrace();
}
}
复制代码
// 已废弃,不推荐使用
// HTable table = new HTable(config, "mytable");
// 官方推荐
try (Connection connection = ConnectionFactory.createConnection(config)) {
connection.getTable(TableName.valueOf("tb1"));
}
复制代码
Put put =new Put(Bytes.toBytes("qinkaixinRowkeys"))
put.addColumn(Bytes.toBytes("mycf"),Bytes.toBytes("name"),Bytes.toBytes("ted"));
boolean result = table.checkAndput(
Bytes.toBytes("row3"),
Bytes.toBytes("mycf"),
Bytes.toBytes("name"),
Bytes.toBytes("ted"),
put)
复制代码
保证原子性的状况下,把数据库中的某个列的数字进行数学运算
Table table = connection.getTable(TableName.valueOf("tb1"));
Increment inc = new Increment(Bytes.toBytes("row1"));
inc.addColumn(Bytes.toBytes("cf1"), Bytes.toBytes("age"), 1L);
table.increment(inc);
复制代码
把添加一列的时候同时删除另外一列的操做放在同一个原子操做中
Delete delete =new Delete(Bytes.toBytes("qinkaixinRowkeys"))
delete.addColumn(Bytes.toBytes("mycf"),Bytes.toBytes("name"),Bytes.toBytes("ted"));
Put put =new Put(Bytes.toBytes("qinkaixinRowkeys"))
put.addColumn(Bytes.toBytes("mycf"),Bytes.toBytes("name"),Bytes.toBytes("ted"));
RowMutations rowMutations =new RowMutations(Bytes.toBytes("ted"));
rowMutations.add(delete);
rowMutations.add(put);
table.mutateRow(rowMutations);
复制代码
客户端写缓冲区就是一个在客户端 JVM 里面的缓存机制,能够把多个 Put 操做攒到一块儿经过单个 RPC 请求发送给客户端,目的是节省网络握手带来的 IO 消耗。这个缓冲区能够经过调用 HTable.setAutoFlush(false) 来开启。 最新版的 API 中 setAutoFlush 被废弃了,每一个表自带的 writeBuffer 也被废弃了,可是客户端写缓冲区仍是存在的,只是转而使用 BufferedMutator 对象。
最好不要把针对同一个单元格的 Put 和 Delete 放到同一个 actions 列表里面,由于 HBase 不必定是顺序地执行这些操做的,你可能会获得意想不到的结果。
connection.getTable(TableName.valueOf("tb1")
Table table = connection.getBufferedMutator(TableName.valueOf("mytable"));
bm.mutate(put);
bm.flush();
bm.close();
复制代码
HBase 提供了专门针对批量 put 的操做方法:void put(List puts);其实内部也是用 batch 来实现的。须要注意的是,当一部分数据插入成功,可是另外一部分数据插入失败,好比某个 RegionServer 服务器出现了问题,这时会返回一个 IOException,操做会被放弃,不过插入成功的数据不会被回滚。
对于插入失败的数据,服务器会尝试着再次去插入或者换一个 RegionServer,当尝试的次数大于定义的最大次数会抛出 RetriesExhaustedWithDetailsException 异常,该异常包含了不少错误信息,包括有多少操做失败了,失败的缘由以及服务器名和重试的次数。
如今的 HBase 在扫描的时候已经默认开启了缓存。
每一次的 next() 操做都会产生一次完整的 RPC 请求,而此次 RPC 请求能够获取多少数据是经过 hbase-site.xml 中的 hbase.client.scanner.caching 参数配置的。好比你若是配置该项为 1,那么当你遍历了 10 个结果就会发送 10 次请求,显而易见这是比较消耗性能的,尤为是当单条的数据量较小的时候。
hbase-site.xml参数调优:
<property>
<name>hbase.client.scanner.caching</name>
<value>100</value>
</property>
复制代码
本文解决了连个问题包括HBase数据块编码压缩机制调优以及客户端API 新版本最佳实践
版权声明:本套技术专栏是做者(秦凯新)平时工做的总结和升华,经过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何技术交流,可随时联系。
参考文档及链接:Hbase不睡觉书 及官方文档资料
复制代码
秦凯新 于深圳 201801102252