大数据最佳实践 | HBase客户端

1减少RPC调用的方法

1.1.问题提出

HBase中rowkey是索引,任何对全表的扫描或是统计都需要用到scan接口,一般都是通过next()方法获取数据。而每一个next()调用都会为每行数据生成一个单独的RPC请求,这样会产生大量的RPC请求,性能不会很好。

1.2.解决思路

如果执行一次RPC请求就可以获取多行数据,那肯定会大大提高系统的性能。这一块主要分为面向行级的缓存以及面向列级的缓存:

1)面向行级的缓存

我们可以通过使用扫描缓存方法来实现,不过这个缓存默认是关闭的,要用得打开。在表的层面使用时,这个表所有的扫描实例的缓存都会生效,在扫描层面也只会影响当前的扫描实例。

用户可以使用HTable.setScannerCaching()方法设置表级的扫描缓存,以及使用Scan.setCaching()方法设置扫描级的缓存。

2)面向列级的批量

用户可以使用Scan.setBatch()方法设置返回多少列。

通过组合使用扫描器缓存和批量大小的方式,可以让用户方便的控制扫描一个范围内的行健时所需要的RPC调用次数。

1.3.实践情况

举例如下:

我们建立了一张有两个列族的表,添加了10行数据,每个行的每个列族下有10列。这意味着整个表一共有200列(或单元格,因为每个列只有一个版本),其中每行有20列。公式如下:

RPC请求的次数 =(行数×每行的列数)/Min(每行的列数,批量大小)/扫描器缓存

表说明如下:

缓存

批量处理

Result个数

RPC次数

说明

1

1

200

201

每个列都作为一个Result实例返回。最后还多一个RPC确认扫描完成。

200

1

200

2

每个Result实例都只包含一列的值,不过它们都被一次RPC请求取回(加一次完成检查)。

2

10

20

11

批量参数是一行所包含的列数的一半,所以200列除以10,需要20个Result实例。同时需要10次RPC请求取回(加一次完成检查)。

5

100

10

3

对于一行来讲,这个批量参数太大了,所以一行的20列都被放入了一个Result实例中。同时缓存为5,所以10个Result实例被两次RPC请求取回(加一次完成检查)。

5

20

10

3

同上,不过这次的批量值与一行的列数正好相同,所以输出与上面一种情况相同。

10

10

20

3

这次把表分成了较小的Result实例,但使用了较大的缓存值,所以也是只用了两次RPC请求就取回了数据。

要计算一次扫描操作的RPC请求的次数,用户需要先计算出行数和每行列数的乘积(至少了解大概情况)。然后用这个值除以批量大小和每行列数中较小的那个值。最后再用除得的结果除以扫描器缓存值。

1.4.效果评价

合理的组合使用扫描器缓存和批量大小,可以有效的减少client端和服务器的RPC交互次数,提供系统整体性能。

1.5.注意事项

  • scanner需要通过客户端的内存来维持这些被cache的行记录,合理设置catching大小,防止出现OOM;
  • cache使用的内存计算公式为:并发数×cache数×单个result大小。

2客户端其它最佳实践方法

2.1.问题提出

平常情况下,很多的应用主要是通过使用客户端来访问HBase集群,进而完成业务。因此整个系统的性能有很大一部分依赖于客户端的性能。客户端的开发主要是使用HBase提供的API,往往又由于不同的程序员对API的掌握程度不一,导致了客户端的性能差别很大。

2.2.解决思路

客户端是使用HBase提供的API来完成读写数据,因此我们针对API的使用整理了一些最佳实践。

1)禁止自动刷新

当有大量的写入操作时,使用setAutoFlush(false)方法,确认HTable自动刷新的特性已经被关闭。否则Put实例将会被逐个传送到region服务器。通过HTable.add(Put)添加的Put实例都会添加到一个相同的写入缓存中,如果用户禁用了自动刷新,这些操作直到写缓冲区被填满时才会被送出。如果要显示地刷写数据,用户可以调用flushCommits()方法。调用HTable实例的close()方法也会隐式地调用flushCommits()。

默认的客户端写缓存是2M,我们可以通过修改hbase.client.write.buffer配置来设置大小,以满足应用的需要。

2)使用扫描缓存

如果HBase被用作一个MapReduce作业的输入源,最好将作为MapReduce作业输入扫描器实例的缓存用setCaching()方法设置为比默认值100大得多的值。使用默认的值意味着map任务会在处理每条记录时请求region服务器。例如,将这个值设置为500,则一次可以传送500行数据到客户端进行处理。这里用户需要权衡传输数据的开销和内存的开销,因为缓存更大之后,无论是客户端还是服务器端都将消耗更多内存缓存数据,因此大的缓存并不一定最好。

3)限定扫描范围

当Scan被用来处理大量行时(特别是被用作MapReduce输入源时),注意哪些属性被选中了。如果Scan.addFamily(byte [] family)被调用了,那么特定列族中的所有都将被返回到客户端。

如果只处理列,则应当只有这列被添加到Scan的输入中,如scan.addColumn(byte [] family,byte [] qualifier),因为选中了过多的列将导致大数据集上极大的效率损失。

如果是选择多列,可以使用scan. setFamilyMap(Map<byte[], NavigableSet<byte []>> familyMap)添加多个列族下的多列。

4)关闭ResultScanner

这不会带来性能提升,但是会避免可能的性能问题。如果用户忘记关闭由HTable.getScanner()返回的ResultScanner实例,则可能对服务器端造成影响。

所以建议在在try/catch的finally块中关闭ResultScanner,例如:

   Scan scan = newScan();
   ResultScannerscanner = table.getScanner(scan);
   try {
   for (Resultresult: scanner) {
   //procrss result...
   }
   } catcah (IOExceptione){
   //throwexception
   } finally {
   scanner.close();
   }
   table.close();

5)优化获取行健的方式

当执行一个表的扫描以获取需要的行键时(没有列族、列名、列值和时间戳),在Scan中用setFilter()方法添加一个带MUST_PASS_ALL操作符的FilterList。FilterList中包含FirstKeyOnlyFilter和KeyOnlyFilter两个过滤器,使用以上组合的过滤器将会把发现的第一个KeyValue行键(也就是第一列的行键)返回给客户端,这将会最大程度地减少网络传输。

原文发布于微信公众号 - Spark学习技巧(bigdatatip)

原文发表时间:2018-01-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Felix的技术分享

《一个操作系统的实现》笔记(4)-- Boot&Loader

2657
来自专栏Golang语言社区

几种服务器端IO模型的简单介绍及实现(下)

5、使用事件驱动库libevent的服务器模型 Libevent 是一种高性能事件循环/事件驱动库。 为了实际处理每个请求,libevent 库提供一种事件机制...

2927
来自专栏逍遥剑客的游戏开发

Direct3D资源

3577
来自专栏我的博客

PHP应用技术之——操纵Word

本人测试成功,但是我省略了数据库连接部分代码。其中$Table_Word=word;而word表中字段依次是id、name、qq、add、tel,将代码先留下来...

3285
来自专栏维C果糖

IntelliJ IDEA 之 HelloWorld 项目创建及相关配置文件介绍

在博文“ IntelliJ IDEA 的使用界面介绍 ”中,咱们通过创建一个 Static Web 项目大致了解了 IntelliJ IDEA 的使用界面,接下...

1959
来自专栏linux驱动个人学习

Linux分页机制之分页机制的演变--Linux内存管理(七)

分段,是指将程序所需要的内存空间大小的虚拟空间,通过映射机制映射到某个物理地址空间(映射的操作由硬件完成)。分段映射机制解决了之前操作系统存在的两个问题:

1452
来自专栏向治洪

React Native网络请求

很多移动应用都需要从远程地址中获取数据或资源。你可能需要给某个REST API发起POST请求以提交用户数据,又或者可能仅仅需要从某个服务器上获取一些静态内容—...

28511
来自专栏JavaEdge

GET和POST到底啥区别???

最普遍的答案 我一直就觉得GET和POST没有什么除了语义之外的区别,自打我开始学习Web编程开始就是这么理解的。 可能很多人都已经猜到了,他要的答案是:

1092
来自专栏Golang语言社区

几种服务器端IO模型的简单介绍及实现(下)

5、使用事件驱动库libevent的服务器模型 Libevent 是一种高性能事件循环/事件驱动库。 为了实际处理每个请求,libevent 库提供一种事件机制...

3769
来自专栏Django中文社区

拓展 User 模型

Django 用户认证系统提供了一个内置的 User 对象,用于记录用户的用户名,密码等个人信息。对于 Django 内置的 User 模型, 仅包含以下一些主...

3435

扫码关注云+社区

领取腾讯云代金券