package com.wenbronk.hbase; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.client.HBaseAdmin; import java.math.BigInteger; /** * hbase调优 */ public class UpperHbase { public static boolean createTable(HBaseAdmin admin, HTableDescriptor table, byte[][] splits) { try { // 添加二维数组对region进行查分 admin.createTable(table, splits); } catch (Exception e) { e.printStackTrace(); } return false; } /** * 获取二维数组 */ public static byte[][] getHexSplits(String startKey, String endKey, int numRegions) { // //start:001,endkey:100,10region [001,010] [011,020] byte[][] splits = new byte[numRegions - 1][]; BigInteger lowestKey = new BigInteger(startKey, 16); BigInteger highestKey = new BigInteger(endKey, 16); BigInteger range = highestKey.subtract(lowestKey); BigInteger regionIncrement = range.divide(BigInteger.valueOf(numRegions)); lowestKey = lowestKey.add(regionIncrement); for (int i = 0; i < numRegions - 1; i++) { BigInteger key = lowestKey.add(regionIncrement.multiply(BigInteger.valueOf(i))); byte[] b = String.format("%016x", key).getBytes(); splits[i] = b; } return splits; } }
HBase中row key用来检索表中的记录,支持如下三种方式:html
在HBase中,row key能够是任意字符串,最大长度64KB,实际应用中通常为10~100bytes,存为byte[]字节数组,通常设计成定长的。java
row key是按照字典序存储,所以,设计row key时,要充分利用这个排序特色,将常常一块儿读取的数据存储到一块,将最近可能会被访问的数据放在一块。算法
举个例子:若是最近写入HBase表中的数据是最可能被访问的,能够考虑将时间戳做为row key的一部分,因为是字典序排序,因此可使用Long.MAX_VALUE - timestamp做为row key,这样能保证新写入的数据在读取时能够被快速命中。apache
一、 大小越小越好编程
二、 值根据功能需求决定api
三、 Row最好有散列原则。数组
a) 取反 : 001 => 100, 002 => 200缓存
b) Hash: 经过hash算法进行散列安全
不要在一张表里定义太多的column family(一个列族对应一个store, 有多个storeFile, 进行提交合并时, 全部的store一块进行, 瞬间并发太大)。目前Hbase(2.x) 并不能很好的处理超过2~3个column family的表。由于某个column family在flush的时候,它邻近的column family也会因关联效应被触发flush,最终致使系统产生更多的I/O。感兴趣的同窗能够对本身的HBase集群进行实际测试,从获得的测试结果数据验证一下。网络
建立表的时候,能够经过HColumnDescriptor.setInMemory(true)将表放到RegionServer的缓存中,保证在读取的时候被cache命中。
建立表的时候,能够经过HColumnDescriptor.setMaxVersions(int maxVersions)设置表中数据的最大版本,若是只须要保存最新版本的数据,那么能够设置setMaxVersions(1)。
建立表的时候,能够经过HColumnDescriptor.setTimeToLive(int timeToLive)设置表中数据的存储生命期,过时数据将自动被删除,例如若是只须要存储最近两天的数据,那么能够设置setTimeToLive(2 * 24 * 60 * 60)。
在HBase中,数据在更新时首先写入WAL 日志(HLog)和内存(MemStore)中,MemStore中的数据是排序的,当MemStore累计到必定阈值时,就会建立一个新的MemStore,而且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。于此同时, 系统会在zookeeper中记录一个redo point,表示这个时刻以前的变动已经持久化了(minor compact)。
StoreFile是只读的,一旦建立后就不能够再修改。所以Hbase的更新实际上是不断追加的操做。当一个Store中的StoreFile达到必定的阈值后,就会进行一次合并(major compact),将对同一个key的修改合并到一块儿,造成一个大的StoreFile,当StoreFile的大小达到必定阈值后,又会对 StoreFile进行分割(split),等分为两个StoreFile。
因为对表的更新是不断追加的,处理读请求时,须要访问Store中所有的StoreFile和MemStore,将它们按照row key进行合并,因为StoreFile和MemStore都是通过排序的,而且StoreFile带有内存中索引,一般合并过程仍是比较快的。
实际应用中,能够考虑必要时手动进行major compact,将同一个row key的修改进行合并造成一个大的StoreFile。同时,能够将StoreFile设置大些,减小split的发生。
hbase为了防止小文件(被刷到磁盘的menstore)过多,以保证保证查询效率,hbase须要在必要的时候将这些小的store file合并成相对较大的store file,这个过程就称之为compaction。在hbase中,主要存在两种类型的compaction:minor compaction和major compaction。
minor compaction:的是较小、不多文件的合并。
major compaction 的功能是将全部的store file合并成一个,触发major compaction的可能条件有:major_compact 命令、majorCompact() API、region server自动运行(相关参数:hbase.hregion.majoucompaction 默认为24 小时、hbase.hregion.majorcompaction.jetter 默认值为0.2 防止region server 在同一时间进行major compaction)。hbase.hregion.majorcompaction.jetter参数的做用是:对参数hbase.hregion.majoucompaction 规定的值起到浮动的做用,假如两个参数都为默认值24和0,2,那么major compact最终使用的数值为:19.2~28.8 这个范围。
一、 关闭自动major compaction
二、 手动编程major compaction
Timer类,contab
minor compaction的运行机制要复杂一些,它由一下几个参数共同决定:
hbase.hstore.compaction.min :默认值为 3,表示至少须要三个知足条件的store file时,minor compaction才会启动
hbase.hstore.compaction.max 默认值为10,表示一次minor compaction中最多选取10个store file
hbase.hstore.compaction.min.size 表示文件大小小于该值的store file 必定会加入到minor compaction的store file中
hbase.hstore.compaction.max.size 表示文件大小大于该值的store file 必定会被minor compaction排除
hbase.hstore.compaction.ratio 将store file 按照文件年龄排序(older to younger),minor compaction老是从older store file开始选择
经过调用HTable.setAutoFlush(false)方法能够将HTable写客户端的自动flush关闭,这样能够批量写入数据到HBase,而不是有一条put就执行一次更新,只有当put填满客户端写缓存时,才实际向HBase服务端发起写请求。默认状况下auto flush是开启的。
经过调用HTable.setWriteBufferSize(writeBufferSize)方法能够设置HTable客户端的写buffer大小,若是新设置的buffer小于当前写buffer中的数据时,buffer将会被flush到服务端。其中,writeBufferSize的单位是byte字节数,能够根据实际写入数据量的多少来设置该值。
在HBae中,客户端向集群中的RegionServer提交数据时(Put/Delete操做),首先会先写WAL(Write Ahead Log)日志(即HLog,一个RegionServer上的全部Region共享一个HLog),只有当WAL日志写成功后,再接着写MemStore,而后客户端被通知提交数据成功;若是写WAL日志失败,客户端则被通知提交失败。这样作的好处是能够作到RegionServer宕机后的数据恢复。
所以,对于相对不过重要的数据,能够在Put/Delete操做时,经过调用Put.setWriteToWAL(false)或Delete.setWriteToWAL(false)函数,放弃写WAL日志,从而提升数据写入的性能。
值得注意的是:谨慎选择关闭WAL日志,由于这样的话,一旦RegionServer宕机,Put/Delete的数据将会没法根据WAL日志进行恢复。
经过调用HTable.put(Put)方法能够将一个指定的row key记录写入HBase,一样HBase提供了另外一个方法:经过调用HTable.put(List<Put>)方法能够将指定的row key列表,批量写入多行记录,这样作的好处是批量执行,只须要一次网络I/O开销,这对于对数据实时性要求高,网络传输RTT高的情景下可能带来明显的性能提高。
hbase.client.scanner.caching配置项能够设置HBase scanner一次从服务端抓取的数据条数,默认状况下一次一条。经过将其设置成一个合理的值,能够减小scan过程当中next()的时间开销,代价是scanner须要经过客户端的内存来维持这些被cache的行记录。
有三个地方能够进行配置:1)在HBase的conf配置文件中进行配置;2)经过调用HTable.setScannerCaching(int scannerCaching)进行配置;3)经过调用Scan.setCaching(int caching)进行配置。三者的优先级愈来愈高。
scan时指定须要的Column Family,能够减小网络传输数据量,不然默认scan操做会返回整行全部Column Family的数据。
经过scan取完数据后,记得要关闭ResultScanner,不然RegionServer可能会出现问题(对应的Server资源没法释放)。
HTable是HBase客户端与HBase服务端通信的Java API对象,客户端能够经过HTable对象与服务端进行CRUD操做(增删改查)。它的建立很简单:
Configuration conf = HBaseConfiguration.create(); HTable table = new HTable(conf, "tablename"); //TODO CRUD Operation……
HTable使用时的一些注意事项:
1. 规避HTable对象的建立开销
由于客户端建立HTable对象后,须要进行一系列的操做:检查.META.表确认指定名称的HBase表是否存在,表是否有效等等,整个时间开销比较重,可能会耗时几秒钟之长,所以最好在程序启动时一次性建立完成须要的HTable对象,若是使用Java API,通常来讲是在构造函数中进行建立,程序启动后直接重用。
2. HTable对象不是线程安全的
HTable对象对于客户端读写数据来讲不是线程安全的,所以多线程时,要为每一个线程单首创建复用一个HTable对象,不一样对象间不要共享HTable对象使用,特别是在客户端auto flash被置为false时,因为存在本地write buffer,可能致使数据不一致。
3. HTable对象之间共享Configuration
HTable对象共享Configuration对象,这样的好处在于:
所以,与如下这种方式相比:
HTable table1 = new HTable("table1"); HTable table2 = new HTable("table2");
下面的方式更有效些:
Configuration conf = HBaseConfiguration.create(); HTable table1 = new HTable(conf, "table1"); HTable table2 = new HTable(conf, "table2");
备注:即便是高负载的多线程程序,也并无发现由于共享Configuration而致使的性能问题;若是你的实际状况中不是如此,那么能够尝试不共享Configuration。
HTablePool能够解决HTable存在的线程不安全问题,同时经过维护固定数量的HTable对象,可以在程序运行期间复用这些HTable资源对象。
Configuration conf = HBaseConfiguration.create(); HTablePool pool = new HTablePool(conf, 10);
1. HTablePool能够自动建立HTable对象,并且对客户端来讲使用上是彻底透明的,能够避免多线程间数据并发修改问题。
2. HTablePool中的HTable对象之间是公用Configuration链接的,可以能够减小网络开销。
HTablePool的使用很简单:每次进行操做前,经过HTablePool的getTable方法取得一个HTable对象,而后进行put/get/scan/delete等操做,最后经过HTablePool的putTable方法将HTable对象放回到HTablePool中。
下面是个使用HTablePool的简单例子:
public void createUser(String username, String firstName, String lastName, String email,
String password, String roles) throws IOException { HTable table = rm.getTable(UserTable.NAME); Put put = new Put(Bytes.toBytes(username)); put.add(UserTable.DATA_FAMILY, UserTable.FIRSTNAME, Bytes.toBytes(firstName)); put.add(UserTable.DATA_FAMILY, UserTable.LASTNAME, Bytes.toBytes(lastName)); put.add(UserTable.DATA_FAMILY, UserTable.EMAIL, Bytes.toBytes(email)); put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS, Bytes.toBytes(password)); put.add(UserTable.DATA_FAMILY, UserTable.ROLES, Bytes.toBytes(roles)); table.put(put); table.flushCommits(); rm.putTable(table); }
Hbase和DBMS比较:
查询数据不灵活:
1, 不能使用column之间过滤查询
2, 支持全文索引。使用solr和hbase整合完成全文搜索。
a) 使用MR批量读取hbase中的数据,在solr里面创建索引(no store)之保存rowkey的值。
b) 根据关键词从索引中搜索到rowkey(分页)
c) 根据rowkey从hbase查询全部数据