大数据最佳实践 | 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行键(也就是第一列的行键)返回给客户端,这将会最大程度地减少网络传输。
- 如何正确的猜拳:反事实遗憾最小化算法
- 使用python中的Numpy进行t检验
- 实操 Web Cache
- 怎样制作RPM包
- 框架设计原则和规范(完)
- 这或许是对小白最友好的python入门了吧——9,数字深入体验
- WebAPI返回数据类型解惑 以及怎样解决Extjs无法解析返回的xml
- 图像处理:利用神经网络生成新图像和修复旧图像
- 这或许是对小白最友好的python入门了吧——8,初识for语句
- Extjs 项目中常用的小技巧,也许你用得着(3)
- 对于Ext.data.Store 介紹 与总结,以及对以前代码的重构与优化
- 数据库与图片完美解决方案
- 数据库进程间通信解决方案
- 【实践操作】在iPhone上创建你的第一个机器学习模型
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- gitlab内存消耗大,频繁出现502错误的解决办法
- Java基于POI实现excel任意多级联动下拉列表——支持从数据库查询出多级数据后直接生成【附源码】
- Elasticsearch 通过Scroll遍历索引,构造pandas dataframe 【Python多进程实现】
- 【Java】 NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException、ArrayIndexOutOfBoundsE
- Meow攻击删除不安全(开放的)的Elasticsearch(及MongoDB) 索引,建一堆以Meow结尾的奇奇怪怪的索引(如:m3egspncll-meow)
- MySQL LOAD DATA INFILE—从文件(csv、txt)批量导入数据
- MySQL 快速删除大量数据(千万级别)的几种实践方案——附源码
- 什么样的代码是好代码?
- Elastic search集群新增节点(同一集群,同一物理机)
- Tesseract-OCR 4.1.0 安装和使用— windows及CentOS
- Java 大小端转换(基于ByteBuffer)
- Tika结合Tesseract-OCR 实现光学汉字识别(简体、宋体的识别率百分之百)—附Java源码、测试数据和训练集下载地址
- 阿里《JAVA实习生入职测试题—2019最新》之答案详解(连载一)
- 阿里《JAVA实习生入职测试题—2019最新》之答案详解(连载二)
- 动态生成RDLC报表