列族管理
-
在java中HColumnDescriptor代表列族,但是已经过时了,新代替的是ColumnFamilyDescriptorBuilder来创建列族描述符
... ColumnFamilyDescriptor newc = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("newc")).build(); Admin admin = connection.getAdmin(); TableName tableName = TableName.valueOf("test"); admin.addColumnFamily(tableName,newc); ...
Admin
- 是管理功能最重要的部分,HBaseAdmin是Admin接口的实现类,并实现了自动关闭资源的接口
- 下面是一些比较重要的属性设置
-
数据生存时间
- 即数据插入后存在表中的时间,到时间时候该数据将被清理掉,是以秒来计时的
ColumnFamilyDescriptor newc = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("newc")) .setTimeToLive(10).build();
- 当数据插入十秒内,数据是存在的,之后数据就会被删除,Put也有一个设置存活时间的,不过单位是毫秒
put.setTTL(12L);
-
设置历史版本数
setMinVersions(1); setMaxVersions(2);
-
布隆过滤器
- 之前我们说过,数据最终是以HFile形式存储在HDFS的,HFile数据一般比较大,那么在HFile中查询数据肯定是比较慢的,所以HFile使用了块索引机制,原理就是在HFile中增加一部分,单独存储该HFile中的所有行键,这样扫描器就可以通过块索引来找行键,当找到行键的时候再去具体的位置获取该行的其他信息
- 虽然引入了块索引,但是查询速度依旧是很慢的,因为要把所有的行键按顺序查找过去,还是需要很长时间的,所以引入了布隆过滤器
- 布隆过滤器可以知道元素在结合中是否"不存在"或者"可能存在",也就是说如果布隆过滤器认为该元素不存在那么就是不存在,这样可以极大的加速检索速度,因为跳过了扫描不必要的块索引
-
如上图,结合布隆过滤器就直接滤过了前两个块索引,从而加快查询速度,布隆过滤器有两种工作方式
- 行模式:针对行进行过滤
- 行列模式:针对行列进行过滤
- 行列模式要比行模式大,因为比起行模式,行列模式要额外存储列信息
- 布隆过滤器默认是开启的,并且默认为行模式,可以通过ColumnFamilyDescriptor来设置
setBloomFilterType(BloomType bt);
- 可选枚举有:NONE:关闭布隆过滤器=>ROW:行模式=>ROWCOL:行列模式
-
块缓存
- BlockCache使用内存来记录数据,适用于提升读取性能,当开启块缓存后,HBase会优先从块缓存中查询是否有记录,如果没有采取检索硬盘上的HFile,详细的以后再说
-
大字段
- 在HBase中存储图片文档之类的较大文件的时候,它叫做MOB,即Medium Object,中等对象的意思,当文件大于100KB小于10MB就可以被视为MOB,Hbase存储MOB字段的时候也是把该文件存储到HDFS,而表中只是存储了该文件的链接
-
该特性只有在HFile版本3以上才有,所以可以打开hbase-default.xml确认一下
<property> <name>hfile.format.version</name> <value>3</value> </property>
- 设置列族的MOB方式
.setMobEnabled(true); //默认关闭,开启MOB .setMobThreshold(..); // 设置Mob值,默认为100KB .setMobCompactPartitionPolicy(..); // 开启MOB的分区策略
表管理
- 大多数属性的设置都是在列族上,所以表管理设置的属性比列族要少,之前使用的HTableDescriptor一样过期了,现在推荐使用的是TableDescriptorBuilder来构建TableDescriptor对象
-
最大文件尺寸
- 即设置region的最大尺寸,如果region的大小超过了定义值,则会触发region拆分,默认是10GB才会拆分,设置方式为
TableDescriptorBuilder.newBuilder(TableName.valueOf("test")).setMaxFileSize(1L).build();
-
只读模式
- 设置为只读模式后,如果尝试插入数据后会报错的
builder.setReadOnly(true);
-
MemStore刷写值
- 设置刷写的最大值,当MemStore存储的数据大于该值就会触发刷写flush,默认64MB
builder.setMemStoreFlushSize(1L);
-
操作列族
- 即对列族的增删改
builder.removeColumnFamily(); builder.modifyColumnFamily(); builder.setColumnFamily();
- 还有一些方法可以尝试使用,这里就不列举了
Region管理
-
关闭Region
- 关闭Region之前,我们要知道数据存放在哪个Region上面,进入hbase shell,然后执行
hbase(main):010:0> scan 'hbase:meta',{FILTER=>"(PrefixFilter('test'))"}
- 输出
- 如上可以看出,表只占用了一个Region,并且得知了红框标识的region的信息,比如
- region的name:test,,1544507358220.596ec67dce5ce39adc1654f50c82e4f8.
- 所在服务器:hd4
- 服务器端口:16020
- 启动码(serverstartcode):1544588644294等一些其他信息
- 知道了这些,我们就可以用这些信息来关闭指定的region
Admin admin = connection.getAdmin();
admin.unassign(Bytes.toBytes("test,,1544507358220.596ec67dce5ce39adc1654f50c82e4f8."),true);
- 在执行完毕后,这张表将不能再进行数据查询,会报错:
RetriesExhaustedException
- 既然是假un的方法,那么肯定有相反的方法使其region上线
admin.assign(Bytes.toBytes("test,,1544507358220.596ec67dce5ce39adc1654f50c82e4f8."))
- 在进行查询就可以出数据了
-
查询所有RegionServer列表
Map<ServerName, ServerMetrics> liveServerMetrics = admin.getClusterMetrics().getLiveServerMetrics(); admin.getClusterMetrics().getDeadServerNames().forEach(System.out::println);
- admin.getClusterMetrics()返回的ClusterMetrics对象可以get到很多东西,比如集群id之类的信息
-
查询regionserver下所有region列表
List<RegionInfo> test = admin.getRegions(TableName.valueOf("test")); List<RegionInfo> test2 = admin.getRegions(ServerName.parseServerName("hd4,16020,1544588644294"));
- 这个方法可以获取属于一个表的所有region和一个regionserver上的所有region,第二个方法的ServerName字符串是由:
所在服务器:所在服务器端口,启动码(serverstartcode)
组成的,上面ServerName使用的方法也可以换为ServerName.valueof
- 这个方法可以获取属于一个表的所有region和一个regionserver上的所有region,第二个方法的ServerName字符串是由:
- 对于admin操作region其他api还有很多,包括移动region,切分region,强制刷新region到hdfs上等一系列方法,可以用到的时候在查
快照管理
- 快照就是某一时刻的结构和数据,可以使用快照来将某个表回复到某个时刻的结构和数据,而且不需要担心创建和恢复的过程会很慢,这个过程只需要数秒就可以完成
- 是怎么做到这么快恢复的?,快照只是保存了一份文件列表,通过修改表示所连接的文件来改变表的数据,这样做会相当快并且不消耗额外的磁盘空间
-
使用:首先要确认是否开启了快照功能,检查hbase-site.xml和hbase-default.xml文件
<property> <name>hbase.snapshot.enabled</name> <value>true</value> </property>
-
下面将是列出数据库中的快照列表的操作
List<SnapshotDescription> snapshotDescriptions = admin.listSnapshots();
- 也可以使用
listSnapshots(Pattern pattern)
来匹配快照 -
目前hbase中是没有快照的,现在我们新建一个表,并插入数据
hbase(main):007:0> put 'test','row1','cf:name','wzq'
-
然后我们创建此表的快照
admin.snapshot("mytest_snapshot",TableName.valueOf("test"));
-
这时候再次查看将出现一个新建快照,快照的作用是恢复数据,现在我们来重新插入一条cf:name以覆盖原来的值
hbase(main):012:0> put 'test','row1','cf:name','wangziqiang'
-
好了我们现在用快照恢复一下,在使用快照恢复的时候需要先将表禁用掉,然后才能进行快照恢复,否则会就爆
TableNotDisabledException
admin.disableTable(tableName); admin.restoreSnapshot("mytest_snapshot"); admin.enableTable(tableName);
- 当执行完毕,数据就已经恢复原样了,快照的操作还有删除快照等方法,用到的时候直接查询就行了
均衡器
- 当hbase遇到负载均衡的问题的时候,hbase提供了一个均衡器用于自动均衡各个RegionServer之间的压力,方法就是移动Region到不同的RegionServer上以平摊压力
- 最早的内置均衡器是SimpleLoadBalancer,之后被StochasticLoadBalancer替代,现在是RegionServer Group 技术,不过参考资料中的是StochasticLoadBalancer,所以在这简单的记一下,之后再去了解RegionServer Group
-
StochasticLoadBalancer在做负载均衡的时候同时考虑了以下5个因 素
- Region Load:Region的负载
- Table Load:表的负载
- DataLocality:数据本地化
- MemStore Sizes:MemStore的大小
- StoreFile Size:StoreFile大小
-
下面是均衡器的相关参数
- hbase.balancer.period:均衡器的执行周期,默认5分钟执行一次,均衡器会启动一个叫BalancerChore的线程,该线程会定时去扫描是否有RegionServer需要做重均衡rebalancer
- hbase.regions.slop:负载容忍值,默认0.01,这个参数是有关于判断RegionServer是否需要被均衡的,计算公式是
avg+(avg*slop)regions
,其中avg就是每个regionserver上region的平均数,如果超过了这个结果值,那么就开始进行负载均衡 - hbase.master.loadbalancer.class:均衡器的实现类,默认为
StochasticLoadBalancer
-
下面是admin中balancer的几个方法
//调用平衡器.将运行平衡器,如果区域要移动,它将继续执行并执行重新分配 admin.balance(); //打开或关闭balancer,异步还是同步 admin.balancerSwitch(boolean onOrOff, boolean synchronous);
标准化器
- 用来标准化Region的尺寸的,如果该Region不是标准的Region,那么标准化器就会改变它
- 怎么才算是不标准的region?是否标准是指region的大小,标准化器首先会计算出某个表的平均region大小,当某个region太大或太小了就成为不标准的region,所以最终是把region的大小控制在一个相对稳定的大小范围
-
如果遇到大region肯定要拆分region,而小region就需要合并,所以就会引发拆分合并风暴
- 拆分/合并风暴(split/merge storms)指在某种情况下拆分了某 几个Region后,系统达到了某个阈值,这个阈值会触发Region的合并,于是Region开始合并,但是合并后又触发了另一个阈值,该阈值导致 HBase开始拆分Region,如此循环往复,造成了一个不断拆分/合并的死 循环,大量地消耗HBase的性能
-
有很多因素可以影响region的拆分和合并
- 均衡器定义的hbase.region.slop偏移量
- 拆分region的策略定义
<property> <name>hbase.regionserver.region.split.policy</name> <value> org.apache.hadoop.hbase.regionserver.SteppingSplitPolicy </value> </property>
- 单个region定义的最大文件大小
<property> <name>hbase.hregion.max.filesize</name> <value>10737418240</value> </property>
-
那么标准化器到底是做什么呢
- 获取该表的所有region
- 计算出region的平均大小
- 如果region大小大于平均大小的两倍,就会被拆分
- 不断合并最小的两个region,只要最小的两个region大小之和小于平均大小,这两个region就会被合并
- 空region不参与标准化过程,即size<1MB
-
Admin操作标准化器的方法
//如果标准化器程序运行,返回true,否则返回false。 admin.normalize(); admin.normalizerSwitch(boolean);//打开或者关闭标准化器 admin.isNormalizerEnabled();//是否开启
目录管理器
- 目录是指hbase:meta表中存储的region信息,当hbase在拆分或者合并的时候,为了确保数据不丢失都会保留原来的region信息,等拆分或者合并过程结束后,再使用目录管理器catalog janitor来清理这些旧region信息
-
现在有一个region需要拆分,拆分过程如下
- 创建两个子region
- 将数据分成两份复制到子region中
- 删除父region
- 由于region的创建需要在hbase:meta表中有对应的信息,所以当子region创建出来后,hbase:meta表的数据数据会多出两个子region信息来,当父region被切分完之后,目录管理器就将父region的信息删除到了,然后就是两个region上线使用
-
admin操作目录管理器使用
//开启或者关闭 admin.catalogJanitorSwitch(boolean); //目录管理是否可用 admin.isCatalogJanitorEnabled(); //执行目录管理操作 admin.runCatalogJanitor();
ClusterStatus & ServerLoad
- 之前我们已经获得过ClusterStatus对象,这个类可以获取关于集群了所有状态信息,比如regionserver的数量,region的数量等,api无非就是getxxxSize或者getxxxCount,这里就不贴代码了
-
ServerLoad是服务器负载对象,可以通过这个对象获得内存使用情况,磁盘使用情况等,如果想获取更详细的信息可以访问
http://192.168.230.201:60010/jmx
,端口是下面属性配置的<property> <name>hbase.master.info.port</name> <value>60010</value> </property>
- 对于Admin其他的操作,以后用到了在总结
可见性标签管理
-
可见性标签(Visibility Labels)是一串逻辑表达式字符串,用 于标定数据的访问可见,即对数据的简单权限限制
- developer:只有拥有developer标签的用户可见该数据
- !developer:拥有developer标签的用户看不见该数据
- (manager|developer)&!market:拥有manager标签或者 developer标签,并且同时要没有market标签的用户,才看得见 该数据
-
确认HFile在版本3之上才可以,并且这个功能默认是关闭的,所以需要打开此功能,编辑hbase-site.xml
<property> <name>hbase.security.authorization</name> <value>true</value> </property> <property> <name>hbase.coprocessor.region.classes</name> <value>org.apache.hadoop.hbase.security.visibility.VisibilityController</value> </property> <property> <name>hbase.coprocessor.master.classes</name> <value>org.apache.hadoop.hbase.security.visibility.VisibilityController</value> </property>
-
首先我们看一下系统内是否有自带的标签
VisibilityLabelsProtos.ListLabelsResponse response = VisibilityClient.listLabels(connection,".*"); response.getLabelList().forEach(System.out::println);
-
什么都没输出,说明是没有自带的标签的,所以这些标签都需要我们去添加,既然是涉及到了hbase中的信息修改,就必须有权限才可以进行修改labels表,如果不信可以尝试进行添加labels
VisibilityClient.addLabel(connection,"myLabel");
-
上面执行完毕并不会报错,上面代码会返回一个VisibilityLabelsProtos.VisibilityLabelsResponse对象,输出他你将得到一段错误信息
result { exception { name: "org.apache.hadoop.hbase.security.AccessDeniedException" value: "org.apache.hadoop.hbase.security.AccessDeniedException: User \'qidai\' is not authorized to perform this action.\n\tat org.apache.hadoop.hbase.security.visibility.VisibilityController.checkCallingUserAuth(VisibilityControll... } }
-
所以就必须要有权限才可以,使用权限你当然要有身份,当你调用api时你会产生一个身份,在hbase shell的时候,可以通过whoami来看自己是谁
hbase(main):006:0> whoami root (auth:SIMPLE) groups: root Took 0.0191 seconds
-
而当java调用的使用,是取的系统的用户名
System.getProperty("user.name");
-
当操作标签库等ACL一系列的安全相关的内容时,需要超级用户才可以,所以现在需要将自己的用户名添加到超级用户组,编辑hbase-site.xml
<property> <name>hbase.superuser</name> <value>qidai</value> </property>
-
设置完毕后重启hbase,然后再执行添加
result { }
-
上面是原来返回错误的那个对象输出的结果,看到里面已经没有出错了,所以现在来看一下labels是否添加进去了,在hbase shell 中这样查看
hbase(main):002:0> list_labels myLabel Took 1.8542 seconds => #<Java::JavaUtil::Collections::UnmodifiableRandomAccessList:0x5855b0ed>
- 到这就说明添加成功了
-
好了到这我们规划三个角色,tom,wzq,sam
- tom有developer标签
- wzq有manager标签
- sam没有标签
-
最后我们添加带有标签的单元格,然后分别用三个用户查看数据观察效果
- 添加tom,wzq,sam用户
[root@hd1 ~]# useradd tom [root@hd1 ~]# useradd wzq [root@hd1 ~]# useradd sam
- 如果你搭建环境的时候使用的是root的话,那么你新建的用户是不能使用hbase的因为有linux权限,简单方式是可以chmod 一下,然后就可以了,就去之后执行whoami,发现我就是tom,角色没问题后,往下继续
-
为用户添加标签,首先要有这个label所以先添加developer和manager标签
VisibilityClient.addLabel(connection,"developer"); VisibilityClient.addLabel(connection,"manager");
-
为用户添加developer和manager标签
VisibilityClient.setAuths(connection,new String[]{"developer"},"tom"); VisibilityClient.setAuths(connection,new String[]{"manager"},"wzq");
-
执行完之后我们可以查看一下用户的标签
for (ByteString bytes : tom) { System.out.println("tom=>"+bytes.toStringUtf8()); } System.out.println("*********************"); List<ByteString> wzq = VisibilityClient.getAuths(connection, "wzq").getAuthList(); for (ByteString bytes : wzq) { System.out.println("wzq=>"+bytes.toStringUtf8()); }
-
执行结果为
tom=>developer ********************* wzq=>manager
-
到这已经添加成功了,然后我们用不同身份添加cell,目前test表中有列族cf,数据为空
Put tom = new Put(Bytes.toBytes("row1")); CellVisibility tomCellExpression = new CellVisibility("developer"); tom.setCellVisibility(tomCellExpression); tom.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("name"),Bytes.toBytes("tom")); Put wzq = new Put(Bytes.toBytes("row2")); CellVisibility wzqCellExpression = new CellVisibility("manager"); wzq.setCellVisibility(wzqCellExpression); wzq.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("name"),Bytes.toBytes("wzq")); Put sam = new Put(Bytes.toBytes("row3")); sam.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("name"),Bytes.toBytes("sam"));
- 现在表中数据为
-
然后我们用不同的用户进入hbase shell 查看test表
- tom身份进入
- wzq身份进入
- sam身份进入
- 从上面看出,自己只能查看没有标志其他label的cell和自己的cell,所以到这可见性标签操作就算成功了
-
对于删除label可以像删除一行数据那样就可以了
Table table = connection.getTable(TableName.valueOf("hbase:labels")); Delete delete = new Delete(Bytes.toBytes(2));
- hbase:labels表中的数据只对超级用户提供查看权限,我用两个不同的用户一个是root一个是wzq分别scan label表