反向扫描APIhtml
HBASE-4811(https://issues.apache.org/jira/browse/HBASE-4811)实现了一个API来扫描一个表或范围内的一个范围java
表反向,减小了对正向或反向扫描优化模式的需求。此功能在HBase 0.98和更高版本中可用。有关更多信息,请参阅Scan.setReversed()(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Scan.html#setReversed-boolean-)。node
数据库处理中的一个常见问题是快速找到最新版本的值。使用反向时间戳做为密钥的一部分的技术能够帮助解决这个问题的一个特例。在Tom White的书Hadoop:The Definitive Guide(O'Reilly)的HBase章节中也发现,该技术涉及在任何密钥的末尾附加(Long.MAX_VALUE - 时间戳)。 [键] [reverse_timestamp]。git
经过执行扫描[键]并获取第一条记录,能够找到表格中[键]的最新值。因为HBase密钥的排序顺序不一样,所以该密钥在[key]的任何较旧的行键以前排序,所以是第一个。程序员
(或很长一段时间),同时经过使用相同的扫描技术能够快速访问任何其余版本。github
行键的范围为ColumnFamilies。 所以,相同的rowkey能够存在于没有碰撞的表中存在的每一个ColumnFamily中。算法
行键没法更改。 他们能够在表格中“更改”的惟一方法是该行被删除而后从新插入。 这是HBase dist-list上的一个至关常见的问题,因此在第一次(和/或在插入大量数据以前)得到rowkeys是值得的。数据库
若是您预先拆分表格,了解您的rowkey如何在区域边界上分布是很是重要的。做为重要的一个例子,考虑使用可显示的十六进制字符做为键的前导位置(例如,“0000000000000000”到“ffffffffffffffff”)的示例。经过Bytes.split(这是在建立Admin.createTable(byte [] startKey,byte [] endKey,numRegions)中的区域时使用的分割策略来运行这些关键范围)将生成如下分割...apache
48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 // 0api
54 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 // 6
61 = 67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -68 // =
68 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -126 // D
75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 72 // K
82 18 18 18 18 18 18 18 18 18 18 18 18 18 18 14 // R
88 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -44 // X
95 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -102 // _
102 102 102 102 102 102 102 102 102 102 102 102 102 102 102 102 // f
(注意:前导字节做为注释列在右侧。)鉴于第一个分割是'0'而最后一个分割是'f',一切都很好,对吧?没那么快。
问题是全部的数据都会堆积在前两个区域和最后一个区域,从而产生一个“块状”(也多是“热”)区域问题。要理解为何,请参阅ASCII表(http://www.asciitable.com)。 '0'是字节48,'f'是字节102,但字节值(字节58到96)间存在巨大的差距,永远不会出如今这个密钥空间中,由于惟一的值是[0-9]和[af ]。所以,中间地区将永远不会被使用。为了使用该示例密钥空间进行预分割工做,须要分割的自定义定义(即,而且不依赖于内置分割方法)。
第1课:预分割表一般是最佳作法,但您须要预先拆分它们,以即可以在键空间中访问全部区域。虽然此示例演示了十六进制密钥空间的问题,但任何密钥空间都会出现一样的问题。了解你的数据。
第2课:虽然一般不可取,但只要全部建立的区域均可在键空间中访问,则使用十六进制键(更通常而言,可显示的数据)仍可用于预分割表。
总结这个例子,如下是如何为十六进制密钥预先建立合适的分割的例子:
public static boolean createTable(Admin admin, HTableDescriptor table, byte[][] splits) throws IOException {
try {
admin.createTable( table, splits ); return true;
} catch (TableExistsException e) {
logger.info("table " + table.getNameAsString() + " already exists");
}
}
public static byte[][] getHexSplits(String startKey, String endKey, int numRegions) { 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;
}
要存储的行版本的最大数量是经过HColumnDescriptor(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/HColumnDescriptor.html)为每一个列族配置的。 最大版本的缺省值为1.这是一个重要的参数,由于如数据模型部分所述,HBase不会覆盖行值,而是每次按时间(和限定符)存储不一样的值。 在重要的压缩过程当中删除多余的版本。 最大版本的数量可能须要根据应用需求增长或减小。
建议不要将最高版本数设置为极高的级别(例如,数百或更多),除非这些旧值对您很是重要,由于这会大大增长StoreFile大小。
像行版本的最大版本数同样,每一个列族经过配置保留的最小行数版本
HColumnDescriptor(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/HColumnDescriptor.html)。 min版本的默认值为0,这意味着该功能被禁用。行版本参数的最小数量与生存时间参数一块儿使用,而且能够与行版本数量参数组合,以容许诸如“保留最多T分钟数据值,最多N个版本,可是至少保留M个版本“(其中M是最小行数的值,M <N)。此参数只应在为列族启用生存时间时设置,而且必须小于行版本的数量。
HBase经过Put(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Put.html)和Result(https:// hbase)支持“bytes-in / bytes-out” .apache.org / apidocs / org / apache / hadoop / hbase / client / Result.html),所以能够将任何能够转换为字节数组的值存储为值。输入能够是字符串,数字,复杂对象,甚至图像,只要它们能够呈现为字节。
值的大小有实际的限制(例如,在HBase中存储10-50MB的对象可能太多了)。在邮件列表中搜索关于此主题的对话。 HBase中的全部行都符合数据模型,并包含版本控制。在进行设计时考虑到这一点,以及ColumnFamily的块大小。
值得特别说起的一种支持的数据类型是“计数器”(即可以执行数字的原子增量)。请参阅表中的增量(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Table.html#increment%28org.apache.hadoop.hbase.client.Increment%29)。
计数器上的同步在RegionServer上完成,而不是在客户端上完成。
若是您有多个表,请不要忘记考虑链接模式设计的可能性。
ColumnFamilies能够以秒为单位设置TTL长度,一旦达到到期时间,HBase将自动删除行。这适用于全部版本的行 - 即便是当前版本。在该行的HBase中编码的TTL时间以UTC指定。
存储仅包含过时行的文件将在小型压缩中删除。将hbase.store.delete.expired.storefile设置为false会禁用此功能。将最小版本数设置为0之外的值也会禁用此功能。
有关更多信息,请参阅HColumnDescriptor(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/HColumnDescriptor.html)。 HBase的最新版本也支持设置时间以每一个单元为基础生存。参见HBASE-10560
(https://issues.apache.org/jira/browse/HBASE-10560)以获取更多信息。单元TTL做为突变属性提交
使用突变#setTTL请求(追加,增长,放置等)。若是设置了TTL属性,则该操做将应用于服务器上更新的全部单元。单元TTL处理和ColumnFamily TTL之间有两个显着的区别:
单元TTL以毫秒为单位而不是秒。
单元TTL不能将一个单元的有效生命周期延长超过ColumnFamily级TTL设置。
默认状况下,删除标记延伸回到开始时间。所以,获取(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Get.html)或扫描(https://hbase.apache.org/apidocs/org/apache/hadoop /hbase/client/Scan.html)操做将不会看到已删除的单元格(行或列),即便获取或扫描操做指示删除标记放置以前的时间范围。
ColumnFamilies能够选择保留已删除的单元格。在这种状况下,只要这些操做指定的时间范围在影响单元格的任何删除的时间戳以前结束,仍然能够检索已删除的单元格。这容许甚至在存在删除的状况下进行时间点查询。
Example 17. Change the Value of KEEP_DELETED_CELLS Using HBase Shell
Example 18. Change the Value of KEEP_DELETED_CELLS Using the API
让咱们来讲明在表上设置KEEP_DELETED_CELLS属性的基本效果。 首先,without:
create 'test', {NAME=>'e', VERSIONS=>2147483647}
put 'test', 'r1', 'e:c1', 'value', 10
put 'test', 'r1', 'e:c1', 'value', 12
put 'test', 'r1', 'e:c1', 'value', 14
delete 'test', 'r1', 'e:c1', 11
hbase(main):017:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
r1 column=e:c1, timestamp=11, type=DeleteColumn
r1 column=e:c1, timestamp=10, value=value
1 row(s) in 0.0120 seconds
hbase(main):018:0> flush 'test'
0 row(s) in 0.0350 seconds
hbase(main):019:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
r1 column=e:c1, timestamp=11, type=DeleteColumn
1 row(s) in 0.0120 seconds
hbase(main):020:0> major_compact 'test'
0 row(s) in 0.0260 seconds
hbase(main):021:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
1 row(s) in 0.0120 seconds
JAVA
注意删除单元格是如何放开的。
如今让咱们仅使用在表上设置的KEEP_DELETED_CELLS运行相同的测试(您能够执行表或每列家族):
hbase(main):005:0> create 'test', {NAME=>'e', VERSIONS=>2147483647, KEEP_DELETED_CELLS => true}
0 row(s) in 0.2160 seconds
=> Hbase::Table - test
hbase(main):006:0> put 'test', 'r1', 'e:c1', 'value', 10
0 row(s) in 0.1070 seconds
hbase(main):007:0> put 'test', 'r1', 'e:c1', 'value', 12
0 row(s) in 0.0140 seconds
hbase(main):008:0> put 'test', 'r1', 'e:c1', 'value', 14
0 row(s) in 0.0160 seconds
hbase(main):009:0> delete 'test', 'r1', 'e:c1', 11
0 row(s) in 0.0290 seconds
hbase(main):010:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14,
value=value
r1 column=e:c1, timestamp=12,
value=value
r1 column=e:c1, timestamp=11,
type=DeleteColumn
r1 column=e:c1, timestamp=10,
value=value
1 row(s) in 0.0550 seconds
hbase(main):011:0> flush 'test'
0 row(s) in 0.2780 seconds
hbase(main):012:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14,
value=value
r1 column=e:c1, timestamp=12,
value=value
r1 column=e:c1, timestamp=11,
type=DeleteColumn
r1 column=e:c1, timestamp=10,
value=value
1 row(s) in 0.0620 seconds
hbase(main):013:0> major_compact 'test'
0 row(s) in 0.0530 seconds
hbase(main):014:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14,
value=value
r1 column=e:c1, timestamp=12,
value=value
r1 column=e:c1, timestamp=11,
type=DeleteColumn
r1 column=e:c1, timestamp=10,
value=value
1 row(s) in 0.0650 seconds
KEEP_DELETED_CELLS是为了不从HBase中删除单元格时,删除它们的惟一缘由是删除标记。 所以,若是使用KEEP_DELETED_CELLS启用已删除的单元格,若是您编写的版本多于配置的最大版本,或者您有TTL且单元格超过配置的超时等,则删除的单元格将被删除。
这部分也能够标题为“若是个人表rowkey看起来像这样,但我也想要像这样查询个人表。” dist-list上的一个常见示例是row-key格式为“user-timestamp”的格式,但对于特定时间范围内的用户活动有报告要求。所以,用户选择容易,由于它处于密钥的主导位置,但时间不是。
没有一个最好的方法来解决这个问题的答案,由于它取决于...
用户数量
数据大小和数据到达率
报告要求的灵活性(例如,彻底特定的日期选择与预先配置的范围)
指望的查询执行速度(例如,对于临时报告来讲90秒多是合理的,而对于其余方面可能太长)
解决方案也受集群规模和解决方案所需的处理能力的影响。常见的技巧在下面的小节中介绍。这是一个全面但并不是详尽的方法清单。
二级索引须要额外的集群空间和处理并不使人惊讶。这正是RDBMS中发生的状况,由于建立备用索引的操做须要更新空间和处理周期。
RDBMS产品在这方面更加先进,能够开箱即用地处理替代索引管理。可是,HBase在更大的数据量下能够更好地扩展,因此这是一项功能交换。
在实施任何这些方法时,请注意Apache HBase性能调整。另外,请参阅David Butler在这个dist-list线程HBase,mail#user - Stargate + hbase中的响应
(http://search-hadoop.com/m/nvbiBp2TDP/Stargate%252Bhbase&subj=Stargate+hbase)
根据具体状况,可能适合使用客户端请求过滤器。 在这种状况下,不会建立二级索引。 可是,请勿尝试在应用程序(如单线程客户端)上对此类大型表进行全面扫描。
二级索引能够在另外一个经过MapReduce做业按期更新的表中建立。 这项工做能够在一天内执行,但根据加载策略,它仍然可能与主数据表不一样步。
有关更多信息,请参阅mapreduce.example.readwrite。
另外一种策略是在将数据发布到集群时构建二级索引(例如写入数据表,写入索引表)。 若是这是在数据表已经存在以后采起的方法,那么对于具备MapReduce做业的二级索引将须要引导(请参阅secondary.indexes.periodic)。
在时间范围很是普遍(例如,长达一年的报告)以及数据量很大的状况下,汇总表是经常使用的方法。 这些将经过MapReduce做业生成到另外一个表中。
有关更多信息,请参阅mapreduce.example.summary。
协处理器就像RDBMS触发器同样。这些增长了0.92。有关更多信息,请参阅协处理器
HBase目前支持传统(SQL)数据库术语中的“约束”。 Constraints的建议用法是强制执行表中属性的业务规则(例如,确保值在1-10范围内)。也可使用约束来强制引用完整性,可是强烈建议不要使用约束,由于它会显着下降启用完整性检查的表的写入吞吐量。从版本0.94开始,能够在Constraint(https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/constraint/Constraint.html)上找到有关使用约束的大量文档。
如下将描述HBase的一些典型数据提取用例,以及如何处理rowkey设计和构造。注意:这只是潜在方法的一个例证,而不是详尽的清单。了解您的数据,并了解您的处理要求。
强烈建议您在阅读这些案例研究以前,首先阅读HBase和Schema Design的其他部分。描述了如下案例研究:
记录数据/时间序列数据
日志数据/时间序列上的类固醇
客户订单
高/宽/中图式设计
列表数据
假设正在收集如下数据元素。
主机名
时间戳
记录事件
值/消息
咱们能够将它们存储在名为LOG_DATA的HBase表中,但rowkey会是什么? 从这些属性中,rowkey将是主机名,时间戳和日志事件的一些组合 - 但具体是什么?
44.1.1。行密钥主导位置中的时间戳
rowkey [timestamp] [hostname] [log-event]受单调递增行键/时间序列数据中描述的单调递增rowkey问题的影响。
经过在时间戳上执行mod操做,在dist-lists中常常提到关于“分组”时间戳的另外一种模式。若是时间扫描很重要,这多是一个有用的方法。必须注意桶的数量,由于这将须要相同数量的扫描来返回结果。
long bucket = timestamp%numBuckets;
JAVA
构建:
[桶] [时间戳] [主机名] [登陆事件]
JAVA
如上所述,要选择特定时间范围的数据,须要为每一个存储桶执行扫描。例如,100个存储桶将在密钥空间中提供普遍的分布,但它须要100次扫描才能得到单个时间戳的数据,所以存在权衡。
44.1.2。主持人在Rowkey主导地位
若是存在大量的主机来经过密钥空间来传播写入和读取,则rowkey [主机名] [日志事件] [时间戳]是候选者。若是按主机名扫描是优先事项,则此方法很是有用。
44.1.3。时间戳或反向时间戳?
若是最重要的访问路径是拉取最近的事件,则将时间戳存储为反向时间戳(例如timestamp = Long.MAX_VALUE - timestamp)将建立可以对[hostname] [log-事件]来获取最近捕获的事件。
这两种方法都不是错的,只是取决于最适合这种状况的东西。
反向扫描API
表反向,减小了对正向或反向扫描优化模式的需求。此功能在HBase 0.98和更高版本中可用。有关更多信息,请参阅Scan.setReversed()(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Scan.html#setReversed-boolean-)。
44.1.4。可变长度或固定长度的行键?
记住在HBase的每一列上加盖行密码是很是重要的。若是主机名是a而且事件类型是e1,那么生成的rowkey将会很小。可是,若是所摄入的主机名是myserver1.mycompany.com,而且事件类型是com.package1.subpackage2.subsubpackage3.ImportantService,该怎么办?
在rowkey中使用一些替换多是有意义的。至少有两种方法:散列和数字。在Rowkey Lead Position示例中的主机名中,它可能以下所示:
带有哈希的复合Rowkey:
[主机名的MD5散列] = 16个字节
[事件类型的MD5散列] = 16个字节
[时间戳] = 8个字节
带数字替换的复合行列码:
对于这种方法,除了LOG_DATA以外,还须要另外一个查找表,称为LOG_TYPES。 LOG_TYPES的rowkey将是:
[类型](例如,指示主机名与事件类型的字节)
原始主机名或事件类型的[bytes]可变长度字节。
此rowkey的列多是一个具备指定编号的长整数,可经过使用HBase计数器得到
(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Table.html#incrementColumnValue-byte:A-byte:A-byte:A-long-)
因此获得的复合rowkey将是:
[代替主机名长] = 8个字节
[长时间取代事件类型] = 8个字节
[时间戳] = 8个字节
在Hash或Numeric替换方法中,主机名和事件类型的原始值能够存储为列。
这其实是OpenTSDB的方法。 OpenTSDB作的是重写数据并在特定的时间段内将行打包成列。 有关详细说明,请参阅:http://opentsdb.net/schema.html,以及HBaseCon2012从OpenTSDB得到的经验教训(https://www.slideshare.net/cloudera/4-opentsdb-hbasecon)。
可是,这是通常概念的工做原理:例如,以这种方式摄入数据...
[主机名] [日志事件] [时间戳1] [主机名] [日志事件] [时间戳2] [主机名] [日志事件] [时间戳3]
每一个细节事件都有独立的rowkeys,可是会被重写成这样...
[主机名] [登陆事件] [TIMERANGE]
而且每一个上述事件被转换成相对于开始时间范围(例如每5分钟)以时间偏移存储的列。 这显然是一种很是先进的处理技术,但HBase使这成为可能。
假设HBase用于存储客户和订单信息。 有两种核心记录类型被摄取:客户记录类型和订单记录类型。
顾客号码
顾客姓名
地址(例如,城市,州,邮编)
电话号码等
订单记录类型将包含以下内容:
顾客号码
订单号
销售日期
一系列用于装运位置和订单项的嵌套对象(有关详细信息,请参阅订单对象设计)
假设客户编号和销售订单的组合惟一地标识订单,这两个属性将组成rowkey,特别是组合键,例如:
[customer number][order number]
为一个ORDER表。 可是,还有更多的设计决策须要:原始值是rowkeys的最佳选择?
Log Data用例中的相同设计问题在这里面对咱们。 客户编号的密钥空间是什么,以及格式是什么(例如,数字?字母数字?)因为在HBase中使用固定长度的密钥以及能够在密钥空间中支持合理分布的密钥是有利的,所以相似 选项出现:
带有哈希的复合Rowkey:
[客户号码的MD5] = 16字节
[订单号的MD5] = 16字节组合数字/哈希组合Rowkey:
[代替客户号码] = 8个字节
[订单号的MD5] = 16字节
单表? 多表?
传统的设计方法会为CUSTOMER和SALES分开表格。 另外一种选择是将多个记录类型打包到一个表中(例如,CUSTOMER ++)。
客户记录类型Rowkey:
[客户ID]
[type] =表示客户记录类型的类型'1'订单记录类型Rowkey:
[客户ID]
[type] =指示订单记录类型为'2'的类型
[订购]
这种特定的CUSTOMER ++方法的优势是经过客户ID组织许多不一样的记录类型(例如,单次扫描可让你获得关于该客户的全部信息)。 缺点是扫描特定记录类型并不容易。
订单对象设计
如今咱们须要解决如何建模Order对象。假设类结构以下:
订购
(订单能够有多个ShippingLocations
(一个ShippingLocation能够有多个LineItems,在存储这些数据时有多个选项,彻底标准化
经过这种方法,ORDER,SHIPPING_LOCATION和LINE_ITEM将会有单独的表格。
ORDER表的rowkey如上所述:schema.casestudies.custorder SHIPPING_LOCATION的复合rowkey以下所示:
[订单rowkey]
[出货地点编号](例如,第一地点,第二地点等)LINE_ITEM表的复合行键将是这样的:
[订单rowkey]
[出货地点编号](例如,第一地点,第二地点等)
[订单项编号](例如,第一个线性项,第二个等)
这样的标准化模型极可能是RDBMS的方法,但这不是HBase惟一的选择。这种作法的缺点是要检索任何订单的信息,您须要:
获取订单的订单表
在SHIPPING_LOCATION表上扫描该订单以获取ShippingLocation实例
在每一个ShippingLocation的LINE_ITEM上扫描
这是一个RDBMS不管如何都会在封面下作的事情,但因为HBase中没有链接,因此您只是更加意识到这一点。
单一表与记录类型
采用这种方法,将会存在一个包含单个表的ORDER
Order rowkey如上所述:schema.casestudies.custorder
[订单rowkey]
[订单记录类型]
ShippingLocation复合rowkey将以下所示:
[订单rowkey]
[SHIPPING记录类型]
[出货地点编号](例如,第一地点,第二地点等)LineItem复合行键将是这样的:
[订单rowkey]
[LINE记录类型]
[出货地点编号](例如,第一地点,第二地点等)
[订单项编号](例如,第一个线性项,第二个等)
反规范化
具备记录类型的单一表格的一种变体是对一些对象层次结构进行非规范化和扁平化,好比将ShippingLocation属性折叠到每一个LineItem实例上。
LineItem复合rowkey将以下所示:
[订单rowkey]
[LINE记录类型]
[订单项编号](例如,第一个lineitem,第二个等等,必须注意整个订单中都有惟一),LineItem列将以下所示:
项目编号
数量
价钱
shipToLine1(从ShippingLocation非正规化)
shipToLine2(从ShippingLocation非正规化)
shipToCity(从ShippingLocation非正规化)
shipToState(从ShippingLocation非正规化)
shipToZip(从ShippingLocation非正规化)
这种方法的优势包括不太复杂的对象层次结构,但其中一个缺点是,若是这些信息发生变化,更新会变得更加复杂。
对象BLOB
经过这种方法,整个Order对象图都以某种方式做为BLOB进行处理。例如,上面描述了ORDER表的rowkey:schema.casestudies.custorder,而一个名为“order”的列将包含一个能够反序列化的对象,该对象包含一个容器Order,ShippingLocations和LineItems。
这里有不少选项:JSON,XML,Java序列化,Avro,Hadoop Writable等等。全部这些都是相同方法的变体:将对象图编码为字节数组。应该注意这种方法,以确保向后兼容,以防对象模型发生变化,使旧的持久结构仍能从HBase中读出。
优势是可以以最少的I / O管理复杂的对象图(例如,在本例中为单个HBase Get Order),但缺点包括前面提到的关于序列化的向后兼容性,序列化的语言依赖性(例如Java序列化只适用于Java客户端),事实上你必须反序列化整个对象才能得到BLOB中的任何信息,以及像Hive这样的框架难以使用像这样的自定义对象。
本节将介绍出如今远程列表中的其余模式设计问题,特别是高和宽表。这些是通常准则而不是法律 - 每一个应用程序都必须考虑本身的需求。
行与版本
一个常见的问题是应该更喜欢行仍是HBase的内置版本。上下文一般是保留行的“不少”版本的地方(例如,它明显高于1个最大版本的HBase默认值)。 rows-approach须要在rowkey的某些部分存储一个时间戳,以便在每次连续更新时不会覆盖它们。
首选项:行(通常来讲)。
行与列
另外一个常见问题是,应该更喜欢行仍是列。上下文一般在宽表格的极端状况下,例如具备1行100万个属性,或100万行,每列1列。
首选项:行(通常来讲)。须要说明的是,本指南在上下文中是很是宽泛的状况,而不是标准的用例,其中须要存储几十或者一百列。可是这两个选项之间也有一条中间路径,那就是“行列为列”。
行做为列
行与列之间的中间路径将打包数据,对于某些行,这些数据将成为单独的行。在这种状况下,OpenTSDB就是最好的例子,其中一行表示一个定义的时间范围,而后将离散事件视为列。这种方法一般更加复杂,而且可能须要重写数据的额外复杂性,但具备I / O高效的优势。有关此方法的概述,请参阅schema.casestudies.log-steroids。
在Apache HBase中。
题 *
咱们正在研究如何在HBase中存储大量(每用户)列表数据,而且咱们试图弄清楚哪一种访问模式最有意义。一种选择是将大部分数据存储在一个密钥中,因此咱们能够有这样的内容:
<FixedWidthUserName> <FixedWidthValueId1>:“”(无值)
<FixedWidthUserName> <FixedWidthValueId2>:“”(无值)
<FixedWidthUserName> <FixedWidthValueId3>:“”(无值)
JAVA
咱们的另外一个选择是彻底使用:
<FixedWidthUserName> <FixedWidthPageNum0>:<FixedWidthLength> <FixedIdNextPageNum> <ValueId1> <ValueId2> <ValueId3> ...
<FixedWidthUserName> <FixedWidthPageNum1>:<FixedWidthLength> <FixedIdNextPageNum> <ValueId1> <ValueId2> <ValueId3> ...
XML
每行将包含多个值。因此在一种状况下,读取前三十个值将是:
扫描{STARTROW =>'FixedWidthUsername'LIMIT => 30} JAVA
而在第二种状况下会是这样
获得'FixedWidthUserName \ x00 \ x00 \ x00 \ x00'JAVA
通常的使用模式是仅读取这些列表的前30个值,而且不常常访问更深刻地阅读列表。一些用户在这些列表中的总值将达到30%,而一些用户将拥有数百万(即幂律分布)
单值格式彷佛会占用HBase更多的空间,但会提供一些改进的检索/分页灵活性。是否有任何显着的性能优点可以经过获取与分页扫描分页?
我最初的理解是,若是咱们的分页大小未知(而且缓存设置恰当),那么执行扫描应该会更快,但若是咱们始终须要相同的页面大小,则扫描速度应该更快。我听到不一样的人告诉了我关于表演的相反事情。我假设页面大小会相对一致,因此对于大多数用例,咱们能够保证咱们只须要固定页面长度的状况下的一页数据。我还会假设咱们将不常常更新,但可能会插入这些列表的中间(这意味着咱们须要更新全部后续行)。
感谢您的帮助/建议/后续问题。
答案*
若是我正确理解你,你最终试图以“user,valueid,value”的形式存储三元组,对吗?例如,相似于:
“user123,firstname,Paul”,“user234,lastname,Smith”
JAVA
(但用户名是固定宽度,而valueids是固定宽度)。
并且,您的访问模式符合如下要求:“对于用户X,列出接下来的30个值,以valueid Y开头”。是对的吗?这些值应该返回按valueid排序?
tl; dr版本是,你可能应该为每一个用户+值添加一行,除非你肯定须要,不然不要自行构建复杂的行内分页方案。
您的两个选项反映了人们在设计HBase模式时常见的问题:我应该选择“高”仍是“宽”?您的第一个模式是“高”:每行表明一个用户的一个值,所以每一个用户的表中有不少行;行键是user + valueid,而且会有(可能)单个列限定符,意思是“值”。这是
正确)。你能够在任何用户+ valueid开始扫描,阅读下一个30,并完成。你放弃的是可以在一个用户的全部行周围提供事务保证,但它听起来并不像你须要的那样。一般建议这样作(参见https://hbase.apache.org/book.html#schema.smackdown)。
第二个选项是“宽”:使用不一样的限定符(其中限定符是valueid)将一堆值存储在一行中。简单的作法是将一个用户的全部值存储在一行中。我猜你跳到了“分页”版本,由于你认为在单行中存储数百万列会对性能形成影响,这多是也可能不是真的;只要您不想在单个请求中作太多事情,或者执行诸如扫描并返回行中的全部单元格之类的内容,它不该该基本上变得更糟。客户端具备容许您获取特定的列的片断的方法。
请注意,这两种状况都不会从根本上占用更多的磁盘空间;您只是将部分识别信息“移动”到左侧(在行键中,在选项一中)或向右(在选项2中的列限定符中)。在封面下,每一个键/值仍然存储整个行键和列名称。 (若是这有点使人困惑,请花一个小时观看Lars George关于理解HBase架构设计的优秀视频:http://www.youtube.com/watch?v = _HLoH_PgrLk)。
正如你注意到的那样,手动分页版本有不少复杂性,好比必须跟踪每一个页面中有多少内容,若是插入新值,则从新洗牌等。这看起来要复杂得多。在分机上它可能有一些轻微的速度优点(或缺点!)
将hbase.regionserver.handler.count(在hbase-site.xml中)设置为cores x spindles以实现并发。
可选地,将呼叫队列分红单独的读取和写入队列以用于区别服务。参数
hbase.ipc.server.callqueue.handler.factor指定呼叫队列的数量:
0表示单个共享队列
1表示每一个处理程序有一个队列。
0到1之间的值与处理程序的数量成比例地分配队列数量。例如,.5的值在每一个处理程序之间共享一个队列。
使用hbase.ipc.server.callqueue.read.ratio(0.98中的hbase.ipc.server.callqueue.read.share)将调用队列分为读写队列:
0.5意味着将有相同数量的读写队列
读取比写入少0.5
> 0.5以上的写入比读取
设置hbase.ipc.server.callqueue.scan.ratio(HBase 1.0+)将读取调用队列分为小读和长读队列:
0.5意味着将有相同数量的短读取和长读取队列
更短的读数<0.5
> 0.5为更长的阅读
禁用Nagle的算法。 延迟的ACK能够将RPC往返时间加起来大约200ms。 设置如下参数:
在Hadoop的core-site.xml中:
ipc.server.tcpnodelay = true
ipc.client.tcpnodelay = true
在HBase的hbase-site.xml中:
hbase.ipc.client.tcpnodelay = true
hbase.ipc.server.tcpnodelay = true
尽量快地检测区域服务器故障。 设置如下参数:
在hbase-site.xml中,将zookeeper.session.timeout设置为30秒或更短期来绑定故障检测(20-30秒是一个好的开始)。
检测并避免不健康或失败的HDFS数据节点:在hdfs-site.xml和hbase-site.xml中,设置如下参数:
dfs.namenode.avoid.read.stale.datanode = true
dfs.namenode.avoid.write.stale.datanode = true
跳过本地块的网络。 在hbase-site.xml中,设置如下参数:
dfs.client.read.shortcircuit = true
dfs.client.read.shortcircuit.buffer.size = 131072(重要避免OOME)
确保数据的局部性。 在hbase-site.xml中,设置hbase.hstore.min.locality.to.skip.major.compact = 0.7(意思是0.7 <= n <= 1)
确保DataNode有足够的处理程序进行块传输。 在hdfs-site.xml中,设置如下参数:
dfs.datanode.max.xcievers> = 8192
调整JVM GC以获取低收集延迟
使用CMS收集器:-XX:+ UseConcMarkSweepGC
保持伊甸园空间尽量小,以减小平均收集时间。例:
-XX:CMSInitiatingOccupancyFraction = 70
优化低收集延迟而不是吞吐量:-Xmn512m
并行收集伊甸园:-XX:+ UseParNewGC
避免在压力下收集:-XX:+ UseCMSInitiatingOccupancyOnly
限制每一个请求扫描器的结果大小,因此一切都适合幸存者空间,但没有任职期限。在hbase-site.xml中,将hbase.client.scanner.max.result.size设置为eden空间的1/8(使用-Xmn512m这是〜51MB)
设置max.result.size x handler.count小于生存者空间
OS级调整
关闭透明大页面(THP):
echo never> / sys / kernel / mm / transparent_hugepage / enabled echo never> / sys / kernel / mm / transparent_hugepage / defrag
设置vm.swappiness = 0
将vm.min_free_kbytes设置为至少1GB(较大内存系统上为8GB)
使用vm.zone_reclaim_mode = 0禁用NUMA区域回收
在客户端的hbase-site.xml中,设置如下参数:
设置hbase.client.pause = 1000
设置hbase.client.retries.number = 3
若是你想骑跨分割和区域移动,大幅增长hbase.client.retries.number(> = 20)
设置RecoverableZookeeper重试计数:zookeeper.recovery.retry = 1(不重试)
在服务器端的hbase-site.xml中,设置Zookeeper会话超时以检测服务器故障:
zookeeper.session.timeout⇐30秒(20-30是比较好的)
HBase时间线一致性(HBASE-10070)启用只读副本后,区域(副本)的只读副本将分布在群集中。 One RegionServer为默认或主副本提供服务,这是惟一能够服务写入的副本。 其余Region Server服务于辅助副本,请遵循主要RegionServer,并仅查看提交的更新。 辅助副本是只读的,但能够在主服务器故障时当即提供读取操做,从而将读取可用性从几秒钟减小到几毫秒。 Phoenix支持时间线一致性4.4.0提示:
部署HBase 1.0.0或更高版本。
在服务器端启用时间线一致副本。
使用如下方法之一设置时间线一致性:
使用ALTER SESSION SET CONSISTENCY ='TIMELINE'
在JDBC链接字符串中将链接属性Consistency设置为时间轴
TableInputFormat(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/mapreduce/TableInputFormat.html)用于源MapReduce做业中的HBase表格时,其拆分器将为每一个映射任务建立一个映射任务表的区域。 所以,若是表格中有100个区域,则不管在“扫描”中选择多少个列族,该做业都会有100个地图任务。
对于那些有兴趣实现自定义拆分器的人,请参阅TableInputFormatBase中的getSplits方法(https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/mapreduce/TableInputFormatBase.html)。 这是map-task分配的逻辑所在。
}
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。关于此部分暂时不填入。后续将继续补充
Hadoop认证配置
要使用强身份验证运行HBase RPC,您必须将hbase.security.authentication设置为kerberos。 在这种状况下,您还必须将hadoop.security.authentication设置为core-site.xml中的kerberos。 不然,您将对HBase使用强身份验证,但不会对底层HDFS使用强身份验证,不然会取消任何收益。
Kerberos KDC
您须要有一个可用的Kerberos KDC。
首先,参考security.prerequisites并确保您的基础HDFS配置是安全的。
将如下内容添加到集群中每台服务器计算机上的hbase-site.xml文件中:
<property>
<name>hbase.security.authentication</name>
<value>kerberos</value>
</property>
<property>
<name>hbase.security.authorization</name>
<value>true</value>
</property>
<property>
<name>hbase.coprocessor.region.classes</name>
<value>org.apache.hadoop.hbase.security.token.TokenProvider</value>
</property>
部署这些配置更改时,须要彻底关闭并从新启动HBase服务。
首先,请参阅先决条件并确保您的基础HDFS配置是安全的。
将如下内容添加到每一个客户端上的hbase-site.xml文件中:
<property>
<name>hbase.security.authentication</name>
<value>kerberos</value>
</property>
客户端环境必须经过kinit命令从KDC或keytab登陆到Kerberos,而后才能与HBase群集通讯。
请注意,若是客户端和服务器端站点文件中的hbase.security.authentication不匹配,客户端将没法与群集进行通讯。
一旦将HBase配置为安全RPC,就能够选择配置加密通讯。 为此,请将如下内容添加到每一个客户端上的hbase-site.xml文件中:
<property>
<name>hbase.rpc.protection</name>
<value>privacy</value>
</property>
此配置属性也能够在每一个链接的基础上进行设置。 在提供给表的配置中进行设置:
Connection connection = ConnectionFactory.createConnection(conf);
conf.set("hbase.rpc.protection", "privacy");
try (Connection connection = ConnectionFactory.createConnection(conf); Table table = connection.getTable(TableName.valueOf(tablename))) {
.... do your stuff
}
对于加密通讯,预计会有大约10%的性能损失。
将如下内容添加到每一个Thrift网关的hbase-site.xml文件中
<property>
<name>hbase.thrift.keytab.file</name>
<value>/etc/hbase/conf/hbase.keytab</value>
</property>
<property>
<name>hbase.thrift.kerberos.principal</name>
<value>$USER/_HOST@HADOOP.LOCALDOMAIN</value>
<!-- TODO: This may need to be HTTP/_HOST@<REALM> and _HOST may not work. You may have to put the concrete full hostname.
-->
</property>
<!-- Add these if you need to configure a different DNS interface from the default -->
<property>
<name>hbase.thrift.dns.interface</name>
<value>default</value>
</property>
<property>
<name>hbase.thrift.dns.nameserver</name>
<value>default</value>
</property>
分别替换$ USER和$ KEYTAB的相应凭证和密钥表。
为了使用Thrift API主体与HBase进行交互,还须要将hbase.thrift.kerberos.principal添加到acl表。 例如,为了给予Thrift API主体thrift_server管理访问权限,像这样的命令就足够了。
grant 'thrift_server', 'RWCA'
有关ACL的更多信息,请参阅访问控制标签(ACL)部分
Thrift网关将使用提供的凭证向HBase进行身份验证。 Thrift网关自己不会执行认证。 全部经过Thrift网关访问的客户端都将使用Thrift网关的凭证并拥有其权限。
用于安全操做的客户端配置 - Thrift Gateway描述了如何使用固定用户向HBase验证Thrift客户端。做为替代,您能够将Thrift网关配置为表明客户端向HBase进行身份验证,并使用代理用户访问HBase。这在HBASE-11349(https://issues.apache.org/jira/browse/HBASE-11349)中为Thrift 1和HBASE-11474(https://issues.apache.org/jira/browse/HBASE)实施-11474)为Thrift 2。
节俭框架运输的局限性
若是您使用框架式运输,您还不能利用此功能,由于此时SASL不适用于Thrift框架式运输。
要启用它,请执行如下操做。
1.按照安全操做客户端配置 - Thrift Gateway中所述的步骤,确保Thrift以安全模式运行。
2.确保HBase配置为容许代理用户,如REST网关模拟配置中所述。
3.在运行Thrift网关的每一个群集节点的hbase-site.xml中,将属性hbase.thrift.security.qop设置为如下三个值之一:
integrity - authentication and integrity checking
authentication - authentication checking only
4.从新启动Thrift网关进程以使更改生效。若是节点正在运行Thrift,则jps命令的输出将列出ThriftServer进程。要中止节点上的Thrift,请运行命令bin / hbase-daemon.sh stop thrift。要在节点上启动Thrift,请运行命令bin / hbase-daemon.sh start thrift。
表明客户端配置Thrift网关进行身份验证描述如何配置Thrift网关以表明客户端对HBase进行身份验证,以及如何使用代理用户访问HBase。 这种方法的局限性在于客户端使用特定的凭证集进行初始化后,在会话期间它不能更改这些凭证。 doAs功能提供了使用相同客户端模拟多个主体的灵活方式。 此功能在H BASE-12640(https://issues.apache.org/jira/browse/HBASE-12640)中为Thrift 1实施,但目前不适用于Thrift 2。
要启用doAs功能,请将如下内容添加到每一个Thrift网关的hbase-site.xml文件中:
<property>
<name>hbase.regionserver.thrift.http</name>
<value>true</value>
</property>
<property>
<name>hbase.thrift.support.proxyuser</name>
<value>true/value>
</property>
要在使用doAs模拟时容许代理用户,请将如下内容添加到每一个HBase节点的hbase-site.xml文件中:
<property>
<name>hadoop.security.authorization</name>
<value>true</value>
</property>
<property>
<name>hadoop.proxyuser.$USER.groups</name>
<value>$GROUPS</value>
</property>
<property>
<name>hadoop.proxyuser.$USER.hosts</name>
<value>$GROUPS</value>
</property>
看看演示客户端
(https://github.com/apache/hbase/blob/master/hbase-examples/src/main/java/org/apache/hadoop/hbase/thrift/HttpDoAsClient.java)以得到关于如何使用的整体思路 这个功能在你的客户端。
将如下内容添加到每一个REST网关的hbase-site.xml文件中:
<property>
<name>hbase.rest.keytab.file</name>
<value>$KEYTAB</value>
</property>
<property>
<name>hbase.rest.kerberos.principal</name>
<value>$USER/_HOST@HADOOP.LOCALDOMAIN</value>
</property>
分别替换$ USER和$ KEYTAB的相应凭证和密钥表。 REST网关将使用提供的凭证对HBase进行身份验证。
为了使用REST API主体与HBase进行交互,还须要将hbase.rest.kerberos.principal添加到acl表。 例如,要赋予REST API主体rest_server管理访问权限,像这样的命令就足够了:
有关ACL的更多信息,请参阅访问控制标签(ACL)部分
HBase REST网关支持客户端访问网关的SPNEGO HTTP身份验证(https://hadoop.apache.org/docs/stable/hadoop-auth/index.html)。 要为客户端访问启用REST网关Kerberos身份验证,请将如下内容添加到每一个REST网关的hbase-site.xml文件中。
<property>
<name>hbase.rest.support.proxyuser</name>
<value>true</value>
</property>
<property>
<name>hbase.rest.authentication.type</name>
<value>kerberos</value>
</property>
<property>
<name>hbase.rest.authentication.kerberos.principal</name>
<value>HTTP/_HOST@HADOOP.LOCALDOMAIN</value>
</property>
<property>
<name>hbase.rest.authentication.kerberos.keytab</name>
<value>$KEYTAB</value>
</property>
<!-- Add these if you need to configure a different DNS interface from the default -->
<property>
<name>hbase.rest.dns.interface</name>
<value>default</value>
</property>
<property>
<name>hbase.rest.dns.nameserver</name>
<value>default</value>
</property>
用$ KEYTAB替代HTTP的keytab。
HBase REST网关支持不一样的'hbase.rest.authentication.type':简单,kerberos。 您也能够经过实现Hadoop AuthenticationHandler来实现自定义身份验证,而后将完整的类名称指定为'hbase.rest.authentication.type'值。 有关更多信息,请参阅SPNEGO HTTP身份验证(https://hadoop.apache.org/docs/stable/hadoop-auth/index.html)。
默认状况下,REST网关不支持模拟。 它以用户的身份访问HBase,按照上一节中的配置。 对于HBase服务器,全部请求都来自REST网关用户。 实际用户不详。 您能够打开模拟支持。 经过模拟,REST网关用户是代理用户。 HBase服务器知道每一个请求的实际/真实用户。 所以它能够应用适当的受权。
要打开REST网关模拟,咱们须要配置HBase服务器(主服务器和区域服务器)以容许代理用户; 配置REST网关以启用模拟。
要容许代理用户,请将如下内容添加到每一个HBase服务器的hbase-site.xml文件中:
<property>
<name>hadoop.security.authorization</name>
<value>true</value>
</property>
<property>
<name>hadoop.proxyuser.$USER.groups</name>
<value>$GROUPS</value>
</property>
<property>
<name>hadoop.proxyuser.$USER.hosts</name>
<value>$GROUPS</value>
</property>
将REST网关代理用户替换为$ USER,并将容许的组列表替换为$ GROUPS。
要启用REST网关模拟,请将如下内容添加到每一个REST网关的hbase-site.xml文件中。
<name>hbase.rest.authentication.type</name>
<value>kerberos</value>
</property>
<property>
<name>hbase.rest.authentication.kerberos.principal</name>
<value>HTTP/_HOST@HADOOP.LOCALDOMAIN</value>
</property>
<property>
<name>hbase.rest.authentication.kerberos.keytab</name>
<value>$KEYTAB</value>
</property>
用$ KEYTAB替代HTTP的keytab。
较新版本的Apache HBase(> = 0.92)支持客户端的可选SASL身份验证。 另请参阅Matteo Bertozzi有关了解Apache HBase中的用户身份验证和受权的文章(https://blog.cloudera.com/blog/2012/09/understanding-user-authentication-and-authorization-in-apache-hbase/)。
本节介绍如何设置Apache HBase和客户端,以便用户访问HBase资源。
如下部分介绍如何设置简单的用户访问。 简单的用户访问不是一种运行HBase的安全方法。 此方法用于防止用户犯错。 它可用于在开发系统上模拟访问控制,而无需设置Kerberos。
此方法不用于防止恶意或黑客入侵。 为了使HBase可以抵御这些类型的攻击,您必须配置HBase进行安全操做。 请参阅安全客户端访问Apache HBase部分并完成此处描述的全部步骤。
将如下内容添加到集群中每台服务器计算机上的hbase-site.xml文件中:
<property>
<name>hbase.security.authentication</name>
<value>simple</value>
</property>
<property>
<name>hbase.security.authorization</name>
<value>true</value>
</property>
<property>
<name>hbase.coprocessor.master.classes</name>
<value>org.apache.hadoop.hbase.security.access.AccessController</value>
</property>
<property>
<name>hbase.coprocessor.region.classes</name>
<value>org.apache.hadoop.hbase.security.access.AccessController</value>
</property>
<property>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.security.access.AccessController</value>
</property>
0.94版本如下不适用上述xml配置。
ZooKeeper具备可插入的身份验证机制,可使用不一样的方法访问客户端。 ZooKeeper甚至容许同时验证和未验证的客户端。经过为每一个znode提供访问控制列表(ACL)来限制对znodes的访问。 ACL包含两个组件,即身份验证方法和主体。 ACL不是分层强制执行的。有关详细信息,请参阅ZooKeeper程序员指南(https://zookeeper.apache.org/doc/r3.3.6/zookeeperProgrammers.html#sc_ZooKeeperPluggableAuthentication)。
HBase守护进程经过SASL和Kerberos向ZooKeeper进行身份验证(请参阅使用ZooKeeper进行SASL身份验证)。 HBase设置znode ACL,以便只有HBase用户和配置的hbase超级用户(hbase.superuser)才能访问和修改数据。在ZooKeeper用于服务发现或与客户端共享状态的状况下,由HBase建立的znodes也将容许任何人(无论身份验证)读取这些znode(clusterId,主地址,元位置等),但只有HBase用户能够修改它们。
全部管理的数据都保存在文件系统(hbase.rootdir)的根目录下。访问文件系统中的数据和WAL文件应受到限制,以便用户不能绕过HBase层,并从文件系统中查看底层数据文件。 HBase假定使用的文件系统(HDFS或其余)分层次地强制执行权限。若是没有提供足够的文件系统保护(受权和身份验证),HBase级受权控制(ACL,可见性标签等)就没有意义,由于用户能够随时访问文件系统中的数据。
HBase对其根目录强制执行posix-like权限700(rwx ------)。这意味着只有HBase用户能够读写FS中的文件。经过在hbase-site.xml中配置hbase.rootdir.perms能够更改默认设置。须要从新启动活动主服务器,以便更改使用的权限。对于1.2.0以前的版本,您能够检查是否提交了HBASE-13780,若是没有,您能够根据须要手动设置根目录的权限。使用HDFS,该命令将是:
sudo -u hdfs hadoop fs -chmod 700 /hbase
若是您使用不一样的hbase.rootdir,则应该更改/ hbase。
在安全模式下,应配置SecureBulkLoadEndpoint并将其用于将用户做业从MR做业建立到HBase守护程序和HBase用户的正确处理。分布式文件系统中的临时目录用于批量加载
(hbase.bulkload.staging.dir,默认为/ tmp / hbase-staging)应具备(模式711或rwx-x-x),以便用户能够访问在该父目录下建立的暂存目录,但不能执行任何其余操做操做。有关如何配置SecureBulkLoadEndPoint,请参阅安全批量加载。
在HBase客户端和服务器进程和网关之间配置安全身份验证后,您须要考虑数据自己的安全性。 HBase提供了几种保护数据的策略:
基于角色的访问控制(RBAC)控制哪些用户或组可使用熟悉的角色范例来读取和写入给定的HBase资源或执行协处理器端点。
可见性标签,容许您标记单元格并控制对标记单元格的访问,以进一步限制谁能够读取或写入数据的某些子集。可见性标签存储为标签。有关更多信息,请参阅hbase.tags。
在HFile和WAL中对底层文件系统的静态数据进行透明加密。这能够保护您的数据免受能够访问底层文件系统的攻击者的影响,而无需更改客户端的实现。它还能够防止不正确放置的磁盘泄漏数据,这对法律和法规听从性可能很是重要。
下面讨论这些功能中的每一个功能的服务器端配置,管理和实现细节,以及任何性能折衷。最后给出一个示例安全配置,以显示全部这些功能一块儿使用,由于它们可能在真实世界的场景中。
HBase安全的各个方面都在积极发展和快速发展。您为了保护您的数据而采起的任何策略都应该进行完全的测试。另外,其中一些功能还处于实验阶段。要利用其中的许多功能,您必须运行HBase 0.98+并使用HFile v3文件格式。
保护敏感文件
本节中的几个过程要求您在群集节点之间复制文件。复制包含敏感字符串的密钥,配置文件或其余文件时,请使用安全方法(如ssh)来避免泄露敏感数据。
过程:基本的服务器端配置
<property>
<name>hfile.format.version</name>
<value>3</value>
</property>
标签是HFile v3的一项功能。 标签是做为单元的一部分的元数据,与密钥,值和版本分开。 标签是实现细节,为其余安全相关功能(如单元级ACL和可见性标签)提供基础。 标签存储在HFiles自身中。 未来可能会使用标签来实现其余HBase功能。 您无需了解不少关于标签的信息,以便使用它们启用的安全功能。
实施细节
每一个单元能够有零个或多个标签。 每一个标签都有一个类型和实际的标签字节数组。
就像行键,列族,限定符和值能够被编码同样(参见data.block.encoding.types),标签也能够被编码。 您能够在列族级别启用或禁用标签编码,而且默认状况下启用。 使用HColumnDescriptor#setCompressionTags(boolean compressTags)方法来管理列族的编码设置。 您还须要为列族启用DataBlockEncoder,以使标记的编码生效。
若是启用WAL压缩,则能够经过设置WAL的值来启用WAL中每一个标记的压缩
在hbase-site.xml中将hbase.regionserver.wal.tags.enablecompression设置为true。 标记压缩使用字典编码。
使用WAL加密时,不支持标记压缩。
HBase中的ACL基于用户对组的访问权限,以及给定组访问给定资源的权限。 ACL被实现为称为AccessController的协处理器。
HBase不维护私有组映射,但依赖于Hadoop组映射器,它映射LDAP或Active Directory等目录中的实体和HBase用户。任何支持的Hadoop组映射器均可以工做。而后,针对资源(全局,名称空间,表格,单元或端点)授予用户特定的权限(读取,写入,执行,建立,管理)。
启用Kerberos和访问控制后,客户端对HBase的访问将获得验证,而且用户数据是专用的,除非明确授予访问权限。
与关系数据库相比,HBase具备更简单的安全模型,特别是在客户端操做方面。例如,插入(新记录)和更新(现有记录)之间没有区别,由于二者都折叠成投入。
了解访问级别
HBase访问级别是相互独立授予的,并容许在给定范围内进行不一样类型的操做。
读取(R) - 能够读取给定范围的数据
写入(W) - 能够在给定范围写入数据
执行(X) - 能够在给定范围内执行协处理器端点
建立(C) - 能够在给定范围内建立表或删除表(甚至不建立它们)
管理员(A) - 能够执行群集操做,例如平衡群集或在给定范围内分配区域可能的范围包括:
超级用户 - 超级用户能够执行HBase中可用的任何操做,以访问任何资源。在群集上运行HBase的用户是超级用户,分配给HMaster上的hbase-site.xml中的配置属性hbase.superuser的任何主体都是如此。
全局 - 在全局范围授予的权限容许管理员对集群的全部表进行操做。
名称空间 - 在命名空间范围授予的权限适用于给定名称空间内的全部表。
表 - 在表范围授予的权限适用于给定表中的数据或元数据。
ColumnFamily - 在ColumnFamily范围内授予的权限适用于该ColumnFamily内的单元格。
单元格 - 在单元格范围内授予的权限适用于该确切的单元格坐标(键,值,时间戳)。这容许政策与数据一块儿发展。
要更改特定单元格上的ACL,请使用新ACL写入更新后的单元格,以得到原始坐标的精确坐标。
若是您有多版本模式而且想要更新全部可见版本的ACL,则须要为全部可见版本编写新的单元格。应用程序能够彻底控制策略演变。
上述规则的例外是附加和增量处理。追加和增量能够在操做中携带ACL。若是包含在操做中,那么它将应用于追加或增量的结果。不然,保存您正在追加或增长的现有单元的ACL。
访问级别和范围的组合建立了可授予用户的可能访问级别的矩阵。在生产环境中,根据执行特定工做所需的内容来考虑访问级别是有用的。如下列表描述了一些常见类型的HBase用户的适当访问级别。重要的是不要授予给定用户执行其所需任务所需的更多访问权限。
超级用户 - 在生产系统中,只有HBase用户应具备超级用户访问权限。在开发环境中,管理员可能须要超级用户访问才能快速控制和管理群集。可是,这种类型的管理员一般应该是全局管理员而不是超级用户。
全局管理员 - 全局管理员能够执行任务并访问HBase中的每一个表。在典型的生产环境中,管理员不该具备对表内数据的读取或写入权限。
具备管理员权限的全局管理员能够在群集上执行群集范围的操做,例如平衡,分配或取消分配区域或调用明确的主要压缩。这是一个操做角色。
具备建立权限的全局管理员能够建立或删除HBase中的任何表。这更像是一个DBA类型的角色。
在当前实施中,具备管理员权限的全局管理员能够授予他本身对表的读写权限并得到对该表数据的访问权限。所以,只需将全局管理员权限授予实际须要它们的可信用户。
还要注意,具备建立权限的全局管理员能够在ACL表上执行Put操做,模拟授予或撤销并绕过Global Admin权限的受权检查。
因为这些问题,请谨慎授予全局管理员权限。
命名空间管理员 - 具备建立权限的命名空间管理员能够在该命名空间内建立或删除表,并获取和恢复快照。具备管理员权限的名称空间管理员能够对该名称空间内的表执行操做,例如拆分或主要压缩。
表管理员 - 表管理员只能在该表上执行管理操做。具备建立权限的表管理员能够从该表建立快照或从快照中恢复该表。具备管理员权限的表管理员能够在该表上执行操做,例如拆分或主要压缩。
用户 - 用户能够读取或写入数据,或二者兼有。若是给定可执行文件,用户也能够执行协处理器端点
权限。
Table 8. Real-World Example of Access Levels
Job Title |
Scope |
Permissions |
Description |
Senior Administrator |
Global |
Access, Create |
Manages the cluster and gives access to Junior Administrators. |
Junior Administrator |
Global |
Create |
Creates tables and gives access to Table Administrators. |
Table Administrator |
Table |
Access |
Maintains a table from an operations point of view. |
Data Analyst |
Table |
Read |
Creates reports from HBase data. |
Web Application |
Table |
Read, Write |
Puts data into HBase and uses HBase data to perform operations. |
ACL矩阵
有关ACL如何映射到特定HBase操做和任务的更多详细信息,请参阅附录acl矩阵。
实施细节
单元级ACL使用标签实现(请参阅标签)。为了使用单元级别的ACL,您必须使用HFile v3和HBase 0.98或更高版本。
1.由HBase建立的文件由运行HBase进程的操做系统用户拥有。要与HBase文件交互,您应该使用API或批量加载功能。
2. HBase不在HBase内部为“角色”建模。相反,组名能够被授予权限。这容许经过组成员身份对角色进行外部建模。组经过Hadoop组映射服务在HBase外部建立和操做。
服务器端配置
1.做为先决条件,执行过程:基本服务器端配置中的步骤。
2.经过在hbase-site.xml中设置如下属性来安装和配置AccessController协处理器。这些属性包含一个类的列表。
若是您使用AccessController以及VisibilityController,则AccessController必须首先出如今列表中,由于在这两个组件都处于活动状态时,VisibilityController将委派访问
安全配置示例。
<property>
<name>hbase.coprocessor.region.classes</name>
<value>org.apache.hadoop.hbase.security.access.AccessController, org.apache.hadoop.hbase.security.token.TokenProvider</value>
</property>
<property>
<name>hbase.coprocessor.master.classes</name>
<value>org.apache.hadoop.hbase.security.access.AccessController</value>
</property>
<property>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.security.access.AccessController</value>
</property>
<property>
<name>hbase.security.exec.permission.checks</name>
<value>true</value>
</property>
或者,您能够经过将hbase.rpc.protection设置为隐私来启用传输安全性。 这须要HBase
0.98.4或更新版本
可见性标签控件可用于仅容许与给定标签关联的用户或主体读取或访问具备该标签的单元格。例如,您可能会标记单元格绝密,而且只能将该标签的访问权授予管理员组。可见性标签是使用标签实现的,标签是HFile v3的一个功能,并容许您以每一个单元为基础存储元数据。标签是一个字符串,可使用逻辑运算符(&,|或!)将标签组合为表达式,并使用圆括号进行分组。 HBase不能对表达式进行任何形式的验证,除了基本的良构。可见性标签自己没有意义,能够用来表示敏感级别,特权级别或任何其余任意的语义含义。
在HBase 0.98.6及更高版本中,可见性标签和表达式支持UTF-8编码。当使用org.apache.hadoop.hbase.security.visibility.VisibilityClient类提供的addLabels(conf,labels)方法建立标签并经过扫描或获取在受权中传递标签时,标签能够包含UTF-8字符以及一般在可见性标签中使用的逻辑运算符,以及普通的Java符号,而不须要任何转义方法。可是,当您经过Mutation传递CellVisibility表达式时,若是使用UTF-8字符或逻辑运算符,则必须用CellVisibility.quote()方法将表达式括起来。请参阅TestExpressionParser和源文件hbase-client / src / test / java / org / apache / hadoop / hbase / client / TestScan.java。
用户在Put操做期间向单元添加可见性表达式。在默认配置中,用户不须要访问标签就能够用它来标记单元格。此行为由配置选项hbase.security.visibility.mutations.checkauths控制。若是将此选项设置为true,则用户正在修改的标签做为突变的一部分必须与用户关联,不然突变将失败。在获取或扫描期间肯定用户是否有权读取标记的单元格,而且不容许用户读取的结果被过滤掉。这会致使相同的I / O处罚,如同返回结果同样,但会减小网络负载。
可见性标签也能够在删除操做中指定。有关可见性标签和删除的详细信息,请参阅HBASE-10885(https://issues.apache.org/jira/browse/HBASE-10885)。
当RegionServer首次收到请求时,用户的有效标签集创建在RPC上下文中。用户与标签关联的方式是可插入的。默认插件经过添加到获取或扫描的受权中指定的标签,并检查针对主叫用户的已认证标签列表的标签。当客户端传递用户未经过认证的标签时,默认插件会丢弃它们。您能够经过Get#setAuthorizations(Authorizations(String,...))和Scan#setAuthorizations(Authorizations(String,...))传递一部分用户认证标签;方法。
能够按照与用户相同的方式向组授予可见性标签。组前缀为@符号。当检查用户的可见性标签时,服务器将包括用户所属的组的可见性标签以及用户本身的标签。当使用API VisibilityClient#getAuths或Shell命令get_auths为用户检索可见性标签时,咱们将返回专门为该用户添加的标签,而不是组级标签。
可见性标签访问检查由VisibilityController协处理器执行。您可使用界面VisibilityLabelService来提供自定义实现和/或控制可见性标签与单元格一块儿存储的方式。例如,查看源文件hbase-server / src / test / java / org / apache / hadoop / hbase / security / visibility / TestVisibilityLabelsWithCustomVisLabService.java。
可见性标签能够与ACL一块儿使用。
标签必须在可见标签中使用以前明肯定义。请参阅下面的示例来了解如何完成此操做。
目前没有办法肯定哪些标签已应用于单元格。参见HBASE-12470
(https://issues.apache.org/jira/browse/HBASE-12470)以获取详细信息。
可见性标签目前不适用于超级用户。
Table 9. Examples of Visibility Expressions
Expression |
Interpretation |
fulltime |
Allow access to users associated with the fulltime label. |
!public |
Allow access to users not associated with the public label. |
( secret | topsecret ) & !probationary |
Allow access to users associated with either the secret or topsecret label and not associated with the probationary label. |
1.做为先决条件,执行过程:基本服务器端配置中的步骤。
2.经过在hbase-site.xml中设置如下属性来安装和配置VisibilityController协处理器。 这些属性包含类名的列表。
<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>
若是一块儿使用AccessController和VisibilityController协处理器,AccessController
必须首先出如今列表中,由于在这两个组件都处于活动状态时,VisibilityController会将其系统表上的访问控制委托给AccessController。
3.调整配置
默认状况下,用户可使用任何标签来标记单元格,包括它们没有关联的标签,这意味着用户能够放入他没法阅读的数据。 例如,即便用户没有与该标签相关联,用户也能够用(假设的)'topsecret'标签来标记单元格。 若是您只但愿用户可以标记与其关联的标签的单元格,请将hbase.security.visibility.mutations.checkauths设置为true。 在这种状况下,若是使用用户未关联的标签,则突变将失败。
分发您的配置并从新启动群集以使更改生效。
HBase提供了一种保护静态数据的机制,位于HDFS或其余分布式文件系统中的HFiles和WAL。 双层体系结构用于灵活和非侵入式密钥旋转。 “透明”意味着客户端不须要实施变动。 数据写入时,它被加密。 当它被读取时,它被按需解密。
它是怎么运行的
管理员为集群提供主密钥,该主密钥存储在管理工做站上的每一个受信任的HBase进程均可访问的密钥提供程序中,包括HMaster,RegionServers和客户端(如HBase Shell)。默认密钥提供程序与Java KeyStore API和任何支持它的密钥管理系统集成在一块儿。其余自定义密钥提供者实现是可能的密钥检索机制在hbase-site.xml中配置
外部密钥服务器或硬件安全模块中。此主密钥由HBase进程经过配置的密钥提供程序根据须要进行解析。
接下来,经过建立或修改列描述符以包含两个附加属性,能够在模式中的每列系列中指定加密用途:要使用的加密算法的名称(当前仅支持“AES”),以及可选的数据密钥用集群主密钥打包(加密)。若是没有为ColumnFamily显式配置数据密钥,则HBase将为每一个HFile建立一个随机数据密钥。与替代方案相比,这提供了安全性的渐进式改进。除非须要提供明确的数据密钥,例如在使用给定数据密钥生成用于批量导入的加密HFile的状况下,只需在ColumnFamily模式元数据中指定加密算法,并让HBase按需建立数据密钥。每列系列按键便于低影响增量式按键旋转,并减小关键材料的任何外部泄漏的范围。包装后的数据密钥存储在ColumnFamily模式元数据中,并存储在列族的每一个HFile中,并使用群集主密钥进行加密。 Column Family配置为加密后,任何新的HFile都将被加密写入。为了确保全部HFile的加密,在启用此功能后触发主要压缩。
当HFile打开时,数据密钥从HFile中提取,用集群主密钥解密,并用于解密HFile的其他部分。若是主密钥不可用,HFile将不可读。若是远程用户以某种方式获取对HDile数据的访问权限,或者因为某些HDFS权限失效或丢弃了不适当的媒体,将没法解密数据密钥或文件数据。
也能够加密WAL。尽管WAL是暂时的,可是若是底层文件系统受到威胁,则有必要对WALEdits进行加密,以免对加密列族的HFile进行保护。当启用WAL加密时,不管相关的HFile是否加密,全部WAL都会被加密。
服务器端配置
此过程假定您正在使用默认的Java密钥库实现。 若是您正在使用自定义实现,请检查其文档并进行相应调整。
$ keytool -keystore /path/to/hbase/conf/hbase.jks \
-storetype jceks -storepass **** \
-genseckey -keyalg AES -keysize 128 \
-alias <alias>
上一个命令在HBase conf /目录中建立了一个名为hbase.jks的文件。 设置此文件的权限和全部权,以便只有HBase服务账户用户能够读取该文件,并将密钥安全地分发给全部HBase服务器。
配置HBase守护进程。
在区域服务器上的hbase-site.xml中设置如下属性,将HBase守护程序配置为使用由KeyStore文件支持的密钥提供程序或检索集群主密钥。 在下面的示例中,将****替换为密码。
<property>
<name>hbase.crypto.keyprovider</name>
<value>org.apache.hadoop.hbase.io.crypto.KeyStoreKeyProvider</value>
</property>
<property>
<name>hbase.crypto.keyprovider.parameters</name>
<value>jceks:///path/to/hbase/conf/hbase.jks?password=****</value>
</property>
默认状况下,将使用HBase服务账户名称来解析群集主密钥。 可是,您可使用任意别名(在keytool命令中)存储它。 在这种状况下,请将如下属性设置为您使用的别名。
<property>
<name>hbase.crypto.master.key.name</name>
<value>my-alias</value>
</property>
配置HBase 1.0之后。 对于之前的版本,请在hbase-site.xml文件中设置如下属性
<property>
<name>hfile.format.version</name>
<value>3</value>
</property>
或者,您可使用不一样的密码提供者,能够是Java密码加密(JCE)算法提供者,也能够是自定义HBase密码实现。
JCE:
安装签名的JCE提供程序(支持128位密钥的AES / CTR / NoPadding模式)
将其最高优先级添加到JCE站点配置文件$ JAVA_HOME / lib / security / java.security。
在hbase-site.xml中更新hbase.crypto.algorithm.aes.provider和hbase.crypto.algorithm.rng.provider选项。
定制HBase密码:
实现org.apache.hadoop.hbase.io.crypto.CipherProvider。
将实现添加到服务器类路径。
更新hbase-site.xml中的hbase.crypto.cipherprovider。
4.配置WAL加密。
经过设置如下属性,在每一个RegionServer的hbase-site.xml中配置WAL加密。 您也能够将这些包含在HMaster的hbase-site.xml中,但HMaster没有WAL而且不会使用它们。
<property>
<name>hbase.regionserver.hlog.reader.impl</name>
<value>org.apache.hadoop.hbase.regionserver.wal.SecureProtobufLogReader</value>
</property>
<property>
<name>hbase.regionserver.hlog.writer.impl</name>
<value>org.apache.hadoop.hbase.regionserver.wal.SecureProtobufLogWriter</value>
</property>
<property>
<name>hbase.regionserver.wal.encryption</name>
<value>true</value>
</property>
5.在hbase-site.xml文件上配置权限。
因为密钥库密码存储在hbase-site.xml中,所以您须要确保只有HBase用户可使用文件全部权和权限来读取hbase-site.xml文件。
6.从新启动群集。
将新的配置文件分发到全部节点并从新启动群集。
行政
管理任务能够在HBase Shell或Java API中执行。
Java API
本节中的Java API示例取自源文件hbase-
服务器/ SRC /测试/ JAVA /组织/阿帕奇/ hadoop的/ HBase的/ UTIL / TestHBaseFsckEncryption.java。 。
这些例子以及它们的源文件都不属于公共HBase API的一部分,仅供参考。有关使用说明,请参阅官方API。
在列系列上启用加密
要在列系列上启用加密,您可使用HBase Shell或Java API。启用加密后,触发重大压缩。当主要压缩完成时,HFiles将被加密。
旋转数据键
要旋转数据键,首先更改列描述符中的ColumnFamily键,而后触发一个主要压缩。压缩完成后,全部HFile将使用新数据密钥从新加密。在压实完成以前,旧的HFile仍然可使用旧密钥进行读取。
在使用随机数据键和指定键之间切换
若是将某个列族配置为使用特定的键,而且您但愿返回为该列族使用随机生成的键的默认行为,请使用Java API来更改HColumnDescriptor,以便不会使用键ENCRYPTION_KEY发送值。
旋转主密钥
要旋转主密钥,请首先生成并分发新密钥。而后更新KeyStore以包含新的主密钥,并使用不一样的别名将旧的主密钥保存在KeyStore中。接下来,将备用配置配置到hbase-site.xml文件中的旧主密钥。
因为客户端必须将从MapReduce做业生成的文件的全部权转移给HBase,因此在安全模式下的批量加载比正常设置涉及更多。 安全批量加载由名为的协处理器实现
SecureBulkLoadEndpoint(https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/security/access/SecureBulkLoadEndpoint.html),它使用配置属性hbase.bulkload.staging.dir配置的登台目录,它 默认为
的/ tmp/ HBase的分段/。
安全批量加载算法
只有一次,建立一个世界可穿越的并由运行HBase(模式711或rwx-x-x)的用户所拥有的临时目录。 该目录的列表将以下所示:
$ ls -ld /tmp/hbase-staging
drwx--x--x 2 hbase hbase 68 3 Sep 14:54 /tmp/hbase-staging
用户将数据写入该用户拥有的安全输出目录。 例如,/ user / foo / data。
在内部,HBase建立一个全局可读写的秘密分段目录(-rwxrwxrwx,777)。 例如,/ tmp / hbase-staging / averylongandrandomdirectoryname。 该目录的名称和位置不会公开给用户。 HBase管理这个目录的建立和删除。
用户使数据具备世界可读性和世界可写性,将数据移入随机登台目录,而后调用
SecureBulkLoadClient#bulkLoadHFiles方法。
安全的优点在于秘密目录的长度和随机性。 要启用安全批量加载,请将如下属性添加到hbase-site.xml。